fix(basic.gblib): Allow TABLE keyword multiple times per file #383.

This commit is contained in:
Rodrigo Rodriguez 2023-10-20 13:39:34 -03:00
parent cb311832a4
commit 6e1c01e6fe
4 changed files with 127 additions and 62 deletions

View file

@ -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
});

View file

@ -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++;
}

View file

@ -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;
}

View file

@ -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);