UNPKG

@microsoft/windows-admin-center-sdk

Version:

Microsoft - Windows Admin Center Shell

227 lines (225 loc) 8.63 kB
import { of, Subject } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { Net } from '../data/net'; import { LogLevel } from '../diagnostics/log-level'; import { Logging } from '../diagnostics/logging'; import { SmeWebTelemetry } from '../diagnostics/sme-web-telemetry'; import { TelemetryEventStates } from '../diagnostics/sme-web-telemetry-models'; import { EnvironmentModule } from '../manifest/environment-modules'; import { NotificationState } from './notification-state'; import { SocketMessageFlags, SocketSignalR } from './socket-signalr'; /** * The PowerShell script based notification class. */ export class PowerShellNotification extends SocketSignalR { static connectionUrl = '/api/notifications'; static proxyName = 'notificationHub'; static notify = 'Notify'; static subscribe = 'Subscribe'; subjectInternal; /* tslint:disable:no-bitwise */ static hasConnectionError(item) { return (item.type & SocketMessageFlags.ConnectionError) === SocketMessageFlags.ConnectionError; } static hasError(item) { return (item.type & SocketMessageFlags.Error) === SocketMessageFlags.Error; } static hasException(item) { return (item.type & SocketMessageFlags.Exception) === SocketMessageFlags.Exception; } static hasCompleted(item) { return (item.type & SocketMessageFlags.Completed) === SocketMessageFlags.Completed; } static hasData(item) { return (item.type & SocketMessageFlags.Data) === SocketMessageFlags.Data; } static hasProgress(item) { return (item.type & SocketMessageFlags.Progress) === SocketMessageFlags.Progress; } /** * Initializes a new instance of the GatewaySocket class. */ constructor(gatewayUrl) { super(gatewayUrl, PowerShellNotification.connectionUrl, PowerShellNotification.proxyName, !EnvironmentModule.isGatewayV200); } /** * Gets the subject observable to get notifications. */ get subject() { return this.subjectInternal.asObservable(); } /** * Submit PowerShell command as a work item. * * @param nodeConnection The node connection. * @param command The PowerShell command. * @param metadata The meta data. * @param nodeRequestOptions The node request options. * @param create The callback to create new notification. * @return Observable the observable of initial powershell query result. */ submit(nodeConnection, nodeName, command, metadata, nodeRequestOptions, create) { // For a non-powershell long running task (eg: azure site recovery setup): // The 'command' parameter is null. // The metadata.id is the workitem id generated by a gateway api. if (!command && metadata.id) { const result = { id: metadata.id, completed: false, error: null, state: NotificationState.Started }; if (create) { create(result); } this.subscribeSession(metadata.id); return of({ id: metadata.id, state: NotificationState.Started, error: null }); } const data = Net.createPropertiesJSONString({ ...command, useInProcRunspace: nodeRequestOptions.useInProcRunspace, ...{ metadata: metadata } }); return nodeConnection.post(nodeName, Net.powerShellApiInvokeCommand, data, nodeRequestOptions) .pipe(catchError((error) => { SmeWebTelemetry.tracePowershellEvent(command, TelemetryEventStates.Error, { response: error.response }); const result = { id: MsftSme.newGuid(), completed: true, error: { message: Net.getErrorMessage(error) }, state: NotificationState.Error }; if (create) { create(result); } return of({ id: result.id, state: result.state, error: result.error }); }), map((response) => { const completed = response.completed === 'True'; if (response.id) { // sending back the client for the error. return response; } let error = null; if (response.errors && response.errors.length > 0) { const errorObj = MsftSme.first(response.errors); error = { message: errorObj.message }; SmeWebTelemetry.tracePowershellEvent(command, TelemetryEventStates.Error, { response: response }); } const result = { id: response.sessionId, completed: completed, error: error, state: completed ? (response.errors ? NotificationState.Error : NotificationState.Success) : NotificationState.Started }; if (result.state === NotificationState.Success) { result.error = response.results[0]; } if (create) { create(result); } if (!result.completed) { this.subscribeSession(response.sessionId); // store ID here to correlate command with the gateway notification later const powershellCommandId = response.id ? response.id : response.sessionId; SmeWebTelemetry.addPowershellId(powershellCommandId, command); } this.processPowerShellData(response); // sending back the client. const workItemResult = { id: result.id, state: result.state, error: error }; // passing in the results as object of the workItemResult if (response.results) { workItemResult.object = response.results; } return workItemResult; })); } /** * Initialize to subscribe the web socket connection. */ initialize() { if (this.subjectInternal) { this.subjectInternal.complete(); } this.subjectInternal = new Subject(); this.subscribe(PowerShellNotification.notify); return this.start(); } /** * Uninitialize to subscribe the web socket connection. */ uninitialize() { if (this.subjectInternal) { this.subjectInternal.complete(); this.subjectInternal = null; } this.unsubscribe(PowerShellNotification.notify); this.stop(); } /** * Invoke subscribe method to a session. * * @param id the session ID of work item. * @return Promise the promise object. */ subscribeSession(id) { return this.invoke(PowerShellNotification.subscribe, id); } /** * The client handler to process message. * * @param notification the message notification. */ clientHandler(notification) { this.subjectInternal.next(notification); } /** * Process the message. * * @param messages the messages. */ processMessage(message) { if (!message) { const message2 = MsftSme.getStrings().MsftSmeShell.Core.Error.NotificationEmptyMessage.message; Logging.log({ source: 'socket', level: LogLevel.Warning, message: message2 }); return; } const data = JSON.parse(message); if (data.sessionId) { // PowerShell work item. this.processPowerShellData(data); return; } if (data.id) { // Notification message. this.processMessageData(data); return; } } processPowerShellData(data) { /* tslint:disable:no-bitwise */ let type = SocketMessageFlags.None; if (data.results && data.results.length > 0) { type |= SocketMessageFlags.Data; } if (data.progress && data.progress.length > 0) { type |= SocketMessageFlags.Progress; } if (data.errors && data.errors.length > 0) { type |= SocketMessageFlags.Error; } if (data.exception) { type |= SocketMessageFlags.Exception; } if (data.completed === 'True') { type |= SocketMessageFlags.Completed; } this.clientHandler({ type: type, message: data }); } processMessageData(message) { const type = SocketMessageFlags.Data; this.clientHandler({ type, message }); } } //# sourceMappingURL=powershell-notification.js.map