@microsoft/windows-admin-center-sdk
Version:
Microsoft - Windows Admin Center Shell
241 lines (239 loc) • 10.8 kB
JavaScript
import { forkJoin, of } from 'rxjs';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { ErrorExtended } from '../data/error-extended';
import { NativeQ } from '../data/native-q';
import { LogLevel } from '../diagnostics/log-level';
import { Logging } from '../diagnostics/logging';
import { HostCoreManager, HostCoreTokenMode } from '../host/host-core-manager';
import { EnvironmentModule } from './environment-modules';
/**
* The Manifest service class.
* (Localized string cannot be used in this class due to initialization phase when the strings are not ready yet.)
*/
export class ManifestLoader {
static logSourceName = 'ManifestLoader';
static gatewayUrl = 'gatewayUrl';
static manifestFile = 'manifest.json';
static deferred = NativeQ.defer();
static internalLoaded = ManifestLoader.deferred.promise;
/**
* Manifest loading promise.
*/
get loaded() {
return ManifestLoader.internalLoaded;
}
/**
* Load the manifest.
*/
static loadManifest() {
const self = MsftSme.self();
const mode = self.Init ? self.Init.mode : 0 /* MsftSme.EnvironmentMode.NotUse */;
const hostCoreManager = new HostCoreManager();
switch (mode) {
case 0 /* MsftSme.EnvironmentMode.NotUse */:
// Turn OFF iframe feature.
ManifestLoader.deferred.reject('no iFrame');
break;
case 1 /* MsftSme.EnvironmentMode.LoadEmbedded */:
// JSON file posting by module iframe.
const manifest = {
name: self.Init.moduleName,
version: self.Init.moduleVersion,
signature: EnvironmentModule.defaultSignature,
shell: {
name: EnvironmentModule.nameOfShell,
origin: self.Init.shellOrigin
}
};
// Turn ON module self-loading.
ManifestLoader.load(hostCoreManager, manifest);
break;
case 2 /* MsftSme.EnvironmentMode.Load */:
// Turn ON iframe feature by Shell and Module.
ManifestLoader.load(hostCoreManager);
break;
}
return ManifestLoader.internalLoaded;
}
/**
* Load the manifest into the MsftSme.Environment.
*
* @param hostCoreManager the hostCoreManager object to load the manifest.
* @param manifest the self loading manifest.
* @return Promise<any> the promise object.
*/
static load(hostCoreManager, manifest) {
if (manifest) {
ManifestLoader.update(manifest, null);
return ManifestLoader.internalLoaded;
}
const gatewayUrlParam = MsftSme.getLocationSearchParameter(ManifestLoader.gatewayUrl);
let manifestUrl = ManifestLoader.manifestFile;
if (gatewayUrlParam && MsftSme.self().Init.isProduction && Object.keys(MsftSme.sideLoad()).length === 0) {
// use the gateway endpoint for manifest source.
manifestUrl = `${gatewayUrlParam.value}/${ManifestLoader.manifestFile}`;
}
hostCoreManager.httpGet(manifestUrl)
.pipe(mergeMap(result => ManifestLoader.mergeGatewayManifest(hostCoreManager, result.response)), mergeMap(shellManifest => {
const response = shellManifest;
if (response.modules) {
return ManifestLoader.fetchSideloadManifests(hostCoreManager)
.pipe(map(sideLoads => {
const sideloadShellOrigins = [];
sideLoads = sideLoads.filter(s => {
if (MsftSme.isNullOrUndefined(s)) {
return false;
}
const isShell = s.name === EnvironmentModule.nameOfShell;
if (isShell) {
sideloadShellOrigins.push(s.origin);
}
return !isShell;
});
if (sideloadShellOrigins.length > 0) {
Logging.log({
source: ManifestLoader.logSourceName,
level: LogLevel.Warning,
consoleGroupHeader: `Unable to sideload: "${sideloadShellOrigins}".`,
message: `Cannot sideload ${EnvironmentModule.nameOfShell} from ${sideloadShellOrigins}.
Did you mean to run ${EnvironmentModule.nameOfShell} locally?`
});
}
return { manifest: response, sideLoads: sideLoads };
}));
}
return of({ manifest: response, sideLoads: [] });
}))
.subscribe({
next: result => {
const manifestResult = result.manifest;
const modules = manifestResult.modules;
const selfInit = MsftSme.self().Init;
selfInit.shellVersion = manifestResult.version;
selfInit.gatewayApiVersion = manifestResult.gatewayApiVersion;
selfInit.gatewayPlatform = manifestResult.gatewayPlatform;
result.sideLoads.forEach(sideLoad => {
if (sideLoad) {
sideLoad.isSideLoaded = true;
const foundIndex = modules.findIndex((item) => item.name === sideLoad.name);
if (foundIndex >= 0) {
modules.splice(foundIndex, 1, sideLoad);
}
else {
modules.push(sideLoad);
}
}
});
ManifestLoader.update(result.manifest, hostCoreManager.token);
},
error: error => {
// communicate main.ts for error message.
const extendedError = error;
if (hostCoreManager.tokenMode === HostCoreTokenMode.Aad) {
extendedError.extendedSource = ErrorExtended.sources.aadSso;
}
ManifestLoader.deferred.reject(error);
}
});
return ManifestLoader.internalLoaded;
}
/**
* Merges the GatewayUrl manifest with the passed in shell manifest
* Merges modules only at this time with a preference for modules included in the shell
*/
static mergeGatewayManifest(hostCoreManager, shellManifest) {
// it's production mode and no side-loading omits calling manifest.json again.
if (MsftSme.self().Init.isProduction && Object.keys(MsftSme.sideLoad()).length === 0) {
return of(shellManifest);
}
const gatewayUrlParam = MsftSme.getLocationSearchParameter(ManifestLoader.gatewayUrl);
if (!gatewayUrlParam) {
return of(shellManifest);
}
return hostCoreManager.getNoCache(`${gatewayUrlParam.value}/${ManifestLoader.manifestFile}`, false, 'json')
.pipe(map(result => {
const gatewayManifest = result.response;
if (Array.isArray(gatewayManifest.modules)) {
if (!Array.isArray(shellManifest.modules)) {
shellManifest.modules = gatewayManifest.modules;
}
else {
gatewayManifest.modules.forEach(mod => {
mod.origin = gatewayUrlParam.value;
const foundIndex = shellManifest.modules.findIndex((item) => item.name === mod.name);
if (foundIndex === -1) {
shellManifest.modules.push(mod);
}
});
}
}
return shellManifest;
}), catchError(() => {
// no localization
Logging.log({
source: ManifestLoader.logSourceName,
level: LogLevel.Warning,
consoleGroupHeader: `Unable to load gateway manifest: "${gatewayUrlParam.value}".`,
message: `Cannot load ${gatewayUrlParam.value}. Please make sure that a manifest.json is available at this location.`
});
return of(shellManifest);
}));
}
/**
* retrieves all of the side loaded manifests.
*
* @return Observable<any[]> the manifests.
*/
static fetchSideloadManifests(hostCoreManager) {
const sideLoadList = MsftSme.sideLoad();
if (Object.keys(sideLoadList).length === 0) {
return of([]);
}
const sideLoadManifestAwaiters = [];
MsftSme.forEachKey(sideLoadList, (key, sideLoad) => {
sideLoadManifestAwaiters.push(
// don't send credentials when debugging a side-loading module.
hostCoreManager.getNoCache(`${sideLoad.origin}/${ManifestLoader.manifestFile}`, false, 'json', false)
.pipe(map(result => {
const manifest = result.response;
manifest.origin = sideLoad.origin;
return manifest;
}), catchError(() => {
// no localization
Logging.log({
source: ManifestLoader.logSourceName,
level: LogLevel.Warning,
consoleGroupHeader: `Unable to sideload: "${sideLoad.origin}".`,
message: `Cannot sideload ${sideLoad.origin}. Please make sure that an extension is running at this origin.`
});
return of(null);
})));
});
return forkJoin(sideLoadManifestAwaiters);
}
/**
* Update the environment by the manifest.
*/
static update(manifest, token) {
try {
const self = MsftSme.self();
if (token) {
// store the last token if exists.
if (!manifest.configuration) {
manifest.configuration = {};
}
// passed the signOn objec to GatewayConnection object to configure into the SignOnManager.
manifest.configuration.signOn = { signedHttpRequestToken: token };
}
self.Environment = EnvironmentModule.createEnvironment(manifest, self.Resources.localeId);
ManifestLoader.deferred.resolve();
}
catch (e) {
// no localization
const message = 'Unable to load the manifest: {0}'.format(e);
ManifestLoader.deferred.reject(message);
throw new Error(message);
}
}
}
//# sourceMappingURL=manifest-loader.js.map