fix(basic.gblib): Databases. #392 @othonlima.

This commit is contained in:
Rodrigo Rodriguez 2023-11-29 18:46:02 -03:00
parent d8de3b8778
commit 5dd2cc3a29
5 changed files with 154 additions and 102 deletions

View file

@ -125,6 +125,7 @@
"lodash": "4.17.21", "lodash": "4.17.21",
"luxon": "3.1.0", "luxon": "3.1.0",
"mammoth": "1.5.1", "mammoth": "1.5.1",
"mariadb": "3.2.2",
"mime-types": "2.1.35", "mime-types": "2.1.35",
"moment": "1.3.0", "moment": "1.3.0",
"ms-rest-azure": "3.0.0", "ms-rest-azure": "3.0.0",

View file

@ -32,7 +32,7 @@
'use strict'; 'use strict';
import { GBMinInstance, GBService, IGBCoreService, GBDialogStep, GBLog } from 'botlib'; import { GBMinInstance, GBService, IGBCoreService, GBDialogStep, GBLog, GBError } from 'botlib';
import * as Fs from 'fs'; import * as Fs from 'fs';
import { GBServer } from '../../../src/app.js'; import { GBServer } from '../../../src/app.js';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js'; import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js';
@ -205,7 +205,33 @@ export class GBVMService extends GBService {
const tableDef = JSON.parse(Fs.readFileSync(tablesFile, 'utf8')) as any; const tableDef = JSON.parse(Fs.readFileSync(tablesFile, 'utf8')) as any;
const getTypeBasedOnCondition = (t) => { const getTypeBasedOnCondition = (t, size) => {
if (1) {
switch (t) {
case 'string':
return `varchar(${size})`;
case 'guid':
return 'UUID';
case 'key':
return `varchar(${size})`;
case 'number':
return 'BIGINT';
case 'integer':
return 'INTEGER';
case 'double':
return 'FLOAT';
case 'float':
return 'FLOAT';
case 'date':
return 'DATE';
case 'boolean':
return 'BOOLEAN';
default:
return { type: 'TABLE', name: t };
}
} else {
switch (t) { switch (t) {
case 'string': case 'string':
return { key: 'STRING' }; return { key: 'STRING' };
@ -228,33 +254,30 @@ export class GBVMService extends GBService {
default: default:
return { key: 'TABLE', name: t }; return { key: 'TABLE', name: t };
} }
}
}; };
const associations = []; const associations = [];
const tables = await min.core.sequelize.query(`SELECT table_name, table_schema
FROM information_schema.tables
WHERE table_type = 'BASE TABLE'
ORDER BY table_name ASC`, {
type: QueryTypes.RAW
})
// Loads storage custom connections. // Loads storage custom connections.
const path = DialogKeywords.getGBAIPath(min.botId, null); const path = DialogKeywords.getGBAIPath(min.botId, null);
const filePath = Path.join('work', path, 'connections.json'); const filePath = Path.join('work', path, 'connections.json');
let connections = null; let connections = null;
if(Fs.existsSync(filePath)){ if (Fs.existsSync(filePath)) {
connections = Fs.readFileSync(filePath, 'utf8'); connections = JSON.parse(Fs.readFileSync(filePath, 'utf8'));
} }
tableDef.forEach(t => { tableDef.forEach(async t => {
const tableName = t.name;
const tableName = t.name.trim();
// Determines autorelationship. // Determines autorelationship.
Object.keys(t.fields).forEach(key => { Object.keys(t.fields).forEach(key => {
let obj = t.fields[key]; let obj = t.fields[key];
obj.type = getTypeBasedOnCondition(obj.type); obj.type = getTypeBasedOnCondition(obj.type, obj.size);
if (obj.type.key === "TABLE") { if (obj.type.key === "TABLE") {
obj.type.key = "BIGINT" obj.type.key = "BIGINT"
associations.push({ from: tableName, to: obj.type.name }); associations.push({ from: tableName, to: obj.type.name });
@ -267,16 +290,17 @@ export class GBVMService extends GBService {
// Cutom connection for TABLE. // Cutom connection for TABLE.
const connectionName = t.connection; const connectionName = t.connection;
if (connectionName) { let con;
const con = connections[connectionName]; if (connectionName && connections) {
con = connections.filter(p => p.name === connectionName)[0];
const dialect = con['storageDialect']; const dialect = con['storageDriver'];
const host = con['storageHost']; const host = con['storageServer'];
const port = con['storagePort']; const port = con['storagePort'];
const storageName = con['storageName']; const storageName = con['storageName'];
const username = con['storageUsername']; const username = con['storageUsername'];
const password = con['password']; const password = con['storagePassword'];
const logging: boolean | Function = const logging: boolean | Function =
GBConfigService.get('STORAGE_LOGGING') === 'true' GBConfigService.get('STORAGE_LOGGING') === 'true'
@ -312,14 +336,25 @@ export class GBVMService extends GBService {
} }
}; };
if (!min[connectionName]) {
GBLogEx.info(min, `Loading custom connection ${connectionName}...`);
min[connectionName] = new Sequelize(storageName, username, password, sequelizeOptions); min[connectionName] = new Sequelize(storageName, username, password, sequelizeOptions);
} }
}
if (!con) {
throw new Error(`Invalid connection specified: ${connectionName}.`);
}
// Field checking, syncs if there is any difference. // Field checking, syncs if there is any difference.
const model = min[connectionName] ? min[connectionName].models[tableName] : minBoot.core.sequelize; const seq = min[connectionName] ? min[connectionName]
if (model) { : minBoot.core.sequelize;
if (seq) {
const model = seq.models[tableName];
if (model) {
// Except Id, checks if has same number of fields. // Except Id, checks if has same number of fields.
let equals = 0; let equals = 0;
@ -340,12 +375,25 @@ export class GBVMService extends GBService {
} }
} }
minBoot.core.sequelize.define(tableName, t.fields); seq.define(tableName, t.fields);
// New table checking, if needs sync. // New table checking, if needs sync.
let tables;
if (con.storageDriver === 'mssql') {
tables = await seq.query(`SELECT table_name, table_schema
FROM information_schema.tables
WHERE table_type = 'BASE TABLE'
ORDER BY table_name ASC`, {
type: QueryTypes.RAW
})[0]
}
else if (con.storageDriver === 'mariadb') {
tables = await seq.getQueryInterface().showAllTables();
}
let found = false; let found = false;
tables[0].forEach((storageTable) => { tables.forEach((storageTable) => {
if (storageTable['table_name'] === tableName) { if (storageTable['table_name'] === tableName) {
found = true; found = true;
} }
@ -353,11 +401,10 @@ export class GBVMService extends GBService {
sync = sync ? sync : !found; sync = sync ? sync : !found;
});
associations.forEach(e => { associations.forEach(e => {
const from = minBoot.core.sequelize.models[e.from]; const from = seq.models[e.from];
const to = minBoot.core.sequelize.models[e.to]; const to = seq.models[e.to];
try { try {
to.hasMany(from); to.hasMany(from);
@ -368,18 +415,20 @@ export class GBVMService extends GBService {
}); });
if (sync) { if (sync) {
GBLogEx.info(min, `BASIC: Syncing changes for TABLE keywords (${min.botId})...`); GBLogEx.info(min, `BASIC: Syncing changes for TABLE ${connectionName} ${tableName} keyword (${min.botId})...`);
await minBoot.core.sequelize.sync({ await seq.sync({
alter: true, alter: true,
force: false // Keep it false due to data loss danger. force: false // Keep it false due to data loss danger.
}); });
GBLogEx.info(min, `BASIC: Done sync for ${min.botId} storage tables...`); GBLogEx.info(min, `BASIC: Done sync for ${min.botId} ${connectionName} ${tableName} storage table...`);
} }
else { else {
GBLogEx.verbose(min, `BASIC: TABLE keywords already up to date (${min.botId})...`); GBLogEx.verbose(min, `BASIC: TABLE ${tableName} keywords already up to date ${connectionName} (${min.botId})...`);
} }
} }
});
}
const parsedCode: string = Fs.readFileSync(jsfile, 'utf8'); const parsedCode: string = Fs.readFileSync(jsfile, 'utf8');
min.sandBoxMap[mainName.toLowerCase().trim()] = parsedCode; min.sandBoxMap[mainName.toLowerCase().trim()] = parsedCode;
@ -721,7 +770,7 @@ export class GBVMService extends GBService {
if (endTableReg && table) { if (endTableReg && table) {
tables.push({ tables.push({
name: table, fields: fields, connection:connection name: table, fields: fields, connection: connection
}); });
@ -743,7 +792,7 @@ export class GBVMService extends GBService {
let tableReg = tableKeyword.exec(line); let tableReg = tableKeyword.exec(line);
if (tableReg && !table) { if (tableReg && !table) {
table = tableReg[1]; table = tableReg[1];
connection= tableReg[2]; connection = tableReg[2];
emmit = false; emmit = false;
} }

View file

@ -675,7 +675,8 @@ ENDPOINT_UPDATE=true
public getParam<T>(instance: IGBInstance, name: string, defaultValue?: T): any { public getParam<T>(instance: IGBInstance, name: string, defaultValue?: T): any {
let value = null; let value = null;
if (instance.params) { if (instance.params) {
const params = JSON.parse(instance.params);
const params = typeof (instance.params) === 'object' ? instance.params: JSON.parse(instance.params);
value = params ? params[name] : defaultValue; value = params ? params[name] : defaultValue;
} }
if (typeof defaultValue === 'boolean') { if (typeof defaultValue === 'boolean') {
@ -709,7 +710,7 @@ ENDPOINT_UPDATE=true
let params = null; let params = null;
const list = []; const list = [];
if (instance.params) { if (instance.params) {
params = JSON.parse(instance.params); params = typeof (instance.params) === 'object' ? instance.params : JSON.parse(instance.params);
} }
Object.keys(params).forEach(e => { Object.keys(params).forEach(e => {

View file

@ -619,10 +619,11 @@ export class GBDeployer implements IGBDeployer {
// Find all tokens in .gbot Config. // Find all tokens in .gbot Config.
const strFind = ' Driver'; const strFind = ' Driver';
const tokens = await min.core['findParam'](min.instance, strFind); const conns = await min.core['findParam'](min.instance, strFind);
await CollectionUtil.asyncForEach(tokens,async t => { await CollectionUtil.asyncForEach(conns, async t => {
const connectionName = t.replace(strFind, ''); const connectionName = t.replace(strFind, '');
let con = {}; let con = {};
con['name'] = connectionName;
con['storageServer']= min.core.getParam<string>(min.instance, `${connectionName} Server`, null), con['storageServer']= min.core.getParam<string>(min.instance, `${connectionName} Server`, null),
con['storageName']= min.core.getParam<string>(min.instance, `${connectionName} Name`, null), con['storageName']= min.core.getParam<string>(min.instance, `${connectionName} Name`, null),
con['storageUsername']= min.core.getParam<string>(min.instance, `${connectionName} Username`, null), con['storageUsername']= min.core.getParam<string>(min.instance, `${connectionName} Username`, null),

View file

@ -111,7 +111,7 @@ export class GBServer {
process.on('unhandledRejection', (err, p) => { process.on('unhandledRejection', (err, p) => {
err = err['e'] ? err['e'] : err; err = err['e'] ? err['e'] : err;
GBLog.error(`UNHANDLED_REJECTION(promises): ${err.toString()} ${err['stack'] ? '\n' + err['stack'] : ''}`); GBLog.error(`UNHANDLED_REJECTION(promises): ${err.toString()} ${err['stack'] ? '\n' + err['stack'] : ''} ${err['cause'] ? '\n' + err['cause']?.message : ''}`);
if (err['response']?.obj?.httpStatusCode === 404) { if (err['response']?.obj?.httpStatusCode === 404) {
GBLog.warn(`Check reverse proxy: ${process.env.BOT_URL} as it seems to be invalid.`); GBLog.warn(`Check reverse proxy: ${process.env.BOT_URL} as it seems to be invalid.`);
} }