diff --git a/packages/basic.gblib/services/GBVMService.ts b/packages/basic.gblib/services/GBVMService.ts index 11bd9246..9ca916d1 100644 --- a/packages/basic.gblib/services/GBVMService.ts +++ b/packages/basic.gblib/services/GBVMService.ts @@ -84,6 +84,28 @@ export class GBVMService extends GBService { }); } + public static compare(obj1, obj2) { + //check for obj2 overlapping props + if (!Object.keys(obj2).every(key => obj1.hasOwnProperty(key))) { + return false; + } + + //check every key for being same + return Object.keys(obj1).every(function (key) { + + //if object + if ((typeof obj1[key] == "object") && (typeof obj2[key] == "object")) { + + //recursively check + return GBVMService.compare(obj1[key], obj2[key]); + } else { + + //do the normal compare + return obj1[key] === obj2[key]; + } + }); + } + public async loadDialog(filename: string, folder: string, min: GBMinInstance) { const wordFile = filename; const vbsFile = filename.substr(0, filename.indexOf('docx')) + 'vbs'; @@ -175,9 +197,10 @@ export class GBVMService extends GBService { // Syncronizes Database Objects with the ones returned from "Word". const tablesFile = urlJoin(folder, `${filename}.tables.json`); + let sync = false; + if (Fs.existsSync(tablesFile)) { const minBoot = GBServer.globals.minBoot; - GBLogEx.info(min, `BASIC: Sync TABLE keywords storage for ${min.botId}...`); const tableDef = JSON.parse(Fs.readFileSync(tablesFile, 'utf8')) as any; @@ -204,18 +227,45 @@ export class GBVMService extends GBService { const associations = []; - Object.keys(tableDef.tables).forEach(tableName => { - const t = tableDef[tableName]; + tableDef.forEach(t => { Object.keys(t.fields).forEach(key => { let obj = t.fields[key]; obj.type = getTypeBasedOnCondition(obj.type); if (obj.type.key === "TABLE") { + obj.type.key = "INTEGER" associations.push({ from: t.name, to: obj.type.name }); } if (key.toLowerCase() === 'id') { obj['primaryKey'] = true; } }); + + // Only syncs if there is any difference. + + const model = minBoot.core.sequelize.models[t.name]; + if (model) { + + // Except Id, checks if has same number of fields. + + let equals = 0; + Object.keys(t.fields).forEach(key => { + let obj1 = t.fields[key]; + let obj2 = model['fieldRawAttributesMap'][key]; + + if (key !== "id"){ + if (obj1 && obj2) + { + equals++; + } + } + }); + + + if (equals != Object.keys(t.fields).length) { + sync = true; + } + } + minBoot.core.sequelize.define(t.name, t.fields); }); @@ -228,17 +278,24 @@ export class GBVMService extends GBService { }); + if (sync) { + GBLogEx.info(min, `BASIC: Syncing changes for TABLE keywords (${min.botId})...`); - await minBoot.core.sequelize.sync({ - alter: true, - force: false // Keep it false due to data loss danger. - }); + await minBoot.core.sequelize.sync({ + alter: true, + force: false // Keep it false due to data loss danger. + }); + GBLogEx.info(min, `BASIC: Done sync for ${min.botId} storage tables...`); + } + else + { + GBLogEx.verbose(min, `BASIC: TABLE keywords already up to date (${min.botId})...`); + } } const parsedCode: string = Fs.readFileSync(jsfile, 'utf8'); min.sandBoxMap[mainName.toLowerCase().trim()] = parsedCode; return filename; - } public async translateBASIC(mainName, filename: any, min: GBMinInstance) { @@ -404,7 +461,6 @@ export class GBVMService extends GBService { // Creates an empty object that will receive Sequelize fields. - const path = DialogKeywords.getGBAIPath(min.botId, `gbdialog`); const tablesFile = `${task.file}.tables.json`; Fs.writeFileSync(tablesFile, JSON.stringify(task.tables)); @@ -574,9 +630,9 @@ export class GBVMService extends GBService { tables.push({ name: table, fields: fields }); - - fields = []; + + fields = {}; table = null; emmit = false; } @@ -604,7 +660,7 @@ export class GBVMService extends GBService { lines[i - 1] = emmit ? line : ''; } - if (tables){ + if (tables) { tasks.push({ kind: 'writeTableDefinition', file: filename, tables }); diff --git a/packages/basic.gblib/services/SystemKeywords.ts b/packages/basic.gblib/services/SystemKeywords.ts index 97c70b89..6d49de3b 100644 --- a/packages/basic.gblib/services/SystemKeywords.ts +++ b/packages/basic.gblib/services/SystemKeywords.ts @@ -30,7 +30,7 @@ | | \*****************************************************************************/ 'use strict'; -import { GBLog, GBMinInstance } from 'botlib'; +import { GBError, GBLog, GBMinInstance } from 'botlib'; import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js'; import { CollectionUtil } from 'pragmatismo-io-framework'; import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js'; @@ -1795,38 +1795,33 @@ export class SystemKeywords { const result = await fetch(url, options); - try { + if (result.status === 2000) { - if (result.status === 2000) { - - // Token expired. + // Token expired. - GBLog.info(`Expired Token for ${url}.`); - await DialogKeywords.setOption({ pid, name: `${proc.executable}-continuationToken`, value: null }); + GBLog.info(`Expired Token for ${url}.`); + await DialogKeywords.setOption({ pid, name: `${proc.executable}-continuationToken`, value: null }); - return null; - } - - let res = JSON.parse(await result.text()); - - if (pageMode === "auto") { - - continuationToken = res.next?.headers['MS-ContinuationToken']; - - if (continuationToken) { - GBLog.info(`Updating continuationToken for ${url}.`); - await DialogKeywords.setOption({ pid, name: 'continuationToken', value: continuationToken }); - } - } - - return res; - - } catch (error) { - - // This is not JSON. - - return result; + return null; } + if (result.status != 200) { + throw new Error(`BASIC: GET ${result.status}: ${result.statusText}.`) + } + + let res = JSON.parse(await result.text()); + + if (pageMode === "auto") { + + continuationToken = res.next?.headers['MS-ContinuationToken']; + + if (continuationToken) { + GBLog.info(`Updating continuationToken for ${url}.`); + await DialogKeywords.setOption({ pid, name: 'continuationToken', value: continuationToken }); + } + } + + return res; + } /** @@ -1860,14 +1855,27 @@ export class SystemKeywords { */ public async postByHttp({ pid, url, data, headers }) { const options = { - json: data, - headers: headers + headers: headers, + method: 'POST' }; - let result = await fetch(url, options); - GBLog.info(`[POST]: ${url} (${data}): ${result}`); + if (typeof (data) === 'object') { + options['json'] = data; + } + else { + options['body'] = data; + } - return result ? (typeof result === 'object' ? result : JSON.parse(result)) : true; + let result = await fetch(url, options); + const text = await result.text(); + GBLog.info(`BASIC: POST ${url} (${data}): ${text}`); + + if (result.status != 200) { + throw new Error(`BASIC: POST ${result.status}: ${result.statusText}.`) + } + + let res = JSON.parse(text); + return res; } public async numberOnly({ pid, text }) { @@ -2101,21 +2109,21 @@ export class SystemKeywords { maxLines = Number.parseInt(params.maxLines).valueOf(); } } - + // Choose data sources based on file type (HTML Table, data variable or sheet file) - + let storage = file.indexOf('.xlsx') !== -1; let results; let header = [], rows = []; const minBoot = GBServer.globals.minBoot; let t; - + if (storage) { t = minBoot.core.sequelize.models[file]; rows = await t.findAll({}); header = rows['dataNames']; } else { - + const botId = min.instance.botId; const path = DialogKeywords.getGBAIPath(botId, 'gbdata'); @@ -2131,15 +2139,15 @@ export class SystemKeywords { results = await client .api( `${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A1:CZ${maxLines}')` - ) + ) .get(); - - header = results.text[0]; - rows = results.text; - } - - // As BASIC uses arrays starting with 1 (one) as index, - // a ghost element is added at 0 (zero) position. + + header = results.text[0]; + rows = results.text; + } + + // As BASIC uses arrays starting with 1 (one) as index, + // a ghost element is added at 0 (zero) position. let table = []; table.push({ gbarray: '0' }); @@ -2212,7 +2220,7 @@ export class SystemKeywords { const address = `${cell}:${cell}`; if (value !== found[columnName]) { - + await this.set({ pid, handle: null, file, address, value }); merges++; } diff --git a/packages/core.gbapp/services/GBMinService.ts b/packages/core.gbapp/services/GBMinService.ts index efd8ad85..2c090a22 100644 --- a/packages/core.gbapp/services/GBMinService.ts +++ b/packages/core.gbapp/services/GBMinService.ts @@ -696,7 +696,7 @@ export class GBMinService { const manager = new NlpManager({ languages: ['pt'], forceNER: true }); min['nerEngine'] = manager; - if (GBServer.globals.minBoot === undefined) { + if (!GBServer.globals.minBoot.botId) { GBServer.globals.minBoot = min; } diff --git a/src/app.ts b/src/app.ts index 19171f45..06b72372 100644 --- a/src/app.ts +++ b/src/app.ts @@ -43,7 +43,7 @@ import http from 'http'; import mkdirp from 'mkdirp'; import Path from 'path'; import * as Fs from 'fs'; -import { GBLog, IGBCoreService, IGBInstance, IGBPackage } from 'botlib'; +import { GBLog, GBMinInstance, IGBCoreService, IGBInstance, IGBPackage } from 'botlib'; import { GBAdminService } from '../packages/admin.gbapp/services/GBAdminService.js'; import { AzureDeployerService } from '../packages/azuredeployer.gbapp/services/AzureDeployerService.js'; import { GBConfigService } from '../packages/core.gbapp/services/GBConfigService.js'; @@ -97,6 +97,7 @@ export class GBServer { GBServer.globals.appPackages = []; GBServer.globals.sysPackages = []; GBServer.globals.minInstances = []; + GBServer.globals.minBoot = new GBMinInstance(); GBServer.globals.wwwroot = null; GBServer.globals.entryPointDialog = null; GBServer.globals.debuggers = []; @@ -217,7 +218,7 @@ export class GBServer { ); instances.push(instance); - GBServer.globals.minBoot + GBServer.globals.minBoot.instance = instances[0]; GBServer.globals.bootInstance = instances[0]; await deployer.deployBotFull(instance, GBServer.globals.publicAddress);