new(basic.gblib): TABLE keyword #375 fixes.

This commit is contained in:
Rodrigo Rodriguez 2023-10-05 10:06:03 -03:00
parent 90e0688cd6
commit 7163c077fe
6 changed files with 199 additions and 149 deletions

View file

@ -644,6 +644,19 @@ export class DialogKeywords {
await DialogKeywords.setOption({ pid, name, value });
}
/**
* Returns current if any continuation token for paginated HTTP requests.
*
* @example CONTINUATION TOKEN
*
*/
public async getContinuationToken({ pid }) {
let { min, user, params, proc } = await DialogKeywords.getProcessInfo(pid);
return DialogKeywords.getOption({ pid, name: `${proc.executable}-continuationToken` });
}
/**
* Returns bot param persisted on storage.
*

View file

@ -171,19 +171,42 @@ export class GBVMService extends GBService {
} else {
await this.translateBASIC(mainName, fullFilename, min);
}
// Syncronizes Database Objects with the ones returned from "Word".
const tablesFile = urlJoin(folder, 'tables.json');
const tablesFile = urlJoin(folder, `${filename}.tables.json`);
if (Fs.existsSync(tablesFile)) {
const minBoot = GBServer.globals.minBoot;
GBLogEx.info(min, `BASIC: Reading tables and sync storage for ${min.botId}...`);
const tables = JSON.parse(Fs.readFileSync(tablesFile, 'utf8'));
tables.forEach(t => {
minBoot.core.sequelize.define(t.name, t.fields);
const t = JSON.parse(Fs.readFileSync(tablesFile, 'utf8'));
const getTypeBasedOnCondition = (t) => {
switch (t) {
case 'string':
return { key: 'STRING' };
case 'key':
return { key: 'STRING' }; // Assuming key is a string data type
case 'integer':
return { key: 'INTEGER' };
case 'double':
return { key: 'FLOAT' };
case 'date':
return { key: 'DATE' };
case 'boolean':
return { key: 'BOOLEAN' };
default:
return { key: 'STRING' }; // Default to string if the type is unknown
}
};
Object.keys(t.fields).forEach(key => {
let obj = t.fields[key];
obj.type = getTypeBasedOnCondition(obj.type);
});
minBoot.core.sequelize.define(t.name, t.fields);
await minBoot.core.sequelize.sync();
}
@ -194,6 +217,7 @@ export class GBVMService extends GBService {
}
public async translateBASIC(mainName, filename: any, min: GBMinInstance) {
// Converts General Bots BASIC into regular VBS
let basicCode: string = Fs.readFileSync(filename, 'utf8');
@ -218,7 +242,7 @@ export class GBVMService extends GBService {
}
} while (include);
let { code, map, metadata, tasks } = await this.convert(mainName, basicCode);
let { code, map, metadata, tasks } = await this.convert(filename, mainName, basicCode);
// Generates function JSON metadata to be used later.
@ -231,7 +255,7 @@ export class GBVMService extends GBService {
// Execute off-line code tasks
await this.executeTasks(tasks);
await this.executeTasks(min, tasks);
// Run JS into the GB context.
@ -310,6 +334,15 @@ export class GBVMService extends GBService {
const hour = (v) => { return (async () => { return await dk.getHourFromDate({v}) })(); };
const base64 = (v) => { return (async () => { return await dk.getCoded({v}) })(); };
const tolist = (v) => { return (async () => { return await dk.getToLst({v}) })(); };
const uuid = () => {
var dt = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
var r = (dt + Math.random()*16)%16 | 0;
dt = Math.floor(dt/16);
return (c=='x' ? r :(r&0x3|0x8)).toString(16);
});
return uuid;
};
// Setups interprocess communication from .gbdialog run-time to the BotServer API.
@ -338,29 +371,20 @@ export class GBVMService extends GBService {
}
private async executeTasks(tasks) {
private async executeTasks(min, tasks) {
for (let i = 0; i < tasks.length; i++) {
const task = tasks[i];
if (task.kind === 'writeTableDefinition') {
// Creates an empty object that will receive Sequelize fields.
let obj = {name: task.name};
obj['fields'] = [];
const fields = task.fields;
fields.forEach(f => {
let obj = { name: task.name };
obj['fields'] = task.fields;
const path = DialogKeywords.getGBAIPath(min.botId, `gbdialog`);
const tablesFile = `${task.file}.tables.json`;
let field = {};
field[f.name] = f;
obj['fields'].push(field);
});
const jsonFile = `${task.name}.table.json`;
Fs.writeFileSync(jsonFile, JSON.stringify(obj));
Fs.writeFileSync(tablesFile, JSON.stringify(obj));
}
@ -454,42 +478,20 @@ export class GBVMService extends GBService {
let required = line.indexOf('*') !== -1;
line = line.replace('*', '');
const fieldRegExp = /^\s*(\w+)\s*(\w+)(\((\d+)\))?/gim;
const fieldRegExp = /^\s*(\w+)\s*(\w+)(?:\((\d+)\))?/gim;
let reg = fieldRegExp.exec(line);
const t = reg[2];
const n = reg[1];
let obj = {};
let field = {allowNull: !required };
obj[n] = field;
const name = reg[1];
const getTypeBasedOnCondition = (t) => {
switch (t) {
case 'string':
return DataTypes.STRING;
case 'key':
return DataTypes.STRING; // Assuming key is a string data type
case 'integer':
return DataTypes.INTEGER;
case 'double':
return DataTypes.FLOAT;
case 'date':
return DataTypes.DATE;
case 'boolean':
return DataTypes.BOOLEAN;
default:
return DataTypes.STRING; // Default to string if the type is unknown
}
};
field['type'] = getTypeBasedOnCondition(t);
if (reg[3]){
field['size'] = getTypeBasedOnCondition(t);
let definition = { allowNull: !required };
definition['type'] = t;
if (reg[3]) {
definition['size'] = Number.parseInt(reg[3]);
}
return obj;
return { name, definition };
}
/**
@ -498,7 +500,7 @@ export class GBVMService extends GBService {
*
* @param code General Bots BASIC
*/
public async convert(mainName: string, code: string) {
public async convert(filename: string, mainName: string, code: string) {
// Start and End of VB2TS tags of processing.
@ -511,9 +513,10 @@ export class GBVMService extends GBService {
let description;
let table = null; // Used for TABLE keyword.
const tasks = [];
let fields = [];
let fields = {};
for (let i = 1; i <= lines.length; i++) {
let line = lines[i - 1];
// Remove lines before statments.
@ -541,27 +544,12 @@ export class GBVMService extends GBService {
emmit = false;
}
// Inside BEGIN/END table pair containing FIELDS.
if (table) {
const field = this.parseField(line);
fields.push(field);
}
const tableKeyword = /^\s*TABLE\s*\"(.*)\"/gim;
let tableReg = tableKeyword.exec(line);
if (tableReg) {
table = tableReg[1];
emmit = false;
}
const endTableKeyword = /^\s*END TABLE"/gim;
const endTableKeyword = /^\s*END TABLE\s*/gim;
let endTableReg = endTableKeyword.exec(line);
if (endTableReg) {
if (endTableReg && table) {
tasks.push({
kind: 'writeTableDefinition', name: table, fields: fields
kind: 'writeTableDefinition', file: filename, name: table, fields: fields
});
fields = [];
@ -569,16 +557,27 @@ export class GBVMService extends GBService {
emmit = false;
}
// Inside BEGIN/END table pair containing FIELDS.
if (emmit) {
// Add additional lines returned from replacement.
let add = line.split(/\r\n|\r|\n/).length;
current = current + (add ? add : 0);
map[i] = current;
lines[i - 1] = line;
if (table && line.trim() !== '') {
const field = await this.parseField(line);
fields[field.name] = field.definition;
emmit = false;
}
const tableKeyword = /^\s*TABLE\s*(.*)/gim;
let tableReg = tableKeyword.exec(line);
if (tableReg && !table) {
table = tableReg[1];
emmit = false;
}
// Add additional lines returned from replacement.
let add = emmit ? line.split(/\r\n|\r|\n/).length : 0;
current = current + (add ? add : 0);
map[i] = current;
lines[i - 1] = emmit ? line : '';
}
code = `${lines.join('\n')}\n`;
@ -617,7 +616,7 @@ export class GBVMService extends GBService {
variables['aadToken'] = await (min.adminService as any)['acquireElevatedToken'](min.instance.instanceId, false);
// Adds all .gbot params as variables.
const gbotConfig = JSON.parse(min.instance.params);
let keys = Object.keys(gbotConfig);
for (let j = 0; j < keys.length; j++) {
@ -650,7 +649,7 @@ export class GBVMService extends GBService {
let code = min.sandBoxMap[text];
const channel = step ? step.context.activity.channelId : 'web';
const pid = GBVMService.createProcessInfo(user, min, channel);
const pid = GBVMService.createProcessInfo(user, min, channel, text);
const dk = new DialogKeywords();
const sys = new SystemKeywords();
await dk.setFilter({ pid: pid, value: null });
@ -723,14 +722,15 @@ export class GBVMService extends GBService {
return result;
}
public static createProcessInfo(user: GuaribasUser, min: GBMinInstance, channel: any) {
public static createProcessInfo(user: GuaribasUser, min: GBMinInstance, channel: any, executable: string) {
const pid = GBAdminService.getNumberIdentifier();
GBServer.globals.processes[pid] = {
pid: pid,
userId: user ? user.userId : 0,
instanceId: min.instance.instanceId,
channel: channel,
roles: 'everyone'
roles: 'everyone',
executable: executable
};
return pid;
}

View file

@ -93,6 +93,7 @@ export class KeywordsExpressions {
const convertConditions = input => {
var result = input.replace(/ +and +/gim, ' && ');
result = result.replace(/ +or +/gim, ' || ');
result = result.replace(/ +not +/gim, ' !');
result = result.replace(/ +<> +/gim, ' !== ');
result = result.replace(/ += +/gim, ' === ');
return result;
@ -319,8 +320,14 @@ export class KeywordsExpressions {
return `${$1} = await dk.getConfig ({pid: pid, name: ${$2}})`;
}
];
keywords[i++] = [
/\s*CONTINUATION TOKEN\s*/gim,
() => {
return `await dk.getContinuationToken ({pid: pid})`;
}
];
keywords[i++] = [
/^\s*(set hear on)(\s*)(.*)/gim,
($0, $1, $2, $3) => {
@ -340,14 +347,15 @@ export class KeywordsExpressions {
__next = true;
__calls = 0;
__index = 0;
__data = ${$2};
__url = $2.links?.next?.uri;
__seekToken = $2.links?.self?.headers["MS-ContinuationToken"]
__totalCount = $2["totalCount"];
__url = __data.links?.next?.uri;
__seekToken = __data.links?.self?.headers["MS-ContinuationToken"]
__totalCount = __data["totalCount"];
while (__next)
{
let $1 = $2[__index];
let ${$1} = __data[__index];
`;
}
];
@ -357,37 +365,37 @@ export class KeywordsExpressions {
($0, $1, $2) => {
return `
// TRUE if all items are processed.
if (__index >= __totalCount) {
// Check if HTTP call limit has reached.
if (__calls < __totalCalls) {
// Perform GET request using the constructed URL
$2 = await sys.get ({pid: pid, file: __url, addressOrHeaders: headers, httpUsername, httpPs});
// Update current variable handlers.
// TRUE if all items are processed.
if (__index >= __totalCount) {
// Check if HTTP call limit has reached.
if (__calls < __totalCalls) {
// Perform GET request using the constructed URL
__data = await sys.get ({pid: pid, file: __url, addressOrHeaders: headers, httpUsername, httpPs});
// Update current variable handlers.
__url = __data.links?.next?.uri;
__seekToken = __data.links?.self?.headers["MS-ContinuationToken"]
__totalCount = __data["totalCount"];
index = 0;
__calls++;
} else {
next = false;
}
__url = $2.links?.next?.uri;
__seekToken = $2.links?.self?.headers["MS-ContinuationToken"]
__totalCount = $2["totalCount"];
index = 0;
__calls++;
} else {
next = false;
}
index = index + 1;
}
`;
index = index + 1;
}
}`;
}
];
@ -690,7 +698,7 @@ export class KeywordsExpressions {
keywords[i++] = [
/^\s*(set page mode)(\s*)(.*)/gim,
($0, $1, $2, $3) => {
return `await dk.setPageMode ({pid: pid, ${$3}})`;
return `await dk.setPageMode ({pid: pid, value: ${$3}})`;
}
];
@ -704,7 +712,7 @@ export class KeywordsExpressions {
keywords[i++] = [
/^\s*set header\s*(.*)\s*as\s*(.*)/gim,
($0, $1, $2) => {
return `headers[${$1}]=${$2})`;
return `headers[${$1.trim()}] = ${$2}`;
}
];
@ -845,14 +853,14 @@ export class KeywordsExpressions {
];
keywords[i++] = [
/^\s*(\bexit\b)\s*/gim,
/^\sEXIT\s*$/gim,
() => {
return `return;`;
}
];
keywords[i++] = [
/^\s*(\bEND\b)\s*/gim,
/^\s*END\s*$/gim,
() => {
return `return;`;
}
@ -1072,10 +1080,29 @@ export class KeywordsExpressions {
($0, $1, $2, $3, $4) => {
$3 = $3.replace(/\'/g, '');
$3 = $3.replace(/\"/g, '');
$4 = $4.substr(2);
const fields = $4.split(',');
let fields = $3.split(',');
const table = fields[0].trim();
fields.shift();
return `await sys.saveToStorage({pid: pid, file: "${$3}", args: [${$4}]}, fields)`;
const fieldsAsText = fields.join(',');
let fieldsNamesOnly = [];
let index = 0;
fields.forEach(field => {
// Extracts only the last part of the variable like 'column'
// from 'row.column'.
const fieldRegExp = /(?:.*\.)(.*)/gim;
let name = fieldRegExp.exec(field)[1]
fieldsNamesOnly.push (name);
});
let fieldsNames = fieldsNamesOnly.join(',');
return `await sys.saveToStorage({pid: pid, table: "${table}", fields: [${fieldsAsText}], fieldsNames: [${fieldsNames}] })`;
}
];

View file

@ -656,24 +656,18 @@ export class SystemKeywords {
*/
public async saveToStorage({ pid, table, fields, fieldsNames }) {
const fieldRegExp = /(?:.*\.)(.*)/gim;
GBLog.info(`BASIC: Saving '${table}' (SAVE). Values: ${fields.join(',')}.`);
const minBoot = GBServer.globals.minBoot as any;
const definition = minBoot.core.sequelize.models[table];
let data = {};
let index = 0;
fields.forEach(field => {
// Extracts only the last part of the variable like 'column'
// from 'row.column'.
let name = fieldsNames[index];
name = fieldRegExp.exec(name)[2];
data[name] = field;
fieldsNames.forEach(field => {
data[fieldsNames] = fields[index++];
});
return await definition.create(data);
}
@ -1509,7 +1503,7 @@ export class SystemKeywords {
public generatePassword(pid) {
return GBAdminService.getRndPassword();
}
static aa;
/**
* Calls any REST API by using GET HTTP method.
*
@ -1519,11 +1513,12 @@ export class SystemKeywords {
public async getByHttp({ pid, url, headers, username, ps, qs }) {
let options = {};
const { min, user, params } = await DialogKeywords.getProcessInfo(pid);
const { min, user, params, proc } = await DialogKeywords.getProcessInfo(pid);
GBLogEx.info(min, `GET: ${url}`);
const pageMode = await DialogKeywords.getOption({ pid, name: 'pageMode' });
let continuationToken = await DialogKeywords.getOption({ pid, name: 'continuationToken' });
let continuationToken = await
DialogKeywords.getOption({ pid, name: `${proc.executable}-continuationToken` });
if (pageMode === "auto" && continuationToken) {
headers = headers ? headers : {};
@ -1775,7 +1770,17 @@ export class SystemKeywords {
}
};
const result = await fetch(url, options);
let result;
if (!SystemKeywords.aa){
SystemKeywords.aa = 1;
return r1;
} else {
SystemKeywords.aa = null;
return r2;
}
//const result = await fetch(url, options);
try {
@ -1783,7 +1788,7 @@ export class SystemKeywords {
// Token expired.
GBLog.info(`Expired Token for ${url}.`);
await DialogKeywords.setOption({ pid, name: 'continuationToken', value: null });
await DialogKeywords.setOption({ pid, name: `${proc.executable}-continuationToken`, value: null });
return null;
}

View file

@ -76,11 +76,14 @@ export class GBLogEx {
* Finds and update user agent information to a next available person.
*/
public static async log(instance: IGBInstance, kind: string, message: string): Promise<GuaribasLog> {
message = message ? message.substring(0, 1023) : null;
return await GuaribasLog.create(<GuaribasLog>{
instanceId: instance ? instance.instanceId : 1,
message: message,
kind: kind
});
if (process.env.LOG_ON_STORAGE) {
message = message ? message.substring(0, 1023) : null;
return await GuaribasLog.create(<GuaribasLog>{
instanceId: instance ? instance.instanceId : 1,
message: message,
kind: kind
});
}
}
}

View file

@ -1031,8 +1031,10 @@ export class GBMinService {
return;
}
} else if (context.activity.type === 'message') {
// Processes messages activities.
const pid = GBVMService.createProcessInfo(user, min, step.context.activity.channelId);
const pid = GBVMService.createProcessInfo(user, min, step.context.activity.channelId, null);
step.context.activity['pid'] = pid;
await this.processMessageActivity(context, min, step, pid);