fix(kb.gbapp): /publish review, error handling improved and clean up.

This commit is contained in:
rodrigorodriguez 2023-02-26 15:03:24 -03:00
parent b30e0160c4
commit c94228cd8d
8 changed files with 148 additions and 159 deletions

View file

@ -39,12 +39,13 @@
import crypto from 'crypto'; import crypto from 'crypto';
import urlJoin from 'url-join'; import urlJoin from 'url-join';
import { WaterfallDialog } from 'botbuilder-dialogs'; import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog, GBLog, IGBPackage } from 'botlib'; import { GBMinInstance, IGBDialog } from 'botlib';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js'; import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js';
import { GBImporter } from '../../core.gbapp/services/GBImporterService.js'; import { GBImporter } from '../../core.gbapp/services/GBImporterService.js';
import { Messages } from '../strings.js'; import { Messages } from '../strings.js';
import { GBAdminService } from '../services/GBAdminService.js'; import { GBAdminService } from '../services/GBAdminService.js';
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil } from 'pragmatismo-io-framework';
import { SecService } from '../../security.gbapp/services/SecService.js';
/** /**
* Dialogs for administration tasks. * Dialogs for administration tasks.
@ -144,36 +145,6 @@ export class AdminDialog extends IGBDialog {
try { try {
if (text === 'quit') { if (text === 'quit') {
return await step.replaceDialog('/'); return await step.replaceDialog('/');
} else if (cmdName === 'deployPackage' || cmdName === 'dp') {
await GBAdminService.deployPackageCommand(min, text, deployer);
return await step.replaceDialog('/admin', { firstRun: false });
} else if (cmdName === 'redeployPackage' || cmdName === 'rp') {
await min.conversationalService.sendText(min, step, 'The package is being *unloaded*...');
await GBAdminService.undeployPackageCommand(text, min);
await min.conversationalService.sendText(min, step, 'Now, *deploying* package...');
await GBAdminService.deployPackageCommand(min, text, deployer);
await min.conversationalService.sendText(
min,
step,
'Package deployed. Just need to rebuild the index... Doing it right now.'
);
await GBAdminService.rebuildIndexPackageCommand(min, deployer);
await min.conversationalService.sendText(min, step, 'Finished importing of that .gbkb package. Thanks.');
return await step.replaceDialog('/admin', { firstRun: false });
} else if (cmdName === 'undeployPackage' || cmdName === 'up') {
await min.conversationalService.sendText(min, step, 'The package is being *undeployed*...');
await GBAdminService.undeployPackageCommand(text, min);
await min.conversationalService.sendText(min, step, 'Package *undeployed*.');
return await step.replaceDialog('/admin', { firstRun: false });
} else if (cmdName === 'rebuildIndex' || cmdName === 'ri' || cmdName === 'Ri') {
await GBAdminService.rebuildIndexPackageCommand(min, deployer);
return await step.replaceDialog('/admin', { firstRun: false });
} else if (cmdName === 'syncBotServer') {
await GBAdminService.syncBotServerCommand(min, deployer);
return await step.replaceDialog('/admin', { firstRun: false });
} else if (cmdName === 'setupSecurity') { } else if (cmdName === 'setupSecurity') {
return await step.beginDialog('/setupSecurity'); return await step.beginDialog('/setupSecurity');
} else { } else {
@ -325,18 +296,18 @@ export class AdminDialog extends IGBDialog {
packages.push(`${botId}.gbot`); packages.push(`${botId}.gbot`);
skipError = true; skipError = true;
} else { } else {
await min.conversationalService.sendText(min, step, `Starting publishing for ${filename}...`);
packages.push(filename); packages.push(filename);
} }
await CollectionUtil.asyncForEach(packages, async packageName => { await CollectionUtil.asyncForEach(packages, async packageName => {
let cmd1; let cmd1;
if ( if (
packageName.indexOf('gbdialog') !== -1 || packageName.toLowerCase() === 'gbdialog' ||
packageName.indexOf('gbkb') !== -1 || packageName.toLowerCase() === 'gbkb' ||
packageName.indexOf('gbot') !== -1 || packageName.toLowerCase() === 'gbot' ||
packageName.indexOf('gbtheme') !== -1 packageName.toLowerCase() === 'gbtheme'
) { ) {
packageName = `${min.botId}.${packageName}`; packageName = `${min.botId}.${packageName}`;
} }
@ -353,10 +324,23 @@ export class AdminDialog extends IGBDialog {
const cmd2 = `undeployPackage ${packageName}`; const cmd2 = `undeployPackage ${packageName}`;
await GBAdminService.undeployPackageCommand(cmd2, min); await GBAdminService.undeployPackageCommand(cmd2, min);
} }
await GBAdminService.deployPackageCommand(min, cmd1, deployer); let sec = new SecService();
await min.conversationalService.sendText(min, step, `Finished publishing ${packageName}.`); const member = step.context.activity.from;
const user = await sec.ensureUser(
min.instance.instanceId,
member.id,
member.name,
'',
'web',
member.name,
null
);
await GBAdminService.deployPackageCommand(min, user, cmd1, deployer);
}); });
await min.conversationalService.sendText(min, step, Messages[locale].publish_success); await min.conversationalService.sendText(min, step, `Training is finished.`);
if (!step.activeDialog.state.options.confirm) { if (!step.activeDialog.state.options.confirm) {
return await step.replaceDialog('/ask', { isReturning: true }); return await step.replaceDialog('/ask', { isReturning: true });
} else { } else {

View file

@ -53,6 +53,7 @@ import { caseSensitive_Numbs_SpecialCharacters_PW, lowercase_PW } from 'super-st
import crypto from 'crypto'; import crypto from 'crypto';
import Fs from 'fs'; import Fs from 'fs';
import { GBServer } from '../../../src/app.js'; import { GBServer } from '../../../src/app.js';
import { GuaribasUser } from '../../security.gbapp/models/index.js';
/** /**
* Services for server administration. * Services for server administration.
@ -138,7 +139,7 @@ export class GBAdminService implements IGBAdminService {
public static isSharePointPath(path: string) { public static isSharePointPath(path: string) {
return path.indexOf('sharepoint.com') !== -1; return path.indexOf('sharepoint.com') !== -1;
} }
public static async deployPackageCommand(min: GBMinInstance, text: string, deployer: IGBDeployer) { public static async deployPackageCommand(min: GBMinInstance, user: GuaribasUser, text: string, deployer: IGBDeployer) {
const packageName = text.split(' ')[1]; const packageName = text.split(' ')[1];
if (!this.isSharePointPath(packageName)) { if (!this.isSharePointPath(packageName)) {
@ -146,7 +147,7 @@ export class GBAdminService implements IGBAdminService {
if (additionalPath === undefined) { if (additionalPath === undefined) {
throw new Error('ADDITIONAL_DEPLOY_PATH is not set and deployPackage was called.'); throw new Error('ADDITIONAL_DEPLOY_PATH is not set and deployPackage was called.');
} }
await deployer.deployPackage(min, urlJoin(additionalPath, packageName)); await deployer['deployPackage2'](min, user, urlJoin(additionalPath, packageName));
} else { } else {
const siteName = text.split(' ')[1]; const siteName = text.split(' ')[1];
const folderName = text.split(' ')[2]; const folderName = text.split(' ')[2];
@ -157,7 +158,7 @@ export class GBAdminService implements IGBAdminService {
// of local resources is required. // of local resources is required.
await deployer['downloadFolder'](min, Path.join('work', `${min.instance.botId}.gbai`), Path.basename(folderName)); await deployer['downloadFolder'](min, Path.join('work', `${min.instance.botId}.gbai`), Path.basename(folderName));
await deployer.deployPackage(min, localFolder); await deployer['deployPackage2'](min, user, localFolder);
} }
} }
public static async rebuildIndexPackageCommand(min: GBMinInstance, deployer: GBDeployer) { public static async rebuildIndexPackageCommand(min: GBMinInstance, deployer: GBDeployer) {

View file

@ -362,15 +362,13 @@ export class GBConversationalService {
'content-type': 'application/json', 'content-type': 'application/json',
authorization: `Bearer ${min.instance.smsSecret}` authorization: `Bearer ${min.instance.smsSecret}`
}, },
body: body: JSON.stringify({
JSON.stringify({
numero: `${mobile}`, numero: `${mobile}`,
servico: 'short', servico: 'short',
mensagem: text, mensagem: text,
parceiro_id: '', parceiro_id: '',
codificacao: '0' codificacao: '0'
}) })
}; };
try { try {
@ -383,8 +381,7 @@ export class GBConversationalService {
return Promise.reject(new Error(msg)); return Promise.reject(new Error(msg));
} }
} else { } else {
return new Promise( return new Promise((resolve: any, reject: any): any => {
(resolve: any, reject: any): any => {
const nexmo = new Nexmo({ const nexmo = new Nexmo({
apiKey: min.instance.smsKey, apiKey: min.instance.smsKey,
apiSecret: min.instance.smsSecret apiSecret: min.instance.smsSecret
@ -399,8 +396,7 @@ export class GBConversationalService {
resolve(data); resolve(data);
} }
}); });
} });
);
} }
} }
@ -439,9 +435,7 @@ export class GBConversationalService {
const transcoder = new prism.FFmpeg({ const transcoder = new prism.FFmpeg({
args: ['-analyzeduration', '0', '-loglevel', '0', '-f', 'opus', '-ar', '16000', '-ac', '1'] args: ['-analyzeduration', '0', '-loglevel', '0', '-f', 'opus', '-ar', '16000', '-ac', '1']
}); });
Fs.createReadStream(waveFilename) Fs.createReadStream(waveFilename).pipe(transcoder).pipe(output);
.pipe(transcoder)
.pipe(output);
let url = urlJoin(GBServer.globals.publicAddress, 'audios', oggFilenameOnly); let url = urlJoin(GBServer.globals.publicAddress, 'audios', oggFilenameOnly);
resolve(url); resolve(url);
@ -528,9 +522,7 @@ export class GBConversationalService {
text = await min.conversationalService.translate( text = await min.conversationalService.translate(
min, min,
answer, answer,
user.locale user.locale ? user.locale : min.core.getParam<string>(min.instance, 'Locale', GBConfigService.get('LOCALE'))
? user.locale
: min.core.getParam<string>(min.instance, 'Locale', GBConfigService.get('LOCALE'))
); );
GBLog.verbose(`Translated text(playMarkdown): ${text}.`); GBLog.verbose(`Translated text(playMarkdown): ${text}.`);
} }
@ -851,9 +843,7 @@ export class GBConversationalService {
} }
GBLog.info( GBLog.info(
`NLP called: ${intent}, entities: ${ `NLP called: ${intent}, entities: ${nlp.entities.length}, score: ${score} > required (nlpScore): ${instanceScore}`
nlp.entities.length
}, score: ${score} > required (nlpScore): ${instanceScore}`
); );
step.activeDialog.state.options.entities = nlp.entities; step.activeDialog.state.options.entities = nlp.entities;
@ -956,10 +946,14 @@ export class GBConversationalService {
return Promise.reject(new Error(msg)); return Promise.reject(new Error(msg));
} }
} else { } else {
const url = urlJoin(endPoint, 'translate', new URLSearchParams({ const url = urlJoin(
endPoint,
'translate',
new URLSearchParams({
'api-version': '3.0', 'api-version': '3.0',
to: language to: language
}).toString()); }).toString()
);
let options = { let options = {
method: 'POST', method: 'POST',
headers: { headers: {
@ -992,9 +986,7 @@ export class GBConversationalService {
text = await min.conversationalService.translate( text = await min.conversationalService.translate(
min, min,
text, text,
user.locale user.locale ? user.locale : min.core.getParam<string>(min.instance, 'Locale', GBConfigService.get('LOCALE'))
? user.locale
: min.core.getParam<string>(min.instance, 'Locale', GBConfigService.get('LOCALE'))
); );
GBLog.verbose(`Translated text(prompt): ${text}.`); GBLog.verbose(`Translated text(prompt): ${text}.`);
} }
@ -1013,10 +1005,15 @@ export class GBConversationalService {
const member = step.context.activity.from; const member = step.context.activity.from;
let sec = new SecService(); let sec = new SecService();
let user = await sec.getUserFromSystemId(step.context.activity.from.id); let user = await sec.getUserFromSystemId(step.context.activity.from.id);
await this['sendTextWithOptionsAndUser'](min, user, step, text, true, null);
}
public async sendTextWithOptionsAndUser(min: GBMinInstance, user, step, text, translate, keepTextList) {
const member = step ? step.context.activity.from : null;
if (translate) {
let replacements = []; let replacements = [];
if (translate) {
// To fix MSFT bug. // To fix MSFT bug.
if (keepTextList) { if (keepTextList) {
@ -1058,7 +1055,7 @@ export class GBConversationalService {
} }
analytics.createMessage(min.instance.instanceId, conversation, null, text); analytics.createMessage(min.instance.instanceId, conversation, null, text);
if (!isNaN(member.id) && !member.id.startsWith('1000')) { if (member && !isNaN(member.id) && !member.id.startsWith('1000')) {
const to = step.context.activity.group ? step.context.activity.group : member.id; const to = step.context.activity.group ? step.context.activity.group : member.id;
await min.whatsAppDirectLine.sendToDevice(to, text, step.context.activity.conversation.id); await min.whatsAppDirectLine.sendToDevice(to, text, step.context.activity.conversation.id);
@ -1066,7 +1063,6 @@ export class GBConversationalService {
await step.context.sendActivity(text); await step.context.sendActivity(text);
} }
} }
public async broadcast(min: GBMinInstance, message: string) { public async broadcast(min: GBMinInstance, message: string) {
GBLog.info(`Sending broadcast notifications...`); GBLog.info(`Sending broadcast notifications...`);

View file

@ -559,11 +559,13 @@ export class GBDeployer implements IGBDeployer {
instanceId: instanceId instanceId: instanceId
}); });
} }
public async deployPackage(min: GBMinInstance, localPath: string) {
// TODO: @alanperdomo: Adjust interface mismatch.
}
/** /**
* Deploys a folder into the bot storage. * Deploys a folder into the bot storage.
*/ */
public async deployPackage(min: GBMinInstance, localPath: string) { public async deployPackage2(min: GBMinInstance, user, localPath: string) {
const packageType = Path.extname(localPath); const packageType = Path.extname(localPath);
let handled = false; let handled = false;
let pck = null; let pck = null;
@ -717,7 +719,7 @@ export class GBDeployer implements IGBDeployer {
try { try {
GBLogEx.info(instance.instanceId, `Acquiring rebuildIndex mutex...`); GBLogEx.info(instance.instanceId, `Acquiring rebuildIndex mutex...`);
release = await GBServer.globals.indexSemaphore.acquire(); release = await GBServer.globals.indexSemaphore.acquire();
GBLogEx.info(instance.instanceId, `Acquire rebuildIndex done.`);
const search = new AzureSearch( const search = new AzureSearch(
instance.searchKey, instance.searchKey,
instance.searchHost, instance.searchHost,
@ -729,7 +731,11 @@ export class GBDeployer implements IGBDeployer {
try { try {
await search.createDataSource(dsName, dsName, 'GuaribasQuestion', 'azuresql', connectionString); await search.createDataSource(dsName, dsName, 'GuaribasQuestion', 'azuresql', connectionString);
} catch (err) { } catch (err) {
GBLog.error(err); // If it is a 404 there is nothing to delete as it is the first creation.
if (err.code !== 400 && err.message.indexOf('already exists') !==-1) {
throw err;
}
} }
// Removes the index. // Removes the index.
@ -739,11 +745,11 @@ export class GBDeployer implements IGBDeployer {
} catch (err) { } catch (err) {
// If it is a 404 there is nothing to delete as it is the first creation. // If it is a 404 there is nothing to delete as it is the first creation.
if (err.code !== 404 && err.code !== 'OperationNotAllowed') { if (err.code !== 'ResourceNameAlreadyInUse') {
throw err;
} }
} }
GBLogEx.info(instance.instanceId, `Acquire rebuildIndex done.`);
await search.rebuildIndex(instance.searchIndexer); await search.rebuildIndex(instance.searchIndexer);
release(); release();
GBLogEx.info(instance.instanceId, `Released rebuildIndex mutex.`); GBLogEx.info(instance.instanceId, `Released rebuildIndex mutex.`);

View file

@ -282,26 +282,27 @@ export class GBMinService {
// min['groupCache'] = await KBService.getGroupReplies(instance.instanceId); // min['groupCache'] = await KBService.getGroupReplies(instance.instanceId);
GBServer.globals.minInstances.push(min); GBServer.globals.minInstances.push(min);
const user = null; // No user context.
await this.deployer.deployPackage(min, 'packages/default.gbtheme'); await this.deployer['deployPackage2'](min, user, 'packages/default.gbtheme');
// Install per bot deployed packages. // Install per bot deployed packages.
let packagePath = `work/${min.botId}.gbai/${min.botId}.gbdialog`; let packagePath = `work/${min.botId}.gbai/${min.botId}.gbdialog`;
if (Fs.existsSync(packagePath)) { if (Fs.existsSync(packagePath)) {
await this.deployer.deployPackage(min, packagePath); await this.deployer['deployPackage2'](min, user, packagePath);
} }
packagePath = `work/${min.botId}.gbai/${min.botId}.gbapp`; packagePath = `work/${min.botId}.gbai/${min.botId}.gbapp`;
if (Fs.existsSync(packagePath)) { if (Fs.existsSync(packagePath)) {
await this.deployer.deployPackage(min, packagePath); await this.deployer['deployPackage2'](min, user, packagePath);
} }
packagePath = `work/${min.botId}.gbai/${min.botId}.gbtheme`; packagePath = `work/${min.botId}.gbai/${min.botId}.gbtheme`;
if (Fs.existsSync(packagePath)) { if (Fs.existsSync(packagePath)) {
await this.deployer.deployPackage(min, packagePath); await this.deployer['deployPackage2'](min, user, packagePath);
} }
packagePath = `work/${min.botId}.gbai/${min.botId}.gblib`; packagePath = `work/${min.botId}.gbai/${min.botId}.gblib`;
if (Fs.existsSync(packagePath)) { if (Fs.existsSync(packagePath)) {
await this.deployer.deployPackage(min, packagePath); await this.deployer['deployPackage2'](min, user, packagePath);
} }
let dir = `work/${min.botId}.gbai/cache`; let dir = `work/${min.botId}.gbai/cache`;
@ -375,6 +376,7 @@ export class GBMinService {
}; };
await CollectionUtil.asyncForEach(steps, async step => { await CollectionUtil.asyncForEach(steps, async step => {
client.apis.Conversations.Conversations_PostActivity({ client.apis.Conversations.Conversations_PostActivity({
conversationId: conversationId, conversationId: conversationId,
activity: { activity: {
@ -388,7 +390,7 @@ export class GBMinService {
} }
}); });
await sleep(5000); await sleep(3000);
}); });
} }

View file

@ -65,53 +65,52 @@ export class GuaribasSubject extends Model<GuaribasSubject> {
@PrimaryKey @PrimaryKey
@AutoIncrement @AutoIncrement
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
subjectId: number; declare subjectId: number;
@Column(DataType.INTEGER)
internalId: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
title: string; declare internalId: string;
declare title: string;
@Column(DataType.STRING(512)) @Column(DataType.STRING(512))
description: string; declare description: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
from: string; declare from: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
to: string; declare to: string;
@ForeignKey(() => GuaribasSubject) @ForeignKey(() => GuaribasSubject)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
parentSubjectId: number; declare parentSubjectId: number;
@BelongsTo(() => GuaribasSubject, 'parentSubjectId') @BelongsTo(() => GuaribasSubject, 'parentSubjectId')
parentSubject: GuaribasSubject; parentSubject: GuaribasSubject;
@HasMany(() => GuaribasSubject, { foreignKey: 'parentSubjectId' }) @HasMany(() => GuaribasSubject, { foreignKey: 'parentSubjectId' })
childrenSubjects: GuaribasSubject[]; declare childrenSubjects: GuaribasSubject[];
@ForeignKey(() => GuaribasInstance) @ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
instanceId: number; declare instanceId: number;
@BelongsTo(() => GuaribasInstance) @BelongsTo(() => GuaribasInstance)
instance: GuaribasInstance; declare instance: GuaribasInstance;
@ForeignKey(() => GuaribasUser) @ForeignKey(() => GuaribasUser)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
responsibleUserId: number; declare responsibleUserId: number;
@BelongsTo(() => GuaribasUser) @BelongsTo(() => GuaribasUser)
responsibleUser: GuaribasUser; declare responsibleUser: GuaribasUser;
@ForeignKey(() => GuaribasPackage) @ForeignKey(() => GuaribasPackage)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
packageId: number; declare packageId: number;
@BelongsTo(() => GuaribasPackage) @BelongsTo(() => GuaribasPackage)
package: GuaribasPackage; declare package: GuaribasPackage;
} }
/** /**

View file

@ -869,8 +869,9 @@ export class KBService implements IGBKBService {
let level; let level;
for (level = 0; level < MAX_LEVEL; level++) { for (level = 0; level < MAX_LEVEL; level++) {
menu = row._cells[level]; const cell = row._cells[level];
if (menu && menu.text) { if (cell && cell.text) {
menu = cell.text;
break; break;
} }
} }
@ -890,10 +891,10 @@ export class KBService implements IGBKBService {
activeChildrenGivenLevel[level] = childrenNode; activeChildrenGivenLevel[level] = childrenNode;
// Insert the object into JSON. // Insert the object into JSON.
const description = row._cells[level + 1]?row._cells[level + 1].text: null;
activeObj = { activeObj = {
title: menu, title: menu,
description: row._cells[level + 1], description: description,
id: menu, id: menu,
children: [] children: []
}; };
@ -984,7 +985,7 @@ export class KBService implements IGBKBService {
min['groupCache'] = await KBService.getGroupReplies(instance.instanceId); min['groupCache'] = await KBService.getGroupReplies(instance.instanceId);
await KBService.RefreshNER(min); await KBService.RefreshNER(min);
GBLog.info(`[GBDeployer] Opening package: ${localPath}`); GBLog.info(`[GBDeployer] Start Bot Server Side Rendering... ${localPath}`);
const html = await GBSSR.getHTML(min); const html = await GBSSR.getHTML(min);
const path = Path.join( const path = Path.join(
process.env.PWD, process.env.PWD,
@ -993,7 +994,7 @@ export class KBService implements IGBKBService {
`${min.instance.botId}.gbui`, `${min.instance.botId}.gbui`,
'index.html' 'index.html'
); );
GBLogEx.info(min, `[GBDeployer] Generating SSR HTML in ${path}.`); GBLogEx.info(min, `[GBDeployer] Saving SSR HTML in ${path}.`);
Fs.writeFileSync(path, html, 'utf8'); Fs.writeFileSync(path, html, 'utf8');
GBLog.info(`[GBDeployer] Finished import of ${localPath}`); GBLog.info(`[GBDeployer] Finished import of ${localPath}`);

View file

@ -103,11 +103,11 @@ export class GBServer {
server.use(bodyParser.urlencoded({ extended: true })); server.use(bodyParser.urlencoded({ extended: true }));
process.on('unhandledRejection', (err, p) => { process.on('unhandledRejection', (err, p) => {
GBLog.error(`UNHANDLED_REJECTION(promises): ${p} ${err.toString()}`); GBLog.error(`UNHANDLED_REJECTION(promises): ${err.toString()} ${err['stack']?'\n'+err['stack']:''}`);
}); });
process.on('uncaughtException', (err, origin) => { process.on('uncaughtException', (err, p) => {
GBLog.error(`UNCAUGHT_EXCEPTION: ${err.toString()}`); GBLog.error(`UNCAUGHT_EXCEPTION: ${err.toString()} ${err['stack']?'\n'+err['stack']:''}`);
}); });
// Creates working directory. // Creates working directory.