@microsoft/windows-admin-center-sdk
Version:
Microsoft - Windows Admin Center Shell
227 lines (225 loc) • 8.63 kB
JavaScript
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