@microsoft/windows-admin-center-sdk
Version:
Microsoft - Windows Admin Center Shell
1 lines • 20.6 kB
Source Map (JSON)
{"version":3,"sources":["../../../packages/core/notification/work-item-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,UAAU,EAAgC,MAAM,MAAM,CAAC;AAEhF,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAE/D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAQzD,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAEnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAMnE,OAAO,EAAuB,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1E;;GAEG;AACH,qBAAa,eAAe;IA0BpB,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,iBAAiB;IACzB,OAAO,CAAC,cAAc;IACf,sBAAsB,EAAE,sBAAsB;IA5BzD,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAiC;IACnE,OAAO,CAAC,MAAM,CAAC,4BAA4B,CAAiC;IAC5E,OAAO,CAAC,MAAM,CAAC,oCAAoC,CAA6B;IAEzE,MAAM,UAAS;IAEtB,OAAO,CAAC,iBAAiB,CAAe;IACxC,OAAO,CAAC,sBAAsB,CAAyB;IACvD,OAAO,CAAC,wBAAwB,CAAe;IAC/C,OAAO,CAAC,uBAAuB,CAAe;IAC9C,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,eAAe,CAA6E;IACpG,OAAO,CAAC,wBAAwB,CAA2B;IAC3D,OAAO,CAAC,kBAAkB,CAAS;IAEnC;;;;;;;OAOG;gBAES,GAAG,EAAE,GAAG,EACR,iBAAiB,EAAE,iBAAiB,EACpC,cAAc,EAAE,cAAc,EAC/B,sBAAsB,EAAE,sBAAsB;IAKzD;;OAEG;IACI,KAAK,IAAI,IAAI;IAiKpB;;OAEG;IACI,IAAI,IAAI,IAAI;IAuBnB;;;;;OAKG;IACI,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAAC,cAAc,CAAC;IAqDvE;;;;;OAKG;IACI,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAAC,cAAc,CAAC;IAwBtE,OAAO,CAAC,aAAa;CAqCxB","file":"work-item-manager.d.ts","sourcesContent":["import { forkJoin, from, Observable, of, Subscription, throwError } from 'rxjs';\r\nimport { catchError, switchMap } from 'rxjs/operators';\r\nimport { GatewayConnection } from '../data/gateway-connection';\r\nimport { Net } from '../data/net';\r\nimport { NodeConnection } from '../data/node-connection';\r\nimport { PowerShell, PowerShellCommand } from '../data/powershell';\r\nimport { LogLevel } from '../diagnostics/log-level';\r\nimport { Logging } from '../diagnostics/logging';\r\nimport { SmeWebTelemetry } from '../diagnostics/sme-web-telemetry';\r\nimport { TelemetryEventStates } from '../diagnostics/sme-web-telemetry-models';\r\nimport { Strings } from '../generated/strings';\r\nimport { EnvironmentModule } from '../manifest/environment-modules';\r\nimport { Rpc } from '../rpc/rpc';\r\nimport { RpcWorkItem } from '../rpc/work-item/rpc-work-item-model';\r\nimport { RpcWorkItemSubjectServer } from '../rpc/work-item/rpc-work-item-subject-server';\r\nimport { NotificationConnection } from './notification-connection';\r\nimport { NotificationManager } from './notification-manager';\r\nimport { NotificationMessage } from './notification-message';\r\nimport { NotificationState } from './notification-state';\r\nimport { PowerShellNotification, PowerShellWorkItemMessage } from './powershell-notification';\r\nimport { SocketMessage } from './socket-signalr';\r\nimport { WorkItemRequestType, WorkItemResult } from './work-item-request';\r\n\r\n/**\r\n * Work item manager class.\r\n */\r\nexport class WorkItemManager {\r\n private static apiWorkItems24hours = '/workitems?lastMinutes=1440';\r\n private static apiNotificationMessageStored = '/NotificationMessage/stored';\r\n private static apiNotificationMessageSubscriptionId = '/NotificationMessage/id';\r\n\r\n public active = false;\r\n\r\n private startSubscription: Subscription;\r\n private powerShellNotification: PowerShellNotification;\r\n private notificationSubscription: Subscription;\r\n private rpcWorkItemSubscription: Subscription;\r\n private notificationManager: NotificationManager;\r\n private sequenceCounter = 1;\r\n private sequencePackets: { [index: number]: { success: boolean, data: WorkItemResult | any } } = {};\r\n private rpcWorkItemSubjectServer: RpcWorkItemSubjectServer;\r\n private rootSubscriptionId: string;\r\n\r\n /**\r\n * Initializes a new instance of the WorkItemManager class.\r\n *\r\n * @param rpc the RPC object.\r\n * @param gatewayConnection the gateway connection service.\r\n * @param nodeConnection the node connection service.\r\n * @param notificationManager the notification manager.\r\n */\r\n constructor(\r\n private rpc: Rpc,\r\n private gatewayConnection: GatewayConnection,\r\n private nodeConnection: NodeConnection,\r\n public notificationConnection: NotificationConnection) {\r\n this.notificationManager = this.notificationConnection.notificationManager;\r\n this.start();\r\n }\r\n\r\n /**\r\n * Start the work item management.\r\n */\r\n public start(): void {\r\n this.stop();\r\n this.rpcWorkItemSubjectServer = new RpcWorkItemSubjectServer(this.rpc);\r\n this.active = true;\r\n\r\n // pickup active work items for last 24hours\r\n this.startSubscription = this.gatewayConnection.get(WorkItemManager.apiWorkItems24hours)\r\n .pipe(\r\n catchError((error) => {\r\n const message = MsftSme.getStrings<Strings>().MsftSmeShell.Core.Error.NotificationNoWorkItemFound.message;\r\n Logging.logError('Notification', message.format(Net.getErrorMessage(error)));\r\n return of({ response: { value: null } });\r\n }),\r\n switchMap(response => {\r\n if (response.value) {\r\n response.value.forEach(element => this.notificationManager.addFromRecover(element));\r\n }\r\n\r\n // start websocket status query.\r\n this.powerShellNotification = new PowerShellNotification(this.gatewayConnection.gatewayUrl);\r\n return from(this.powerShellNotification.initialize());\r\n }))\r\n .subscribe({\r\n next: () => {\r\n // notification from the gateway...\r\n this.notificationSubscription = this.powerShellNotification.subject\r\n .subscribe(item => {\r\n const message: any = item.message;\r\n if (message && message.sessionId) {\r\n // PowerShell Work Item message.\r\n const workItem: PowerShellWorkItemMessage = message;\r\n const psSessionId = workItem.sessionId;\r\n\r\n // Pass in psSessionId so telemetry can search up the PSCommand for this\r\n if (PowerShellNotification.hasError(item)) {\r\n SmeWebTelemetry.tracePowershellEvent(\r\n null, TelemetryEventStates.Error, { response: workItem, id: psSessionId});\r\n } else if (PowerShellNotification.hasCompleted(item)) {\r\n SmeWebTelemetry.removePowershellId(psSessionId);\r\n }\r\n\r\n if (!this.notificationManager.updateFromMessage(\r\n psSessionId, <SocketMessage<PowerShellWorkItemMessage>>item)) {\r\n const unexpectedMessage =\r\n MsftSme.getStrings<Strings>().MsftSmeShell.Core.Error.NotificationUnexpectedReceived.message;\r\n Logging.log({\r\n source: 'Notification', level: LogLevel.Warning, message: unexpectedMessage.format(psSessionId)\r\n });\r\n }\r\n } else if (message && message.id) {\r\n // PowerShell Work Item message.\r\n const notification: NotificationMessage = message;\r\n const state = this.getErrorLevel(notification);\r\n\r\n // Pass in psSessionId so telemetry can search up the PSCommand for this\r\n if (state === NotificationState.Error) {\r\n SmeWebTelemetry.tracePowershellEvent(\r\n null, TelemetryEventStates.Error, { response: message, id: message.id});\r\n } else if (state === NotificationState.Success) {\r\n SmeWebTelemetry.removePowershellId(message.id);\r\n }\r\n\r\n if (this.notificationManager.addForNotificationMessage(state, notification)) {\r\n this.powerShellNotification.subscribeSession(notification.id);\r\n }\r\n\r\n if (!this.notificationManager.updateFromNotificationMessage(state, message)) {\r\n const unexpectedMessage =\r\n MsftSme.getStrings<Strings>().MsftSmeShell.Core.Error.NotificationUnexpectedReceived.message;\r\n Logging.logWarning('Notification', unexpectedMessage.format(message.id));\r\n }\r\n }\r\n });\r\n\r\n if (EnvironmentModule.isGatewayV200) {\r\n this.notificationSubscription.add(\r\n forkJoin([\r\n this.gatewayConnection.get(WorkItemManager.apiNotificationMessageSubscriptionId),\r\n this.gatewayConnection.get(WorkItemManager.apiNotificationMessageStored)\r\n ]).subscribe(([idData, storedData]) => {\r\n this.rootSubscriptionId = idData.id;\r\n this.powerShellNotification.subscribeSession(this.rootSubscriptionId);\r\n storedData.value.forEach(element => {\r\n const state = this.getErrorLevel(element);\r\n this.notificationManager.addForNotificationMessage(state, element);\r\n this.powerShellNotification.subscribeSession(element.id);\r\n });\r\n }));\r\n }\r\n\r\n // workItem request from rpc...\r\n this.rpcWorkItemSubscription = this.rpcWorkItemSubjectServer.subject\r\n .subscribe(item => {\r\n switch (item.data.type) {\r\n // legacy support for quick submission. it was expected 3 seconds response.\r\n case WorkItemRequestType.PowerShellSubmit:\r\n this.submitWorkItem(item.data).toPromise().then(item.deferred.resolve, item.deferred.reject);\r\n break;\r\n\r\n // new support for longer submission case like manage-as dialog displays.\r\n case WorkItemRequestType.WorkItemSubmit:\r\n const submitSequenceId = this.sequenceCounter++;\r\n item.data.sequenceId = submitSequenceId;\r\n this.submitWorkItem(item.data)\r\n .subscribe({\r\n next: data => this.sequencePackets[submitSequenceId] = { success: true, data: data },\r\n error: error => this.sequencePackets[submitSequenceId] = { success: false, data: error }\r\n });\r\n\r\n // using sequence id, submission return immediately.\r\n item.deferred.resolve(<WorkItemResult>{ sequenceId: submitSequenceId, id: null });\r\n break;\r\n\r\n // query to notification by 'id' or query to submission result by 'sequenceId'.\r\n case WorkItemRequestType.StateQuery:\r\n default:\r\n if (item.data.id) {\r\n // query to 'id'\r\n this.queryWorkItem(item.data).toPromise().then(item.deferred.resolve, item.deferred.reject);\r\n } else {\r\n // query to 'sequenceId'\r\n const querySequenceId = item.data.sequenceId;\r\n const result = this.sequencePackets[querySequenceId];\r\n if (result) {\r\n // have a result, delete it and pass back as response.\r\n delete this.sequencePackets[querySequenceId];\r\n if (result.success) {\r\n item.deferred.resolve(result.data);\r\n } else {\r\n item.deferred.reject(result.data);\r\n }\r\n } else {\r\n // no result yet.\r\n item.deferred.resolve(<WorkItemResult>{ sequenceId: querySequenceId, id: null });\r\n }\r\n }\r\n\r\n break;\r\n\r\n }\r\n });\r\n\r\n // subscribe the session notification to the gateway...\r\n this.notificationManager.items.forEach((value) => {\r\n // only if it's not finalized.\r\n if (value.state === NotificationState.Started || value.state === NotificationState.InProgress) {\r\n this.powerShellNotification.subscribeSession(value.id);\r\n }\r\n });\r\n },\r\n error: error => {\r\n const message = MsftSme.getStrings<Strings>().MsftSmeShell.Core.Error.NotificationWebsocketInitialize.message;\r\n Logging.log({\r\n source: 'Notification',\r\n level: LogLevel.Error,\r\n message: message.format(Net.getErrorMessage(error))\r\n });\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Stop the work item management.\r\n */\r\n public stop(): void {\r\n this.active = false;\r\n if (this.startSubscription) {\r\n this.startSubscription.unsubscribe();\r\n this.startSubscription = null;\r\n }\r\n\r\n if (this.notificationSubscription) {\r\n this.notificationSubscription.unsubscribe();\r\n this.notificationSubscription = null;\r\n }\r\n\r\n if (this.powerShellNotification) {\r\n this.powerShellNotification.uninitialize();\r\n this.powerShellNotification = null;\r\n }\r\n\r\n if (this.rpcWorkItemSubscription) {\r\n this.rpcWorkItemSubscription.unsubscribe();\r\n this.rpcWorkItemSubscription = null;\r\n }\r\n }\r\n\r\n /**\r\n * Create and submit a workItem.\r\n *\r\n * @param request the work item request.\r\n * @return Observable the WorkItemResult observable.\r\n */\r\n public submitWorkItem(request: RpcWorkItem): Observable<WorkItemResult> {\r\n let command: PowerShellCommand = null;\r\n const notificationId = request.id || MsftSme.newGuid();\r\n\r\n if (request.powerShellCommand || request.powerShellScript) {\r\n // For a non-powershell long running task (eg: azure site recovery setup), command is null.\r\n command = PowerShell.getPowerShellCommand(request.powerShellCommand || request.powerShellScript);\r\n }\r\n\r\n if (request.powerShellScript) {\r\n delete request['powerShellScript'];\r\n }\r\n\r\n if (request.powerShellCommand) {\r\n delete request['powerShellCommand'];\r\n }\r\n\r\n const nodeRequestOptions = PowerShell.newEndpointOptions(request.nodeRequestOptions);\r\n if (request.nodeRequestOptions) {\r\n delete request['nodeRequestOptions'];\r\n }\r\n\r\n // remember current URL where original request generated.\r\n request.locationPathname = window.location.pathname;\r\n request.locationSearch = window.location.search;\r\n\r\n // send submit notification\r\n this.notificationManager.addFromWorkItem(\r\n notificationId,\r\n request,\r\n request.startedMessage ? NotificationState.Started : NotificationState.InProgress\r\n );\r\n\r\n return this.powerShellNotification.submit(\r\n this.nodeConnection,\r\n request.nodeName,\r\n command,\r\n request,\r\n nodeRequestOptions,\r\n result => {\r\n if (!this.notificationManager.updateWorkItemWithPsSession(\r\n notificationId, result.id, request, result.state, result.error)) {\r\n const message = MsftSme.getStrings<Strings>()\r\n .MsftSmeShell.Core.Error.NotificationUnexpectedReceived.message;\r\n Logging.log({\r\n source: 'Notification',\r\n level: LogLevel.Warning,\r\n message: message.format(notificationId)\r\n });\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Query a workItem.\r\n *\r\n * @param request the work item request.\r\n * @return Observable the WorkItemResult observable.\r\n */\r\n public queryWorkItem(request: RpcWorkItem): Observable<WorkItemResult> {\r\n const notification = this.notificationManager.find(request.id);\r\n if (notification) {\r\n if (notification.state === NotificationState.Error) {\r\n return of(<WorkItemResult>{\r\n id: notification.id,\r\n state: notification.state,\r\n percent: notification.progressPercent,\r\n error: notification.object\r\n });\r\n }\r\n\r\n return of(<WorkItemResult>{\r\n id: notification.id,\r\n state: notification.state,\r\n percent: notification.progressPercent,\r\n object: notification.object\r\n });\r\n }\r\n\r\n const message = MsftSme.getStrings<Strings>().MsftSmeShell.Core.Error.NotificationNoIdFound.message;\r\n return throwError(() => message.format(request.id));\r\n }\r\n\r\n private getErrorLevel(notification: NotificationMessage): NotificationState {\r\n switch (notification.errorLevel) {\r\n // Notification is unknown.\r\n case 0:\r\n case 'None':\r\n return NotificationState.Informational;\r\n\r\n // Notification is in progress. (non-terminated message.)\r\n case 1:\r\n case 'InProgress':\r\n return NotificationState.InProgress;\r\n\r\n // Notification is success. (terminated message.)\r\n case 2:\r\n case 'Success':\r\n return NotificationState.Success;\r\n\r\n // Notification is informational. (terminated message.)\r\n case 3:\r\n case 'Informational':\r\n return NotificationState.Informational;\r\n\r\n // Notification is warning. (terminated message.)\r\n case 4:\r\n case 'Warning':\r\n return NotificationState.Warning;\r\n\r\n // Notification is error. (terminated message.)\r\n case 5:\r\n case 'Error':\r\n return NotificationState.Error;\r\n\r\n // Notification is unknown.\r\n default:\r\n throw new Error('Couldn\\'t get expected error level from a message notification.');\r\n }\r\n }\r\n}\r\n"]}