@microsoft/windows-admin-center-sdk
Version:
Microsoft - Windows Admin Center Shell
256 lines (254 loc) • 9.46 kB
JavaScript
import { EnvironmentModule } from '../manifest/environment-modules';
import { PerformanceProfile } from '../performance/performance-profile';
import { RpcBase, RpcInboundCommands, RpcOutboundCommands, RpcType } from './rpc-base';
import { RpcChannel } from './rpc-channel';
import { RpcInbound } from './rpc-inbound';
import { RpcOutbound } from './rpc-outbound';
import { RpcSeekClient } from './seek/rpc-seek-client';
/**
* The status of RPC remote that sent the message
*/
export var RpcRemoteState;
(function (RpcRemoteState) {
RpcRemoteState[RpcRemoteState["Active"] = 0] = "Active";
RpcRemoteState[RpcRemoteState["Inactive"] = 1] = "Inactive";
})(RpcRemoteState || (RpcRemoteState = {}));
/**
* RpcManager class.
*/
export class RpcManager {
static serial = 0;
rpcChannel;
rpcInboundHandlers;
rpcOutboundHandlers;
currentRpcInbound;
currentRpcOutbound;
parentRpcInbound;
/**
* Initializes a new instance of the RpcManager class.
*/
constructor() {
const inboundHandlers = {};
const outboundHandlers = {};
const inboundCommands = Object.keys(RpcInboundCommands);
const outboundCommands = Object.keys(RpcOutboundCommands);
inboundCommands.forEach((command) => {
const handlerName = RpcBase.commandToHandlerName(command);
inboundHandlers[handlerName] = _ => Promise.resolve();
});
outboundCommands.forEach((command) => {
const handlerName = RpcBase.commandToHandlerName(command);
outboundHandlers[handlerName] = _ => Promise.resolve();
});
this.rpcInboundHandlers = inboundHandlers;
this.rpcOutboundHandlers = outboundHandlers;
}
/**
* Gets last rpc to-shell.
*/
get rpcInbound() {
return this.currentRpcInbound;
}
/**
* Gets last rpc to-module.
*/
get rpcOutbound() {
return this.currentRpcOutbound;
}
/**
* Gets rpc inbound for report data.
*/
get rpcReportDataInbound() {
return this.parentRpcInbound || this.currentRpcInbound;
}
/**
* Initialize the rpc communication channel based on manifest.
*
* @param inboundHandlers the set of rpc inbound handlers.
* @param outboundHandlers the set of rpc outbound handlers.
*/
init(inboundHandlers, outboundHandlers) {
// extend module and shell handlers
if (inboundHandlers) {
this.rpcInboundHandlers = Object.assign(this.rpcInboundHandlers, inboundHandlers);
}
if (outboundHandlers) {
this.rpcOutboundHandlers = Object.assign(this.rpcOutboundHandlers, outboundHandlers);
}
// read environment and create rpc self instance.
const global = window;
const { name, origin, signature } = global.MsftSme.Environment;
this.rpcChannel = new RpcChannel(name, origin, signature);
this.rpcChannel.subName = '##';
this.rpcChannel.rpcInboundHandlers = this.rpcInboundHandlers;
// for module configure shell access.
this.initRpcInbound();
// start rpc response.
this.rpcChannel.start();
}
/**
* Register inbound command handler.
*
* @param command The command name.
* @param handler The command handler.
*/
registerInboundHandler(command, handler) {
const handlerName = RpcBase.commandToHandlerName(command);
this.rpcInboundHandlers[handlerName] = handler;
}
/**
* Configure Rpc as parent frame.
*/
initRpcInbound() {
// accept initial ping query for any parent.
const rpcInbound = new RpcInbound(this.rpcChannel, '*', '*');
rpcInbound.subName = '*';
this.rpcChannel.registerRpc(rpcInbound, RpcType.Inbound);
this.currentRpcInbound = rpcInbound;
rpcInbound.registerAll(this.rpcOutboundHandlers);
}
/**
* Connect Rpc module.
*
* @param name the name of module.
* @param path the entry point to open for this module.
* @param iframe the iframe object.
* @param primary the primary iframe to support report data response.
* @return Promise<string> The promise with the sub name of outbound connection.
*/
connectRpcOutbound(name, path, iFrame, primary) {
// making all instance to be unique at any order of connection.
const subName = '{0}#{1}+{2}'.format(path, Date.now(), RpcManager.serial++);
const rpcOutbound = this.createRpcOutbound(name, subName, iFrame);
rpcOutbound.registerAll(this.rpcInboundHandlers);
// pinging to establish connection to the module.
const start = Date.now();
return rpcOutbound.ping({ name: 'ping' }).then(_ => {
// regressed, moved after ping was succeeded.
// Primary check removed to support new connection functionality...
if (primary) {
this.currentRpcOutbound = rpcOutbound;
}
PerformanceProfile.logRouteNavigation('RpcManager', start, Date.now(), '[rpcPing]', name);
return subName;
});
}
/**
* Reconnect Rpc module.
*
* @param name the name of module.
* @param subName the sub name.
* @param primary the primary iframe to support report data response.
* @return RpcOutbound the rpc outbound object.
*/
reconnectRpcOutbound(name, subName, primary) {
const rpcOutbound = this.rpcChannel.getRpc(name, subName, RpcType.Outbound);
if (primary) {
this.currentRpcOutbound = rpcOutbound;
}
return rpcOutbound;
}
/**
* Disconnect Rpc module.
*/
disconnectRpcOutbound() {
this.currentRpcOutbound = null;
}
/**
* Remove RpcOutbound.
*
* @param module the environment module to remove.
*/
removeRpcOutbound(name, subName) {
const rpcOutbound = this.rpcChannel.unregisterRpc(name, subName, RpcType.Outbound);
if (this.currentRpcOutbound === rpcOutbound) {
this.disconnectRpcOutbound();
}
return rpcOutbound;
}
/**
* Get current live outbound rpc.
* - these set could be changed if it's handled async.
*/
getCurrentRpcOutbound() {
return this.rpcChannel.getAllRpc(RpcType.Outbound);
}
/**
* Get the remote status of a given module name
*
* @param name The name of the RPC remote endpoint to get the status from
* @param subName The sub name of the remote iframe instance.
* @returns The state of the remote. Active if it's the current channel
* for communication or Inactive if the channel is not the currently active channel in this
* manager
*/
getSourceStatus(name, subName) {
if (!this.currentRpcOutbound
|| this.currentRpcOutbound.name !== name
|| this.currentRpcOutbound.subName !== subName) {
return {
status: RpcRemoteState.Inactive,
subName: subName,
entryPoint: null
};
}
const segments = subName.split('#');
return {
status: RpcRemoteState.Active,
subName: subName,
entryPoint: segments && segments.length > 0 ? segments[0] : ''
};
}
/**
* Seek shell or parent frame.
*
* @param Promise<any> the promise object.
*/
seekShell(mode) {
if (this.currentRpcInbound.name === EnvironmentModule.nameOfShell) {
return Promise.resolve({ name: this.currentRpcInbound.name, subName: this.currentRpcInbound.subName });
}
let depth = this.rpcChannel.depth;
let current = window.self;
while (--depth >= 0 && current !== current.parent) {
current = current.parent;
}
const rpcInbound = new RpcInbound(this.rpcChannel, EnvironmentModule.nameOfShell, '*');
rpcInbound.window = current;
rpcInbound.subName = '##';
rpcInbound.depth = 0;
this.rpcChannel.registerRpc(rpcInbound, RpcType.Inbound);
rpcInbound.registerAll(this.rpcOutboundHandlers);
this.currentRpcInbound = rpcInbound;
return RpcSeekClient.seek(this, { mode: mode }).then(result => {
this.parentRpcInbound = this.currentRpcInbound;
this.currentRpcInbound = rpcInbound;
return result;
}, error => {
this.rpcChannel.unregisterRpc(rpcInbound.name, rpcInbound.subName, RpcType.Inbound);
return error;
});
}
/**
* Create and add RpcOutbound object.
*
* @param name the name of module.
* @param subName the sub name.
* @param module the environment module to remove.
*/
createRpcOutbound(name, subName, iFrame) {
const global = window;
const { modules } = global.MsftSme.Environment;
const module = modules.find((value) => value.name === name);
if (!module) {
const message = MsftSme.getStrings().MsftSmeShell.Core.Error.RpcFailedFindModuleManifest.message;
throw new Error(message.format(name));
}
const rpc = new RpcOutbound(this.rpcChannel, module.name, module.origin);
rpc.subName = subName;
rpc.window = iFrame;
this.rpcChannel.registerRpc(rpc, RpcType.Outbound);
return rpc;
}
}
//# sourceMappingURL=rpc-manager.js.map