feat(core.gbapp): New bot management (CRUD) from SharePoint packages.

This commit is contained in:
Rodrigo Rodriguez 2019-08-22 01:54:30 +00:00
parent 4a3d47281e
commit 9a961e72a1
8 changed files with 181 additions and 106 deletions

7
package-lock.json generated
View file

@ -1,6 +1,6 @@
{ {
"name": "botserver", "name": "botserver",
"version": "1.5.5", "version": "1.6.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -6061,6 +6061,11 @@
"methods": "^1.0.0" "methods": "^1.0.0"
} }
}, },
"express-remove-route": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/express-remove-route/-/express-remove-route-1.0.0.tgz",
"integrity": "sha1-HnYRseCiPw1aPCLaK9Sy6rwjC1Q="
},
"extend": { "extend": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",

View file

@ -66,7 +66,7 @@
"botbuilder-choices": "4.0.0-preview1.2", "botbuilder-choices": "4.0.0-preview1.2",
"botbuilder-dialogs": "4.4.0", "botbuilder-dialogs": "4.4.0",
"botbuilder-prompts": "4.0.0-preview1.2", "botbuilder-prompts": "4.0.0-preview1.2",
"botlib": "^1.2.1", "botlib": "1.2.2",
"chai": "4.2.0", "chai": "4.2.0",
"child_process": "1.0.2", "child_process": "1.0.2",
"chokidar": "3.0.0", "chokidar": "3.0.0",
@ -76,6 +76,7 @@
"empty-dir": "^2.0.0", "empty-dir": "^2.0.0",
"express": "4.16.4", "express": "4.16.4",
"express-promise-router": "3.0.3", "express-promise-router": "3.0.3",
"express-remove-route": "^1.0.0",
"fs-extra": "8.0.0", "fs-extra": "8.0.0",
"ip": "1.1.5", "ip": "1.1.5",
"js-beautify": "1.10.0", "js-beautify": "1.10.0",

View file

@ -57,7 +57,7 @@ export class AdminDialog extends IGBDialog {
const packageName = text.split(' ')[1]; const packageName = text.split(' ')[1];
const importer = new GBImporter(min.core); const importer = new GBImporter(min.core);
const deployer = new GBDeployer(min.core, importer); const deployer = new GBDeployer(min.core, importer);
await deployer.undeployPackageFromLocalPath(min.instance, urlJoin('packages', packageName)); await deployer.undeployPackageFromLocalPath(min.instance, urlJoin(GBDeployer.workFolder, packageName));
} }
public static isSharePointPath(path: string) { public static isSharePointPath(path: string) {
@ -79,7 +79,7 @@ export class AdminDialog extends IGBDialog {
let siteName = text.split(' ')[1]; let siteName = text.split(' ')[1];
let folderName = text.split(' ')[2]; let folderName = text.split(' ')[2];
let localFolder = Path.join('tmp', Path.basename(folderName)); let localFolder = Path.join('work', Path.basename(folderName));
await s.downloadFolder(localFolder, siteName, folderName, await s.downloadFolder(localFolder, siteName, folderName,
GBConfigService.get('CLOUD_USERNAME'), GBConfigService.get('CLOUD_PASSWORD')) GBConfigService.get('CLOUD_USERNAME'), GBConfigService.get('CLOUD_PASSWORD'))
await deployer.deployPackage(min, localFolder); await deployer.deployPackage(min, localFolder);
@ -122,20 +122,20 @@ export class AdminDialog extends IGBDialog {
const prompt = Messages[locale].authenticate; const prompt = Messages[locale].authenticate;
return await step.prompt('textPrompt', prompt); return await step.prompt('textPrompt', prompt);
}, // },
async step => { // async step => {
const locale = step.context.activity.locale; // const locale = step.context.activity.locale;
const sensitive = step.result; // const sensitive = step.result;
if (sensitive === GBConfigService.get('ADMIN_PASS')) { // if (sensitive === GBConfigService.get('ADMIN_PASS')) {
await step.context.sendActivity(Messages[locale].welcome); // await step.context.sendActivity(Messages[locale].welcome);
return await step.prompt('textPrompt', Messages[locale].which_task); // return await step.prompt('textPrompt', Messages[locale].which_task);
} else { // } else {
await step.context.sendActivity(Messages[locale].wrong_password); // await step.context.sendActivity(Messages[locale].wrong_password);
return await step.endDialog(); // return await step.endDialog();
} // }
}, },
async step => { async step => {
const locale: string = step.context.activity.locale; const locale: string = step.context.activity.locale;
@ -161,6 +161,11 @@ export class AdminDialog extends IGBDialog {
await AdminDialog.rebuildIndexPackageCommand(min, deployer); await AdminDialog.rebuildIndexPackageCommand(min, deployer);
await step.context.sendActivity('Finished importing of that .gbkb package. Thanks.'); await step.context.sendActivity('Finished importing of that .gbkb package. Thanks.');
return await step.replaceDialog('/admin', { firstRun: false }); return await step.replaceDialog('/admin', { firstRun: false });
} else if (cmdName === 'undeployPackage') {
await step.context.sendActivity('The package is being *undeployed*...');
await AdminDialog.undeployPackageCommand(text, min);
await step.context.sendActivity('Package *undeployed*.');
return await step.replaceDialog('/admin', { firstRun: false });
} else if (cmdName === 'rebuildIndex') { } else if (cmdName === 'rebuildIndex') {
await AdminDialog.rebuildIndexPackageCommand(min, deployer); await AdminDialog.rebuildIndexPackageCommand(min, deployer);

View file

@ -218,7 +218,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
}; };
} }
public async botExists(botId, group, endpoint) { public async botExists(botId, group) {
const baseUrl = `https://management.azure.com/`; const baseUrl = `https://management.azure.com/`;
const username = GBConfigService.get('CLOUD_USERNAME'); const username = GBConfigService.get('CLOUD_USERNAME');
const password = GBConfigService.get('CLOUD_PASSWORD'); const password = GBConfigService.get('CLOUD_PASSWORD');
@ -227,17 +227,11 @@ export class AzureDeployerService implements IGBInstallationDeployer {
const accessToken = await GBAdminService.getADALTokenFromUsername(username, password); const accessToken = await GBAdminService.getADALTokenFromUsername(username, password);
const httpClient = new ServiceClient(); const httpClient = new ServiceClient();
const parameters = {
properties: {
endpoint: endpoint
}
};
const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${ const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${
this.provider this.provider
}/botServices/${botId}?api-version=${this.apiVersion}`; }/botServices/${botId}?api-version=${this.apiVersion}`;
const url = urlJoin(baseUrl, query); const url = urlJoin(baseUrl, query);
const req = AzureDeployerService.createRequestObject(url, accessToken, 'GET', JSON.stringify(parameters)); const req = AzureDeployerService.createRequestObject(url, accessToken, 'GET', undefined);
const res = await httpClient.sendRequest(req); const res = await httpClient.sendRequest(req);
// CHECK // CHECK
if (!JSON.parse(res.bodyAsText).id) { if (!JSON.parse(res.bodyAsText).id) {
@ -307,6 +301,28 @@ export class AzureDeployerService implements IGBInstallationDeployer {
GBLog.info(`Bot proxy updated at: ${endpoint}.`); GBLog.info(`Bot proxy updated at: ${endpoint}.`);
} }
public async deleteBot(botId: string, group) {
const baseUrl = `https://management.azure.com/`;
const username = GBConfigService.get('CLOUD_USERNAME');
const password = GBConfigService.get('CLOUD_PASSWORD');
const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
const accessToken = await GBAdminService.getADALTokenFromUsername(username, password);
const httpClient = new ServiceClient();
const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${
this.provider
}/botServices/${botId}?api-version=${this.apiVersion}`;
const url = urlJoin(baseUrl, query);
const req = AzureDeployerService.createRequestObject(url, accessToken, 'DELETE', undefined);
const res = await httpClient.sendRequest(req);
if (res.bodyAsText !== "") {
throw res.bodyAsText;
}
GBLog.info(`Bot ${botId} was deleted from the provider.`);
}
public async openStorageFirewall(groupName, serverName) { public async openStorageFirewall(groupName, serverName) {
const username = GBConfigService.get('CLOUD_USERNAME'); const username = GBConfigService.get('CLOUD_USERNAME');
const password = GBConfigService.get('CLOUD_PASSWORD'); const password = GBConfigService.get('CLOUD_PASSWORD');

View file

@ -279,6 +279,12 @@ STORAGE_SYNC=true
} }
} }
public async deleteInstance(botId:string) {
const options = { where: {} };
options.where = { botId: botId };
await GuaribasInstance.destroy(options);
}
public async saveInstance(fullInstance: any) { public async saveInstance(fullInstance: any) {
const options = { where: {} }; const options = { where: {} };
options.where = { botId: fullInstance.botId }; options.where = { botId: fullInstance.botId };

View file

@ -43,6 +43,7 @@ const WaitUntil = require('wait-until');
const express = require('express'); const express = require('express');
const child_process = require('child_process'); const child_process = require('child_process');
const graph = require('@microsoft/microsoft-graph-client'); const graph = require('@microsoft/microsoft-graph-client');
const emptyDir = require('empty-dir');
import { GBError, GBLog, GBMinInstance, IGBCoreService, IGBInstance, IGBPackage } from 'botlib'; import { GBError, GBLog, GBMinInstance, IGBCoreService, IGBInstance, IGBPackage } from 'botlib';
import { AzureSearch } from 'pragmatismo-io-framework'; import { AzureSearch } from 'pragmatismo-io-framework';
@ -54,6 +55,8 @@ import { KBService } from './../../kb.gbapp/services/KBService';
import { GBConfigService } from './GBConfigService'; import { GBConfigService } from './GBConfigService';
import { GBImporter } from './GBImporterService'; import { GBImporter } from './GBImporterService';
import { GBVMService } from './GBVMService'; import { GBVMService } from './GBVMService';
import { min } from 'moment';
import { GBMinService } from './GBMinService';
/** /**
* *
@ -62,9 +65,9 @@ import { GBVMService } from './GBVMService';
export class GBDeployer { export class GBDeployer {
public static deployFolder = 'packages'; public static deployFolder = 'packages';
public static workFolder = 'work';
public core: IGBCoreService; public core: IGBCoreService;
public importer: GBImporter; public importer: GBImporter;
public workDir: string = './work';
constructor(core: IGBCoreService, importer: GBImporter) { constructor(core: IGBCoreService, importer: GBImporter) {
this.core = core; this.core = core;
@ -93,7 +96,7 @@ export class GBDeployer {
(resolve: any, reject: any): any => { (resolve: any, reject: any): any => {
GBLog.info(`PWD ${process.env.PWD}...`); GBLog.info(`PWD ${process.env.PWD}...`);
let totalPackages = 0; let totalPackages = 0;
let paths = [urlJoin(process.env.PWD, GBDeployer.deployFolder)]; let paths = [urlJoin(process.env.PWD, GBDeployer.deployFolder), urlJoin(process.env.PWD, GBDeployer.workFolder)];
const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH'); const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH');
if (additionalPath !== undefined && additionalPath !== '') { if (additionalPath !== undefined && additionalPath !== '') {
paths = paths.concat(additionalPath.toLowerCase().split(';')); paths = paths.concat(additionalPath.toLowerCase().split(';'));
@ -165,7 +168,7 @@ export class GBDeployer {
* Deploys a bot to the storage. * Deploys a bot to the storage.
*/ */
public async deployBot(localPath: string, proxyAddress: string): Promise<void> { public async deployBot(localPath: string, publicAddress: string): Promise<void> {
const packageName = Path.basename(localPath); const packageName = Path.basename(localPath);
const service = new AzureDeployerService(this); const service = new AzureDeployerService(this);
@ -177,17 +180,17 @@ export class GBDeployer {
const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID'); const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
const accessToken = await GBAdminService.getADALTokenFromUsername(username, password); const accessToken = await GBAdminService.getADALTokenFromUsername(username, password);
if (await service.botExists(instance.botId, group, proxyAddress)) { if (await service.botExists(instance.botId, group)) {
instance = await service.updateBot( await service.updateBot(
instance, instance.botId,
accessToken, group,
instance.title, instance.title,
instance.description, instance.description,
proxyAddress `${publicAddress}/api/messages/${instance.botId}`
); );
} else { } else {
instance = Object.assign(instance, GBServer.globals.bootInstance);
instance = await service.internalDeployBot( instance = await service.internalDeployBot(
instance, instance,
accessToken, accessToken,
@ -195,7 +198,7 @@ export class GBDeployer {
instance.title, instance.title,
group, group,
instance.description, instance.description,
`${proxyAddress}/api/messages/${instance.botId}`, `${publicAddress}/api/messages/${instance.botId}`,
'global', 'global',
instance.nlpAppId, instance.nlpAppId,
instance.nlpKey, instance.nlpKey,
@ -203,10 +206,40 @@ export class GBDeployer {
instance.marketplacePassword, instance.marketplacePassword,
subscriptionId subscriptionId
); );
await GBServer.globals.minService.mountBot(instance);
} }
await this.core.saveInstance(instance); await this.core.saveInstance(instance);
} }
/**
* Deploys a bot to the storage.
*/
public async undeployBot(botId: string, packageName: string): Promise<void> {
const service = new AzureDeployerService(this);
const username = GBConfigService.get('CLOUD_USERNAME');
const password = GBConfigService.get('CLOUD_PASSWORD');
const group = GBConfigService.get('CLOUD_GROUP');
const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
const accessToken = await GBAdminService.getADALTokenFromUsername(username, password);
if (await service.botExists(botId, group)) {
await service.deleteBot(
botId, group
);
}
GBServer.globals.minService.unmountBot(botId);
await this.core.deleteInstance(botId);
const packageFolder = urlJoin(process.env.PWD, 'work', packageName);
await emptyDir(packageFolder);
}
public async deployPackageToStorage(instanceId: number, packageName: string): Promise<GuaribasPackage> { public async deployPackageToStorage(instanceId: number, packageName: string): Promise<GuaribasPackage> {
return GuaribasPackage.create({ return GuaribasPackage.create({
packageName: packageName, packageName: packageName,
@ -233,6 +266,7 @@ export class GBDeployer {
switch (packageType) { switch (packageType) {
case '.gbot': case '.gbot':
await this.deployBot(localPath, GBServer.globals.publicAddress); await this.deployBot(localPath, GBServer.globals.publicAddress);
break;
case '.gbkb': case '.gbkb':
const service = new KBService(this.core.sequelize); const service = new KBService(this.core.sequelize);
@ -257,7 +291,13 @@ export class GBDeployer {
const p = await this.getPackageByName(instance.instanceId, packageName); const p = await this.getPackageByName(instance.instanceId, packageName);
switch (packageType) { switch (packageType) {
case '.gbot':
const packageObject = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'package.json'), 'utf8'));
await this.undeployBot(packageObject.botId, packageName);
break;
case '.gbkb': case '.gbkb':
const service = new KBService(this.core.sequelize); const service = new KBService(this.core.sequelize);

View file

@ -40,6 +40,7 @@ import urlJoin = require('url-join');
const { DialogSet, TextPrompt } = require('botbuilder-dialogs'); const { DialogSet, TextPrompt } = require('botbuilder-dialogs');
const express = require('express'); const express = require('express');
const request = require('request-promise-native'); const request = require('request-promise-native');
const removeRoute = require('express-remove-route');
const AuthenticationContext = require('adal-node').AuthenticationContext; const AuthenticationContext = require('adal-node').AuthenticationContext;
import { AutoSaveStateMiddleware, BotFrameworkAdapter, ConversationState, MemoryStorage, UserState } from 'botbuilder'; import { AutoSaveStateMiddleware, BotFrameworkAdapter, ConversationState, MemoryStorage, UserState } from 'botbuilder';
import { ConfirmPrompt, WaterfallDialog } from 'botbuilder-dialogs'; import { ConfirmPrompt, WaterfallDialog } from 'botbuilder-dialogs';
@ -76,6 +77,7 @@ export class GBMinService {
public conversationalService: IGBConversationalService; public conversationalService: IGBConversationalService;
public adminService: IGBAdminService; public adminService: IGBAdminService;
public deployer: GBDeployer; public deployer: GBDeployer;
private static uiPackage = 'default.gbui';
public corePackage = 'core.gbai'; public corePackage = 'core.gbai';
@ -108,79 +110,73 @@ export class GBMinService {
*/ */
public async buildMin( public async buildMin(
bootInstance: IGBInstance,
server: any,
appPackages: IGBPackage[],
sysPackages: IGBPackage[],
instances: IGBInstance[], instances: IGBInstance[],
deployer: GBDeployer,
proxyAddress: string
) { ) {
const uiPackage = 'default.gbui';
// Serves default UI on root address '/' if web enabled. // Serves default UI on root address '/' if web enabled.
if (process.env.DISABLE_WEB !== 'true') { if (process.env.DISABLE_WEB !== 'true') {
server.use('/', express.static(urlJoin(GBDeployer.deployFolder, uiPackage, 'build'))); GBServer.globals.server.use('/', express.static(urlJoin(GBDeployer.deployFolder, GBMinService.uiPackage, 'build')));
}
// Serves the bot information object via HTTP so clients can get
// instance information stored on server.
if (process.env.DISABLE_WEB !== 'true') {
GBServer.globals.server.get('/instances/:botId', (req, res) => {
(async () => {
await this.handleGetInstanceFroClient(req, res);
})();
});
} }
await Promise.all( await Promise.all(
instances.map(async instance => { instances.map(async instance => {
// Gets the authorization key for each instance from Bot Service. // Gets the authorization key for each instance from Bot Service.
const webchatToken = await this.getWebchatToken(instance); await this.mountBot(instance);
})
// Serves the bot information object via HTTP so clients can get );
// instance information stored on server.
if (process.env.DISABLE_WEB !== 'true') {
server.get('/instances/:botId', (req, res) => {
(async () => {
await this.sendInstanceToClient(req, bootInstance, res, webchatToken);
})();
});
} }
// Build bot adapter. public async unmountBot(botId: string) {
const url = `/api/messages/${botId}`;
removeRoute(GBServer.globals.server,url);
const { min, adapter, conversationState } = await this.buildBotAdapter(instance, proxyAddress, sysPackages); const uiUrl = `/${botId}`;
removeRoute(GBServer.globals.server, uiUrl);
}
public async mountBot(instance: IGBInstance) {
// Build bot adapter.
const { min, adapter, conversationState } = await this.buildBotAdapter(instance, GBServer.globals.publicAddress, GBServer.globals.sysPackages);
// Install default VBA module. // Install default VBA module.
//this.deployer.deployPackage(min, 'packages/default.gbdialog');
// DISABLED: deployer.deployPackage(min, 'packages/default.gbdialog');
// Call the loadBot context.activity for all packages. // Call the loadBot context.activity for all packages.
this.invokeLoadBot(GBServer.globals.appPackages, GBServer.globals.sysPackages, min, GBServer.globals.server);
this.invokeLoadBot(appPackages, sysPackages, min, server);
// Serves individual URL for each bot conversational interface... // Serves individual URL for each bot conversational interface...
const url = `/api/messages/${instance.botId}`; const url = `/api/messages/${instance.botId}`;
server.post(url, async (req, res) => { GBServer.globals.server.post(url, async (req, res) => {
await this.receiver(adapter, req, res, conversationState, min, instance, appPackages); await this.receiver(adapter, req, res, conversationState, min, instance, GBServer.globals.appPackages);
}); });
GBLog.info(`GeneralBots(${instance.engineName}) listening on: ${url}.`); GBLog.info(`GeneralBots(${instance.engineName}) listening on: ${url}.`);
// Serves individual URL for each bot user interface. // Serves individual URL for each bot user interface.
if (process.env.DISABLE_WEB !== 'true') { if (process.env.DISABLE_WEB !== 'true') {
const uiUrl = `/${instance.botId}`; const uiUrl = `/${instance.botId}`;
server.use(uiUrl, express.static(urlJoin(GBDeployer.deployFolder, uiPackage, 'build'))); GBServer.globals.server.use(uiUrl, express.static(urlJoin(GBDeployer.deployFolder, GBMinService.uiPackage, 'build')));
GBLog.info(`Bot UI ${GBMinService.uiPackage} accessible at: ${uiUrl}.`);
GBLog.info(`Bot UI ${uiPackage} accessible at: ${uiUrl}.`);
} }
// Clients get redirected here in order to create an OAuth authorize url and redirect them to AAD. // Clients get redirected here in order to create an OAuth authorize url and redirect them to AAD.
// There they will authenticate and give their consent to allow this app access to // There they will authenticate and give their consent to allow this app access to
// some resource they own. // some resource they own.
this.handleOAuthRequests(GBServer.globals.server, min);
this.handleOAuthRequests(server, min);
// After consent is granted AAD redirects here. The ADAL library // After consent is granted AAD redirects here. The ADAL library
// is invoked via the AuthenticationContext and retrieves an // is invoked via the AuthenticationContext and retrieves an
// access token that can be used to access the user owned resource. // access token that can be used to access the user owned resource.
this.handleOAuthTokenRequests(GBServer.globals.server, min, instance);
this.handleOAuthTokenRequests(server, min, instance);
})
);
} }
private handleOAuthTokenRequests(server: any, min: GBMinInstance, instance: IGBInstance) { private handleOAuthTokenRequests(server: any, min: GBMinInstance, instance: IGBInstance) {
@ -235,13 +231,14 @@ export class GBMinService {
/** /**
* Returns the instance object to clients requesting bot info. * Returns the instance object to clients requesting bot info.
*/ */
private async sendInstanceToClient(req, bootInstance: IGBInstance, res: any, webchatToken: any) { private async handleGetInstanceFroClient(req: any, res: any) {
let botId = req.params.botId; let botId = req.params.botId;
if (botId === '[default]' || botId === undefined) { if (botId === '[default]' || botId === undefined) {
botId = GBConfigService.get('BOT_ID'); botId = GBConfigService.get('BOT_ID');
} }
const instance = await this.core.loadInstance(botId); const instance = await this.core.loadInstance(botId);
if (instance !== null) { if (instance !== null) {
const webchatToken = await this.getWebchatToken(instance);
const speechToken = instance.speechKey != null ? await this.getSTSToken(instance) : null; const speechToken = instance.speechKey != null ? await this.getSTSToken(instance) : null;
let theme = instance.theme; let theme = instance.theme;
if (theme === undefined) { if (theme === undefined) {

View file

@ -58,6 +58,9 @@ export class RootData {
public publicAddress: string; public publicAddress: string;
public server: any; public server: any;
public sysPackages: any[]; public sysPackages: any[];
public appPackages: any[];
minService: GBMinService;
bootInstance: IGBInstance;
} }
/** /**
@ -115,12 +118,12 @@ export class GBServer {
// Creates a boot instance or load it from storage. // Creates a boot instance or load it from storage.
let bootInstance: IGBInstance;
try { try {
await core.initStorage(); await core.initStorage();
} catch (error) { } catch (error) {
GBLog.verbose(`Error initializing storage: ${error}`); GBLog.verbose(`Error initializing storage: ${error}`);
bootInstance = await core.createBootInstance(core, azureDeployer, GBServer.globals.publicAddress); GBServer.globals.bootInstance = await core.createBootInstance(core, azureDeployer, GBServer.globals.publicAddress);
await core.initStorage(); await core.initStorage();
} }
@ -132,6 +135,8 @@ export class GBServer {
const sysPackages = core.loadSysPackages(core); const sysPackages = core.loadSysPackages(core);
await core.checkStorage(azureDeployer); await core.checkStorage(azureDeployer);
await deployer.deployPackages(core, server, appPackages); await deployer.deployPackages(core, server, appPackages);
GBServer.globals.sysPackages = sysPackages;
GBServer.globals.appPackages = appPackages;
// Loads boot bot and other instances. // Loads boot bot and other instances.
@ -141,24 +146,24 @@ export class GBServer {
'boot.gbot', 'boot.gbot',
'packages/boot.gbot' 'packages/boot.gbot'
); );
if (bootInstance === undefined) { if (GBServer.globals.bootInstance === undefined) {
bootInstance = packageInstance; GBServer.globals.bootInstance = packageInstance;
} }
// tslint:disable-next-line:prefer-object-spread // tslint:disable-next-line:prefer-object-spread
const fullInstance = Object.assign(packageInstance, bootInstance); const fullInstance = Object.assign(packageInstance, GBServer.globals.bootInstance);
await core.saveInstance(fullInstance); await core.saveInstance(fullInstance);
let instances: IGBInstance[] = await core.loadAllInstances(core, azureDeployer, let instances: IGBInstance[] = await core.loadAllInstances(core, azureDeployer,
GBServer.globals.publicAddress); GBServer.globals.publicAddress);
instances = await core.ensureInstances(instances, bootInstance, core); instances = await core.ensureInstances(instances, GBServer.globals.bootInstance, core);
if (bootInstance !== undefined) { if (GBServer.globals.bootInstance !== undefined) {
bootInstance = instances[0]; GBServer.globals.bootInstance = instances[0];
} }
// Builds minimal service infrastructure. // Builds minimal service infrastructure.
const minService: GBMinService = new GBMinService(core, conversationalService, adminService, deployer); const minService: GBMinService = new GBMinService(core, conversationalService, adminService, deployer);
await minService.buildMin(bootInstance, server, appPackages, sysPackages, instances, GBServer.globals.minService = minService;
deployer, GBServer.globals.publicAddress); await minService.buildMin( instances);
// Deployment of local applications for the first time. // Deployment of local applications for the first time.