feat(whatsapp.gblib): Same chat-api provider now shared between instances and deploy improvements.

This commit is contained in:
Rodrigo Rodriguez 2019-08-22 17:28:11 -03:00
parent 9a961e72a1
commit b2da413f0f
9 changed files with 54 additions and 47 deletions

View file

@ -73,7 +73,6 @@
"cli-spinner": "0.2.10", "cli-spinner": "0.2.10",
"csv-parse": "4.4.1", "csv-parse": "4.4.1",
"dotenv-extended": "2.4.0", "dotenv-extended": "2.4.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", "express-remove-route": "^1.0.0",

View file

@ -537,6 +537,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
const resChannel = await httpClient.sendRequest(req); const resChannel = await httpClient.sendRequest(req);
const key = JSON.parse(resChannel.bodyAsText).properties.properties.sites[0].key; const key = JSON.parse(resChannel.bodyAsText).properties.properties.sites[0].key;
instance.webchatKey = key; instance.webchatKey = key;
instance.whatsappBotKey = key;
resolve(instance); resolve(instance);
} catch (error) { } catch (error) {
reject(error); reject(error);

View file

@ -43,7 +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'); const rimraf = require('rimraf');
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';
@ -190,6 +190,8 @@ export class GBDeployer {
); );
} else { } else {
console.log(GBServer.globals.bootInstance);
instance = Object.assign(instance, GBServer.globals.bootInstance); instance = Object.assign(instance, GBServer.globals.bootInstance);
instance = await service.internalDeployBot( instance = await service.internalDeployBot(
instance, instance,
@ -214,17 +216,13 @@ export class GBDeployer {
/** /**
* Deploys a bot to the storage. * UndDeploys a bot to the storage.
*/ */
public async undeployBot(botId: string, packageName: string): Promise<void> { public async undeployBot(botId: string, packageName: string): Promise<void> {
const service = new AzureDeployerService(this); const service = new AzureDeployerService(this);
const username = GBConfigService.get('CLOUD_USERNAME');
const password = GBConfigService.get('CLOUD_PASSWORD');
const group = GBConfigService.get('CLOUD_GROUP'); 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)) { if (await service.botExists(botId, group)) {
@ -235,10 +233,14 @@ export class GBDeployer {
} }
GBServer.globals.minService.unmountBot(botId); GBServer.globals.minService.unmountBot(botId);
await this.core.deleteInstance(botId); await this.core.deleteInstance(botId);
const packageFolder = urlJoin(process.env.PWD, 'work', packageName); const packageFolder =Path.join(process.env.PWD, 'work', packageName);
await emptyDir(packageFolder); rimraf.sync(packageFolder);
} }
public async undeployTheme(packageName: string): Promise<void> {
const packageFolder = Path.join(process.env.PWD, 'work', packageName);
rimraf.sync(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({
@ -270,13 +272,13 @@ export class GBDeployer {
case '.gbkb': case '.gbkb':
const service = new KBService(this.core.sequelize); const service = new KBService(this.core.sequelize);
await service.deployKb(this.core, this, localPath); await service.deployKb(this.core, this, localPath);
break;
case '.gbdialog': case '.gbdialog':
const vm = new GBVMService(); const vm = new GBVMService();
await vm.loadDialogPackage(localPath, min, this.core, this); await vm.loadDialogPackage(localPath, min, this.core, this);
break;
default: default:
const err = GBError.create(`Unhandled package type: ${packageType}.`); const err = GBError.create(`Unhandled package type: ${packageType}.`);
@ -300,13 +302,18 @@ export class GBDeployer {
case '.gbkb': case '.gbkb':
const service = new KBService(this.core.sequelize); const service = new KBService(this.core.sequelize);
return service.undeployKbFromStorage(instance, this, p.packageId); return service.undeployKbFromStorage(instance, this, p.packageId);
case '.gbui': case '.gbui':
break;
case '.gbtheme':
this.undeployTheme(packageName);
break; break;
case '.gbdialog': case '.gbdialog':
break; break;
default: default:

View file

@ -41,6 +41,7 @@ import fs = require('fs');
import urlJoin = require('url-join'); import urlJoin = require('url-join');
import { GuaribasInstance } from '../models/GBModel'; import { GuaribasInstance } from '../models/GBModel';
import { GBConfigService } from './GBConfigService'; import { GBConfigService } from './GBConfigService';
import { GBServer } from '../../../src/app';
/** /**
* Handles the importing of packages. * Handles the importing of packages.
@ -70,7 +71,7 @@ export class GBImporter {
const settings = JSON.parse(fs.readFileSync(urlJoin(localPath, 'settings.json'), 'utf8')); const settings = JSON.parse(fs.readFileSync(urlJoin(localPath, 'settings.json'), 'utf8'));
const servicesJson = JSON.parse(fs.readFileSync(urlJoin(localPath, 'services.json'), 'utf8')); const servicesJson = JSON.parse(fs.readFileSync(urlJoin(localPath, 'services.json'), 'utf8'));
packageJson = { ...packageJson, ...settings, ...servicesJson }; packageJson = { ...GBServer.globals.bootInstance, ...packageJson, ...settings, ...servicesJson };
if (botId !== undefined) { if (botId !== undefined) {
packageJson.botId = botId; packageJson.botId = botId;
@ -81,7 +82,7 @@ export class GBImporter {
return this.core.saveInstance(instance); return this.core.saveInstance(instance);
} else { } else {
return GuaribasInstance.create(packageJson); return await GuaribasInstance.create(packageJson);
} }
} }
} }

View file

@ -125,6 +125,16 @@ export class GBMinService {
})(); })();
}); });
} }
const url = '/webhooks/whatsapp';
GBServer.globals.server.post(url, async (req, res) => {
const text = req.body.messages[0].body;
const from = req.body.messages[0].author.split('@')[0];
const fromName = req.body.messages[0].senderName;
let botId = 'subway-prd';
const min = GBServer.globals.minInstances.filter(p => p.botId === botId)[0];
(min as any).whatsAppDirectLine.received(req, res);
});
await Promise.all( await Promise.all(
instances.map(async instance => { instances.map(async instance => {
@ -141,12 +151,15 @@ export class GBMinService {
const uiUrl = `/${botId}`; const uiUrl = `/${botId}`;
removeRoute(GBServer.globals.server, uiUrl); removeRoute(GBServer.globals.server, uiUrl);
GBServer.globals.minInstances = GBServer.globals.minInstances.filter(p => p.botId !== botId);
} }
public async mountBot(instance: IGBInstance) { public async mountBot(instance: IGBInstance) {
// Build bot adapter. // Build bot adapter.
const { min, adapter, conversationState } = await this.buildBotAdapter(instance, GBServer.globals.publicAddress, GBServer.globals.sysPackages); const { min, adapter, conversationState } = await this.buildBotAdapter(instance, GBServer.globals.publicAddress, GBServer.globals.sysPackages);
GBServer.globals.minInstances.push(min);
// Install default VBA module. // Install default VBA module.
//this.deployer.deployPackage(min, 'packages/default.gbdialog'); //this.deployer.deployPackage(min, 'packages/default.gbdialog');
@ -359,12 +372,6 @@ export class GBMinService {
let index = 0; let index = 0;
sysPackages.forEach(e => { sysPackages.forEach(e => {
e.loadBot(min); e.loadBot(min);
if (index === 6) { // TODO: Remove this magic number and use a map.
const url = '/instances/:botId/whatsapp';
server.post(url, async (req, res) => {
await (e as any).channel.received(req, res);
});
}
index++; index++;
}, this); }, this);

View file

@ -66,9 +66,6 @@ export class AskDialog extends IGBDialog {
min.dialogs.add(new WaterfallDialog('/answerEvent', AskDialog.getAnswerEventDialog(service, min))); min.dialogs.add(new WaterfallDialog('/answerEvent', AskDialog.getAnswerEventDialog(service, min)));
min.dialogs.add(new WaterfallDialog('/answer', AskDialog.getAnswerDialog(min, service))); min.dialogs.add(new WaterfallDialog('/answer', AskDialog.getAnswerDialog(min, service)));
min.dialogs.add(new WaterfallDialog('/ask', AskDialog.getAskDialog(min))); min.dialogs.add(new WaterfallDialog('/ask', AskDialog.getAskDialog(min)));
} }
private static getAskDialog(min: GBMinInstance) { private static getAskDialog(min: GBMinInstance) {

View file

@ -46,13 +46,12 @@ import { WhatsappDirectLine } from './services/WhatsappDirectLine';
export class GBWhatsappPackage implements IGBPackage { export class GBWhatsappPackage implements IGBPackage {
public sysPackages: IGBPackage[]; public sysPackages: IGBPackage[];
public channel: WhatsappDirectLine;
public loadBot(min: GBMinInstance): void { public loadBot(min: GBMinInstance): void {
// Only loads engine if it is defined on services.json. // Only loads engine if it is defined on services.json.
if (min.instance.whatsappBotKey !== undefined) { if (min.instance.whatsappBotKey !== undefined) {
this.channel = new WhatsappDirectLine( min.whatsAppDirectLine = new WhatsappDirectLine(
min.botId, min.botId,
min.instance.whatsappBotKey, min.instance.whatsappBotKey,
min.instance.whatsappServiceKey, min.instance.whatsappServiceKey,
@ -62,9 +61,6 @@ export class GBWhatsappPackage implements IGBPackage {
} }
} }
public getChannel() {
return this.channel;
}
public getDialogs(min: GBMinInstance) { public getDialogs(min: GBMinInstance) {
GBLog.verbose(`getDialogs called.`); GBLog.verbose(`getDialogs called.`);

View file

@ -86,7 +86,7 @@ export class WhatsappDirectLine extends GBService {
url: urlJoin(this.whatsappServiceUrl, 'webhook'), url: urlJoin(this.whatsappServiceUrl, 'webhook'),
qs: { qs: {
token: this.whatsappServiceKey, token: this.whatsappServiceKey,
webhookUrl: `${GBServer.globals.publicAddress}/instances/${this.botId}/whatsapp`, webhookUrl: `${GBServer.globals.publicAddress}/webhooks/whatsapp`,
set: true set: true
}, },
headers: { headers: {

View file

@ -49,8 +49,6 @@ import { GBDeployer } from '../packages/core.gbapp/services/GBDeployer';
import { GBImporter } from '../packages/core.gbapp/services/GBImporterService'; import { GBImporter } from '../packages/core.gbapp/services/GBImporterService';
import { GBMinService } from '../packages/core.gbapp/services/GBMinService'; import { GBMinService } from '../packages/core.gbapp/services/GBMinService';
const appPackages: IGBPackage[] = [];
/** /**
* Global shared server data; * Global shared server data;
*/ */
@ -61,6 +59,7 @@ export class RootData {
public appPackages: any[]; public appPackages: any[];
minService: GBMinService; minService: GBMinService;
bootInstance: IGBInstance; bootInstance: IGBInstance;
public minInstances: any[];
} }
/** /**
@ -81,6 +80,10 @@ export class GBServer {
const port = GBConfigService.getServerPort(); const port = GBConfigService.getServerPort();
const server = express(); const server = express();
GBServer.globals.server = server; GBServer.globals.server = server;
GBServer.globals.appPackages = [];
GBServer.globals.sysPackages = [];
GBServer.globals.minInstances = [];
server.use(bodyParser.json()); server.use(bodyParser.json());
server.use(bodyParser.urlencoded({ extended: true })); server.use(bodyParser.urlencoded({ extended: true }));
@ -92,7 +95,6 @@ export class GBServer {
// Reads basic configuration, initialize minimal services. // Reads basic configuration, initialize minimal services.
const core: IGBCoreService = new GBCoreService(); const core: IGBCoreService = new GBCoreService();
const importer: GBImporter = new GBImporter(core); const importer: GBImporter = new GBImporter(core);
const deployer: GBDeployer = new GBDeployer(core, importer); const deployer: GBDeployer = new GBDeployer(core, importer);
const azureDeployer: AzureDeployerService = new AzureDeployerService(deployer); const azureDeployer: AzureDeployerService = new AzureDeployerService(deployer);
@ -118,7 +120,6 @@ export class GBServer {
// Creates a boot instance or load it from storage. // Creates a boot instance or load it from storage.
try { try {
await core.initStorage(); await core.initStorage();
} catch (error) { } catch (error) {
@ -132,11 +133,9 @@ export class GBServer {
// Deploys system and user packages. // Deploys system and user packages.
GBLog.info(`Deploying packages...`); GBLog.info(`Deploying packages...`);
const sysPackages = core.loadSysPackages(core); GBServer.globals.sysPackages = core.loadSysPackages(core);
await core.checkStorage(azureDeployer); await core.checkStorage(azureDeployer);
await deployer.deployPackages(core, server, appPackages); await deployer.deployPackages(core, server, GBServer.globals.appPackages);
GBServer.globals.sysPackages = sysPackages;
GBServer.globals.appPackages = appPackages;
// Loads boot bot and other instances. // Loads boot bot and other instances.