refs #4823 Added function handler and more

This commit is contained in:
Guillermo Bonet 2023-06-16 13:43:24 +02:00
parent 02cd8124ba
commit 14b29400a4
3 changed files with 106 additions and 93 deletions

View File

@ -6,22 +6,22 @@ import fs from 'fs';
const env = process.env; const env = process.env;
const flModels = yml.load(fs.readFileSync('./models/models.yml', 'utf8')); const flModels = yml.load(fs.readFileSync('./models/models.yml', 'utf8'));
let cycle = 1; let cycle = 1, stopSchedule = false;
class Floriday { class Floriday {
async start() { async start() {
try { try {
await utils.checkConfig(); await utils.checkConfig();
await utils.checkToken(); await utils.checkToken();
utils.separator('Init'); await utils.separator('Init');
} catch (err) { } catch (err) {
utils.criticalError(err); utils.handler('critical', err);
} }
}; };
async tryConn() { async tryConn() {
while (true) while (true)
try { try {
utils.warning(new Error('Awaiting a response from the database...')); await utils.handler('warning', new Error('Awaiting a response from the database...'));
await utils.sleep(env.DB_RECON_TIMEOUT); await utils.sleep(env.DB_RECON_TIMEOUT);
await checkCon(); await checkCon();
await this.schedule(); await this.schedule();
@ -34,7 +34,7 @@ class Floriday {
const intervalTime = JSON.parse(env.IS_PRODUCTION) const intervalTime = JSON.parse(env.IS_PRODUCTION)
? env.MS_PRODUCTION_SCHEDULE ? env.MS_PRODUCTION_SCHEDULE
: env.MS_TEST_SCHEDULE; : env.MS_TEST_SCHEDULE;
while (!this.stopSchedule) { while (!stopSchedule) {
try { try {
await this.trunk(); await this.trunk();
await new Promise(resolve => setTimeout(resolve, intervalTime)); await new Promise(resolve => setTimeout(resolve, intervalTime));
@ -50,26 +50,27 @@ class Floriday {
async trunk() { async trunk() {
try{ try{
for (let model of flModels)
await utils.syncModel(model);
await utils.checkConnections(); await utils.checkConnections();
utils.separator(`${cycle++} ${(cycle == 2 ? 'Cycle' : 'Cycles')}`);
} catch (err) {
if (['SequelizeConnectionRefusedError', 'SequelizeConnectionError'].includes(err.name))
throw err;
utils.criticalError(err); for (let model of flModels) {
if (stopSchedule) return;
await utils.syncModel(model);
}
await utils.separator(`${cycle++} ${(cycle == 2 ? 'Cycle' : 'Cycles')}`);
} catch (err) {
if (err?.original?.code === 'ER_SOCKET_UNEXPECTED_CLOSE') throw err;
utils.handler('critical', err);
} }
}; };
async stop() { async stop() {
try { try {
this.stopSchedule = false; stopSchedule = true;
await closeCon(); await closeCon();
console.warn(chalk.dim('\nBye, come back soon 👋')) console.warn(chalk.dim('\nBye, come back soon 👋'))
} catch (err) { } catch (err) {
utils.criticalError(err); utils.handler('critical', err);
} }
}; };
}; };

View File

@ -19,12 +19,12 @@ console.log(chalk.hex('#06c581')(
)) ))
try { try {
utils.startSpin('Creating database connection...'); await utils.startSpin('Creating database connection...');
sequelize = await createConn(); sequelize = await createConn();
await checkCon(); await checkCon();
utils.okSpin(); await utils.okSpin();
} catch (err) { } catch (err) {
utils.criticalSpin(err); await utils.criticalSpin(err);
} }
// Conf Models // Conf Models
@ -161,13 +161,13 @@ try {
}); });
*/ */
} catch (err) { } catch (err) {
utils.criticalError(err); utils.handler('critical', err);
} }
try { try {
const action = JSON.parse(env.FORCE_SYNC) ? { force: true } : { alter: true }; const action = JSON.parse(env.FORCE_SYNC) ? { force: true } : { alter: true };
const actionMsg = JSON.parse(env.FORCE_SYNC) ? 'Forcing' : 'Altering'; const actionMsg = JSON.parse(env.FORCE_SYNC) ? 'Forcing' : 'Altering';
utils.startSpin(`${actionMsg} models...`); await utils.startSpin(`${actionMsg} models...`);
await sequelize.sync(action); await sequelize.sync(action);
/* /*
@ -177,11 +177,10 @@ try {
// Create procedures // Create procedures
sequelize.query(fs.readFileSync('routines/procedures/offerRefresh.sql', 'utf-8')); sequelize.query(fs.readFileSync('routines/procedures/offerRefresh.sql', 'utf-8'));
*/ */
await utils.okSpin();
utils.okSpin();
} }
catch (err) { catch (err) {
utils.criticalSpin(err) await utils.criticalSpin(err)
} }
/** /**
@ -219,13 +218,13 @@ async function checkCon() {
* Close the connection to the database * Close the connection to the database
*/ */
async function closeCon() { async function closeCon() {
utils.failSpin(null, true); await utils.failSpin(null, true);
utils.startSpin('Closing database connection...'); await utils.startSpin('Closing database connection...');
try { try {
await sequelize.close() await sequelize.close()
utils.okSpin(); await utils.okSpin();
} catch (err) { } catch (err) {
utils.criticalSpin(err); await utils.criticalSpin(err);
} }
} }

145
utils.js
View File

@ -10,7 +10,7 @@ import fs from 'fs';
const env = process.env; const env = process.env;
const methods = yml.load(fs.readFileSync('./methods.yml', 'utf8')); const methods = yml.load(fs.readFileSync('./methods.yml', 'utf8'));
const flModels = yml.load(fs.readFileSync('./models/models.yml', 'utf8')); const flModels = yml.load(fs.readFileSync('./models/models.yml', 'utf8'));
const arrow = '└─────'; const arrow = '└───────';
let spinner; let spinner;
/** /**
@ -21,7 +21,7 @@ let spinner;
*/ */
export async function checkToken(isForce) { export async function checkToken(isForce) {
try { try {
startSpin(`Checking token...`); await startSpin(`Checking token...`);
const clientConfigData = await models.config.findOne(); const clientConfigData = await models.config.findOne();
@ -57,9 +57,9 @@ export async function checkToken(isForce) {
}); });
} else } else
optionalMsg = 'Using stored token...'; optionalMsg = 'Using stored token...';
okSpin(optionalMsg); await okSpin(optionalMsg);
} catch (err) { } catch (err) {
failSpin(err); await await failSpin(err);
} }
}; };
@ -76,7 +76,7 @@ export async function getCurrentToken() {
* Check the floriday data config. * Check the floriday data config.
*/ */
export async function checkConfig() { export async function checkConfig() {
startSpin(`Checking config...`); await startSpin(`Checking config...`);
const excludedEnvVars = ['VSCODE_GIT_ASKPASS_EXTRA_ARGS']; const excludedEnvVars = ['VSCODE_GIT_ASKPASS_EXTRA_ARGS'];
const requiredEnvVars = Object.keys(env); const requiredEnvVars = Object.keys(env);
@ -84,13 +84,13 @@ export async function checkConfig() {
for (const reqEnvVar of filteredEnvVars) for (const reqEnvVar of filteredEnvVars)
if (!process.env[reqEnvVar]) if (!process.env[reqEnvVar])
failSpin(new Error(`You haven't provided the ${reqEnvVar} environment variable`)); await failSpin(new Error(`You haven't provided the ${reqEnvVar} environment variable`));
const clientConfigData = await models.config.findOne(); const clientConfigData = await models.config.findOne();
if (!clientConfigData) if (!clientConfigData)
await updateClientConfig(env.CLIENT_ID, env.CLIENT_SECRET); await updateClientConfig(env.CLIENT_ID, env.CLIENT_SECRET);
okSpin(); await okSpin();
}; };
/** /**
@ -125,8 +125,8 @@ export async function updateClientConfig(clientConfig) {
* *
* @param {Integer} ms * @param {Integer} ms
*/ */
export function sleep(ms) { export async function sleep(ms) {
new Promise(resolve => setTimeout(resolve, ms)); await new Promise(resolve => setTimeout(resolve, ms));
}; };
/** /**
@ -135,7 +135,7 @@ export function sleep(ms) {
* @param {String} model Supported models (./models/methods.yml) * @param {String} model Supported models (./models/methods.yml)
*/ */
export async function syncModel(model) { export async function syncModel(model) {
startSpin(`Syncing ${model}...`); await startSpin(`Syncing ${model}...`);
try { try {
const dbSeqNum = await models.sequenceNumber.findOne({ where: { model } }) const dbSeqNum = await models.sequenceNumber.findOne({ where: { model } })
let curSeqNum = dbSeqNum?.maxSequenceNumber ?? 0, i = 0; let curSeqNum = dbSeqNum?.maxSequenceNumber ?? 0, i = 0;
@ -155,21 +155,26 @@ export async function syncModel(model) {
params = params.toString(); params = params.toString();
} }
const res = (await vnRequest('GET', `${env.API_URL}${methods[model].sync.url}${curSeqNum}${params ? `?${params}` : ''}`)).data; const res = (await vnRequest('GET', `${env.API_URL}${methods[model].sync.url}${curSeqNum}${params ? `?${params}` : ''}`));
const data = res.results; if (res?.response?.status == 403 && res?.response?.data?.title === 'There are no connected suppliers.') { // Forbidden
curSeqNum = res.maximumSequenceNumber; await await warnSpin(`Syncing ${model}...`, new Error(res.response.data.title), true);
return;
}
const data = res.data.results;
curSeqNum = res.data.maximumSequenceNumber;
misSeqNum = maxSeqNum - curSeqNum; misSeqNum = maxSeqNum - curSeqNum;
await insertModel(model, data); await insertModel(model, data);
txtSpin(`Syncing ${i = i + data.length} ${model}, ${misSeqNum} missing...`); await txtSpin(`Syncing ${i = i + data.length} ${model}, ${misSeqNum} missing...`);
await insertSequenceNumber(model, curSeqNum); await insertSequenceNumber(model, curSeqNum);
} }
await insertSequenceNumber(model, maxSeqNum); await insertSequenceNumber(model, maxSeqNum);
txtSpin((i) await txtSpin((i)
? `Syncing ${i} ${model}...` ? `Syncing ${i} ${model}...`
: `Syncing ${model}...`); : `Syncing ${model}...`);
okSpin(); await okSpin();
} catch (err) { } catch (err) {
failSpin(err); await await failSpin(err);
} }
}; };
@ -210,12 +215,12 @@ export async function insertModel(model, data) {
*/ */
export async function checkConnections(){ export async function checkConnections(){
try { try {
startSpin('Checking connections...'); await startSpin('Checking connections...');
await createConnections(); await createConnections();
await deleteConnections(); await deleteConnections();
if (spinner) okSpin(); if (spinner) await okSpin();
} catch (err) { } catch (err) {
failSpin(err); await failSpin(err);
} }
}; };
@ -669,13 +674,13 @@ export async function createConnections() {
connectionsToPut.push(value.organizationId); connectionsToPut.push(value.organizationId);
}); });
if (connectionsToPut.length && !spinner) startSpin('Creating connections...'); if (connectionsToPut.length && !spinner) await startSpin('Creating connections...');
for (let connection of connectionsToPut) { for (let connection of connectionsToPut) {
await vnRequest('PUT', `${env.API_URL}${methods.connections.base.url}${connection}`); await vnRequest('PUT', `${env.API_URL}${methods.connections.base.url}${connection}`);
txtSpin(`Creating ${i++} connections, ${connectionsToPut.length - i} missing...`); await txtSpin(`Creating ${i++} connections, ${connectionsToPut.length - i} missing...`);
} }
if (spinner && i) okSpin(`Creating ${i++} connections...`); if (spinner && i) await okSpin(`Creating ${i++} connections...`);
} catch (err) { } catch (err) {
throw err; throw err;
} }
@ -697,16 +702,16 @@ export async function deleteConnections() {
ghostConnections.push(value.organizationId); ghostConnections.push(value.organizationId);
}); });
if (ghostConnections.length && !spinner) startSpin('Deleting connections...'); if (ghostConnections.length && !spinner) await startSpin('Deleting connections...');
for (let connection of ghostConnections) { for (let connection of ghostConnections) {
await vnRequest('DELETE', `${env.API_URL}/connections/${connection}`); await vnRequest('DELETE', `${env.API_URL}/connections/${connection}`);
txtSpin(`Deleting ${i++} connections, ${ghostConnections.length - i} missing...`) await txtSpin(`Deleting ${i++} connections, ${ghostConnections.length - i} missing...`)
} }
if (spinner && i) okSpin(`Deleting ${i++} connections...`); if (spinner && i) await okSpin(`Deleting ${i++} connections...`);
} catch (err) { } catch (err) {
criticalSpin(err); await criticalSpin(err);
} }
}; };
@ -737,43 +742,43 @@ export async function vnRequest(method, url, data, headers) {
switch (err.code) { switch (err.code) {
case 'ECONNRESET': // Client network socket TLS case 'ECONNRESET': // Client network socket TLS
case 'EAI_AGAIN': // getaddrinfo case 'EAI_AGAIN': // getaddrinfo
warnSpin(null, err, false); await await warnSpin(null, err, false);
sleep(1000); await sleep(1000);
break; break;
case 'ECONNABORTED': case 'ECONNABORTED':
case 'ECONNREFUSED': case 'ECONNREFUSED':
case 'ERR_BAD_REQUEST': case 'ERR_BAD_REQUEST':
switch (err.response.status) { switch (err.response.status) {
case 404: // Not found case 404, 403: // Not found and Forbidden
return err; return err;
case 504: case 504:
case 502: case 502:
warnSpin(null, err, false); await warnSpin(null, err, false);
sleep(1000); await sleep(1000);
break; break;
case 429: // Too Many Requests case 429: // Too Many Requests
warnSpin(null, err, false); await warnSpin(null, err, false);
sleep(60000); await sleep(60000);
break; break;
case 401: // Unauthorized case 401: // Unauthorized
warnSpin(null, err, false); await warnSpin(null, err, false);
await checkToken(true); await checkToken(true);
headers.Authorization headers.Authorization
? headers.Authorization = `Bearer ${await getCurrentToken()}` ? headers.Authorization = `Bearer ${await getCurrentToken()}`
: criticalError(err); : handler('critical', err);;
break; break;
default: default:
warnSpin(null, err, false); await warnSpin(null, err, false);
sleep(env.MS_RETRY_UNHANDLED_ERROR); await sleep(env.MS_RETRY_UNHANDLED_ERROR);
break; break;
} }
break; break;
default: default:
warnSpin(null, err, false); await warnSpin(null, err, false);
sleep(env.MS_RETRY_UNHANDLED_ERROR); await sleep(env.MS_RETRY_UNHANDLED_ERROR);
break; break;
} }
startSpin(); await startSpin();
} }
} }
}; };
@ -784,7 +789,7 @@ export async function vnRequest(method, url, data, headers) {
* @param {String} msg Text of spinner * @param {String} msg Text of spinner
* @param {Boolean} isNew Reinstantiate the object * @param {Boolean} isNew Reinstantiate the object
**/ **/
export function startSpin(msg, isKeep) { export async function startSpin(msg, isKeep) {
if (JSON.parse(env.TIME_STAMPS) && msg) if (JSON.parse(env.TIME_STAMPS) && msg)
msg = `${chalk.gray(`[${new moment().format('YYYY-MM-DD hh:mm:ss A')}]`)} ${msg}`; msg = `${chalk.gray(`[${new moment().format('YYYY-MM-DD hh:mm:ss A')}]`)} ${msg}`;
@ -803,7 +808,7 @@ export function startSpin(msg, isKeep) {
* *
* @param {String} msg Text of spinner * @param {String} msg Text of spinner
**/ **/
export function txtSpin(msg) { export async function txtSpin(msg) {
if (JSON.parse(env.TIME_STAMPS) && msg) if (JSON.parse(env.TIME_STAMPS) && msg)
msg = `${chalk.gray(`[${new moment().format('YYYY-MM-DD hh:mm:ss A')}]`)} ${msg}`; msg = `${chalk.gray(`[${new moment().format('YYYY-MM-DD hh:mm:ss A')}]`)} ${msg}`;
@ -815,7 +820,7 @@ export function txtSpin(msg) {
* *
* @param {String} msg Text of spinner * @param {String} msg Text of spinner
**/ **/
export function okSpin(msg) { export async function okSpin(msg) {
if (JSON.parse(env.TIME_STAMPS) && msg) if (JSON.parse(env.TIME_STAMPS) && msg)
msg = `${chalk.gray(`[${new moment().format('YYYY-MM-DD hh:mm:ss A')}]`)} ${msg ?? ''}`; msg = `${chalk.gray(`[${new moment().format('YYYY-MM-DD hh:mm:ss A')}]`)} ${msg ?? ''}`;
@ -832,7 +837,7 @@ export function okSpin(msg) {
* @param {Error} err Error object * @param {Error} err Error object
* @param {Boolean} clear Clean the instance * @param {Boolean} clear Clean the instance
**/ **/
export function warnSpin(msg, err, clear) { export async function warnSpin(msg, err, clear) {
if (JSON.parse(env.TIME_STAMPS) && msg) if (JSON.parse(env.TIME_STAMPS) && msg)
msg = `${chalk.gray(`[${new moment().format('YYYY-MM-DD hh:mm:ss A')}]`)} ${msg}`; msg = `${chalk.gray(`[${new moment().format('YYYY-MM-DD hh:mm:ss A')}]`)} ${msg}`;
@ -840,7 +845,7 @@ export function warnSpin(msg, err, clear) {
spinner.warn(msg); spinner.warn(msg);
if (clear) spinner = null; if (clear) spinner = null;
} }
if (err) warning(err); if (err) await handler('warning', err);
}; };
/** /**
@ -848,7 +853,7 @@ export function warnSpin(msg, err, clear) {
* *
* @param {Error} err Error object * @param {Error} err Error object
**/ **/
export function failSpin(err) { export async function failSpin(err) {
if (spinner) { if (spinner) {
spinner.fail(); spinner.fail();
spinner = null; spinner = null;
@ -861,12 +866,12 @@ export function failSpin(err) {
* *
* @param {Error} err Error object * @param {Error} err Error object
**/ **/
export function criticalSpin(err) { export async function criticalSpin(err) {
if (spinner) { if (spinner) {
spinner.fail(); spinner.fail();
spinner = null; spinner = null;
} }
criticalError(err); handler('critical', err);
}; };
/** /**
@ -874,28 +879,36 @@ export function criticalSpin(err) {
* *
* @param {String} msg String to show * @param {String} msg String to show
**/ **/
export function separator(msg) { export async function separator(msg) {
console.log(chalk.gray(` ──────────────────────── ${msg}`)); console.log(chalk.gray(` ──────────────────────── ${msg}`));
}; };
/** /**
* Critical error. * Function to handle error messages.
* *
* @param {String} type Type name
* @param {Error} err Error object * @param {Error} err Error object
**/ **/
export function criticalError(err) { export async function handler(type, err) {
const msg = `${chalk.red.bold(arrow)} ${chalk.red.bold('[CRITICAL]')}`; let header = (`${arrow} [${type.toUpperCase()}]`);
console.log(`${msg} ${chalk.red(err.message)}`); let msg = (err.response?.status && err.response?.data?.message)
process.exit(); ? `${err.response.status} - ${err.response?.data?.message}`
}; : err.message;
/** switch (type) {
* Warning. case 'critical':
* header = chalk.red.bold(header);
* @param {Error} err msg = chalk.red(msg);
**/ break;
export function warning(err) { case 'warning':
const msg = `${chalk.yellow.bold(arrow)} ${chalk.yellow.bold('[WARNING]')}`; header = chalk.yellow.bold(header);
console.log(`${msg} ${chalk.yellow((err.response?.status && err.response?.data?.message) ?? err.message)}`); msg = chalk.yellow(msg);
break;
default:
console.error('Unhandled handler type');
process.exit();
};
console.log(header, msg);
if (type === 'critical') process.exit();
}; };