2023-05-09 12:13:19 +00:00
|
|
|
import { models } from './models/sequelize.js';
|
2023-02-08 11:31:54 +00:00
|
|
|
import { v4 as uuidv4 } from 'uuid';
|
2023-05-11 06:39:02 +00:00
|
|
|
import axios from 'axios';
|
|
|
|
import moment from 'moment';
|
2023-04-05 12:46:25 +00:00
|
|
|
import chalk from 'chalk';
|
2023-05-11 06:39:02 +00:00
|
|
|
import ora from 'ora';
|
2023-06-15 09:29:19 +00:00
|
|
|
import yml from 'js-yaml';
|
|
|
|
import fs from 'fs';
|
2023-05-11 06:39:02 +00:00
|
|
|
|
2023-04-25 06:09:52 +00:00
|
|
|
const env = process.env;
|
2023-06-15 09:29:19 +00:00
|
|
|
const methods = yml.load(fs.readFileSync('./methods.yml', 'utf8'));
|
|
|
|
const flModels = yml.load(fs.readFileSync('./models/models.yml', 'utf8'));
|
2023-06-16 11:43:24 +00:00
|
|
|
const arrow = '└───────';
|
2023-06-13 10:51:59 +00:00
|
|
|
let spinner;
|
2023-01-11 13:37:46 +00:00
|
|
|
|
2023-01-09 12:35:05 +00:00
|
|
|
/**
|
2023-06-15 10:50:58 +00:00
|
|
|
* Check if the token is valid, and if it
|
|
|
|
* is not, get a new one.
|
2023-05-19 11:47:36 +00:00
|
|
|
*
|
2023-05-15 12:19:43 +00:00
|
|
|
* @param {Boolean} isForce Force to request new token
|
2023-01-09 12:35:05 +00:00
|
|
|
*/
|
2023-06-15 10:50:58 +00:00
|
|
|
export async function checkToken(isForce) {
|
2023-05-05 09:48:44 +00:00
|
|
|
try {
|
2023-06-16 11:43:24 +00:00
|
|
|
await startSpin(`Checking token...`);
|
2023-06-15 10:50:58 +00:00
|
|
|
|
2023-05-30 09:46:46 +00:00
|
|
|
const clientConfigData = await models.config.findOne();
|
2023-05-05 09:48:44 +00:00
|
|
|
|
2023-06-15 10:50:58 +00:00
|
|
|
let tokenExpiration, token, optionalMsg;
|
2023-05-11 07:43:27 +00:00
|
|
|
if (clientConfigData) {
|
|
|
|
token = clientConfigData.currentToken;
|
2023-05-19 06:47:32 +00:00
|
|
|
tokenExpiration = clientConfigData.tokenExpiration;
|
2023-05-11 07:43:27 +00:00
|
|
|
}
|
2023-05-15 12:19:43 +00:00
|
|
|
|
2023-05-19 06:47:32 +00:00
|
|
|
if (isForce || !token || !tokenExpiration || moment().isAfter(tokenExpiration)) {
|
2023-06-15 10:50:58 +00:00
|
|
|
await txtSpin(`Requesting new token...`);
|
2023-05-19 06:47:32 +00:00
|
|
|
const clientId = JSON.parse(env.USE_SECRETS_DB) ? clientConfigData.clientId || env.CLIENT_ID : env.CLIENT_ID;
|
|
|
|
const clientSecret = JSON.parse(env.USE_SECRETS_DB) ? clientConfigData.clientSecret || env.CLIENT_SECRET : env.CLIENT_SECRET;
|
2023-05-05 09:48:44 +00:00
|
|
|
|
2023-05-19 06:47:32 +00:00
|
|
|
const data = new URLSearchParams({
|
2023-05-05 09:48:44 +00:00
|
|
|
grant_type: 'client_credentials',
|
|
|
|
client_id: clientId,
|
|
|
|
client_secret: clientSecret,
|
|
|
|
scope: 'role:app catalog:read supply:read organization:read network:write network:read'
|
2023-05-19 06:47:32 +00:00
|
|
|
}).toString();
|
2023-05-15 12:19:43 +00:00
|
|
|
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
2023-06-15 10:50:58 +00:00
|
|
|
const res = (await vnRequest('POST', env.API_ENDPOINT, data, headers)).data;
|
2023-05-11 06:39:02 +00:00
|
|
|
|
2023-05-19 06:47:32 +00:00
|
|
|
const tokenExpiration = moment()
|
2023-06-15 10:50:58 +00:00
|
|
|
.add(res.expires_in, 's')
|
2023-05-05 09:48:44 +00:00
|
|
|
.format('YYYY-MM-DD HH:mm:ss');
|
2023-05-19 06:47:32 +00:00
|
|
|
|
|
|
|
await updateClientConfig({
|
2023-05-05 09:48:44 +00:00
|
|
|
clientId,
|
|
|
|
clientSecret,
|
2023-06-15 10:50:58 +00:00
|
|
|
currentToken: res.access_token,
|
2023-05-19 06:47:32 +00:00
|
|
|
tokenExpiration,
|
|
|
|
});
|
2023-05-15 12:19:43 +00:00
|
|
|
} else
|
2023-06-13 10:51:59 +00:00
|
|
|
optionalMsg = 'Using stored token...';
|
2023-06-16 11:43:24 +00:00
|
|
|
await okSpin(optionalMsg);
|
2023-05-05 09:48:44 +00:00
|
|
|
} catch (err) {
|
2023-07-03 05:35:18 +00:00
|
|
|
await failSpin(err);
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
2023-01-09 11:58:34 +00:00
|
|
|
|
2023-04-06 08:56:52 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Returns the current token.
|
|
|
|
*
|
2023-06-13 10:51:59 +00:00
|
|
|
* @returns {String} The current token
|
2023-04-06 08:56:52 +00:00
|
|
|
*/
|
2023-04-05 12:46:25 +00:00
|
|
|
export async function getCurrentToken() {
|
2023-05-30 09:46:46 +00:00
|
|
|
return (await models.config.findOne()).currentToken;
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
2023-05-08 10:18:14 +00:00
|
|
|
|
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Check the floriday data config.
|
2023-05-08 10:18:14 +00:00
|
|
|
*/
|
|
|
|
export async function checkConfig() {
|
2023-06-16 11:43:24 +00:00
|
|
|
await startSpin(`Checking config...`);
|
2023-05-08 10:18:14 +00:00
|
|
|
|
|
|
|
const excludedEnvVars = ['VSCODE_GIT_ASKPASS_EXTRA_ARGS'];
|
|
|
|
const requiredEnvVars = Object.keys(env);
|
|
|
|
const filteredEnvVars = requiredEnvVars.filter(reqEnvVar => !excludedEnvVars.includes(reqEnvVar));
|
|
|
|
|
2023-06-13 10:51:59 +00:00
|
|
|
for (const reqEnvVar of filteredEnvVars)
|
|
|
|
if (!process.env[reqEnvVar])
|
2023-06-16 11:43:24 +00:00
|
|
|
await failSpin(new Error(`You haven't provided the ${reqEnvVar} environment variable`));
|
2023-06-13 10:51:59 +00:00
|
|
|
|
2023-05-30 09:46:46 +00:00
|
|
|
const clientConfigData = await models.config.findOne();
|
2023-05-08 11:02:31 +00:00
|
|
|
if (!clientConfigData)
|
|
|
|
await updateClientConfig(env.CLIENT_ID, env.CLIENT_SECRET);
|
|
|
|
|
2023-06-16 11:43:24 +00:00
|
|
|
await okSpin();
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-04-06 08:56:52 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Returns the expiration of current token.
|
|
|
|
*
|
2023-06-13 10:51:59 +00:00
|
|
|
* @returns {String} The expiration of current token
|
2023-04-06 08:56:52 +00:00
|
|
|
*/
|
2023-04-05 12:46:25 +00:00
|
|
|
export async function getCurrentTokenExpiration() {
|
2023-05-30 09:46:46 +00:00
|
|
|
return (await models.config.findOne()).tokenExpiration;
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
2023-04-05 12:46:25 +00:00
|
|
|
|
2023-01-09 12:35:05 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Updates the access token in the client config table.
|
|
|
|
*
|
2023-05-19 06:47:32 +00:00
|
|
|
* @param {Array} clientConfig [clientId, clientSecret, currenToken, tokenExpiration]
|
2023-01-09 12:35:05 +00:00
|
|
|
*/
|
2023-05-19 06:47:32 +00:00
|
|
|
export async function updateClientConfig(clientConfig) {
|
|
|
|
try {
|
2023-06-12 12:42:33 +00:00
|
|
|
if (!JSON.parse(process.env.USE_SECRETS_DB))
|
|
|
|
clientId = clientSecret = null
|
|
|
|
await models.config.upsert({
|
|
|
|
id: 1,
|
|
|
|
...clientConfig,
|
|
|
|
});
|
2023-05-19 06:47:32 +00:00
|
|
|
} catch (err) {
|
|
|
|
throw(err);
|
|
|
|
}
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
2023-01-09 11:58:34 +00:00
|
|
|
|
2023-01-11 12:13:22 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Pauses the execution of the script for the specified number of milliseconds.
|
|
|
|
*
|
|
|
|
* @param {Integer} ms
|
2023-01-11 12:13:22 +00:00
|
|
|
*/
|
2023-06-16 11:43:24 +00:00
|
|
|
export async function sleep(ms) {
|
|
|
|
await new Promise(resolve => setTimeout(resolve, ms));
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
2023-01-11 12:13:22 +00:00
|
|
|
|
2023-05-17 05:36:58 +00:00
|
|
|
/**
|
2023-06-12 12:42:33 +00:00
|
|
|
* Sync a model.
|
|
|
|
*
|
2023-06-15 09:29:19 +00:00
|
|
|
* @param {String} model Supported models (./models/methods.yml)
|
2023-05-17 05:36:58 +00:00
|
|
|
*/
|
2023-06-12 12:42:33 +00:00
|
|
|
export async function syncModel(model) {
|
2023-06-16 11:43:24 +00:00
|
|
|
await startSpin(`Syncing ${model}...`);
|
2023-04-24 10:46:06 +00:00
|
|
|
try {
|
2023-06-12 12:42:33 +00:00
|
|
|
const dbSeqNum = await models.sequenceNumber.findOne({ where: { model } })
|
2023-06-15 10:50:58 +00:00
|
|
|
let curSeqNum = dbSeqNum?.maxSequenceNumber ?? 0, i = 0;
|
2023-06-12 12:42:33 +00:00
|
|
|
|
2023-06-15 09:29:19 +00:00
|
|
|
if (!flModels.includes(model))
|
|
|
|
throw new Error('Unsupported model');
|
2023-06-12 12:42:33 +00:00
|
|
|
|
2023-06-15 09:29:19 +00:00
|
|
|
const maxSeqNum = (await vnRequest('GET', `${env.API_URL}${methods[model].maxSeq.url}`)).data;
|
|
|
|
for (curSeqNum; curSeqNum < maxSeqNum; curSeqNum++) {
|
|
|
|
let params, misSeqNum;
|
2023-06-12 12:42:33 +00:00
|
|
|
|
2023-06-15 09:29:19 +00:00
|
|
|
if (methods[model].sync.params) {
|
|
|
|
params = new URLSearchParams();
|
|
|
|
for (const key in methods[model].sync.params) {
|
|
|
|
params.append(key, methods[model].sync.params[key]);
|
|
|
|
}
|
|
|
|
params = params.toString();
|
2023-06-12 12:42:33 +00:00
|
|
|
}
|
2023-06-15 09:29:19 +00:00
|
|
|
|
2023-06-16 11:43:24 +00:00
|
|
|
const res = (await vnRequest('GET', `${env.API_URL}${methods[model].sync.url}${curSeqNum}${params ? `?${params}` : ''}`));
|
|
|
|
if (res?.response?.status == 403 && res?.response?.data?.title === 'There are no connected suppliers.') { // Forbidden
|
2023-07-03 05:35:18 +00:00
|
|
|
await warnSpin(`Syncing ${model}...`, new Error(res.response.data.title));
|
2023-06-16 11:43:24 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const data = res.data.results;
|
|
|
|
curSeqNum = res.data.maximumSequenceNumber;
|
2023-06-15 09:29:19 +00:00
|
|
|
misSeqNum = maxSeqNum - curSeqNum;
|
2023-06-15 10:50:58 +00:00
|
|
|
await insertModel(model, data);
|
2023-06-16 11:43:24 +00:00
|
|
|
await txtSpin(`Syncing ${i = i + data.length} ${model}, ${misSeqNum} missing...`);
|
2023-06-15 09:29:19 +00:00
|
|
|
await insertSequenceNumber(model, curSeqNum);
|
|
|
|
}
|
|
|
|
await insertSequenceNumber(model, maxSeqNum);
|
2023-06-16 11:43:24 +00:00
|
|
|
await txtSpin((i)
|
2023-06-15 09:29:19 +00:00
|
|
|
? `Syncing ${i} ${model}...`
|
|
|
|
: `Syncing ${model}...`);
|
2023-06-16 11:43:24 +00:00
|
|
|
await okSpin();
|
2023-06-12 12:42:33 +00:00
|
|
|
} catch (err) {
|
2023-07-03 05:35:18 +00:00
|
|
|
await failSpin(err);
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
2023-01-24 12:51:44 +00:00
|
|
|
|
2023-05-16 12:12:34 +00:00
|
|
|
/**
|
2023-06-15 10:50:58 +00:00
|
|
|
* Insert data to a model.
|
|
|
|
*
|
|
|
|
* @param {String} model Supported models (./models/methods.yml)
|
|
|
|
* @param {Array} data An array containing the data to be inserted
|
2023-05-16 12:12:34 +00:00
|
|
|
*/
|
2023-06-15 09:29:19 +00:00
|
|
|
export async function insertModel(model, data) {
|
2023-06-15 10:50:58 +00:00
|
|
|
try {
|
|
|
|
switch (model) {
|
|
|
|
case 'organizations':
|
|
|
|
await insertOrganizations(data);
|
|
|
|
break;
|
|
|
|
case 'warehouses':
|
|
|
|
await insertWarehouses(data);
|
|
|
|
break;
|
|
|
|
case 'tradeItems':
|
|
|
|
await insertTradeItems(data);
|
|
|
|
break;
|
|
|
|
case 'supplyLines':
|
|
|
|
await insertSupplyLines(data);
|
|
|
|
break;
|
|
|
|
case 'clockPresalesSupply':
|
|
|
|
await insertClockPresalesSupply(data);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Error('Unsupported model');
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
throw err;
|
2023-06-15 09:29:19 +00:00
|
|
|
}
|
2023-06-15 10:50:58 +00:00
|
|
|
};
|
2023-05-17 10:39:43 +00:00
|
|
|
|
2023-06-15 09:29:19 +00:00
|
|
|
/**
|
|
|
|
* Check (create and/or remove) the connections in Floriday.
|
|
|
|
*/
|
|
|
|
export async function checkConnections(){
|
2023-04-24 10:46:06 +00:00
|
|
|
try {
|
2023-06-16 11:43:24 +00:00
|
|
|
await startSpin('Checking connections...');
|
2023-06-15 09:29:19 +00:00
|
|
|
await createConnections();
|
|
|
|
await deleteConnections();
|
2023-06-16 11:43:24 +00:00
|
|
|
if (spinner) await okSpin();
|
2023-04-24 10:46:06 +00:00
|
|
|
} catch (err) {
|
2023-06-16 11:43:24 +00:00
|
|
|
await failSpin(err);
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
2023-02-10 09:42:54 +00:00
|
|
|
|
2023-06-06 11:52:25 +00:00
|
|
|
/**
|
|
|
|
* Insert sequence number in the database.
|
|
|
|
*
|
2023-06-13 10:51:59 +00:00
|
|
|
* @param {String} model The model identifier
|
|
|
|
* @param {Number} sequenceNumber The sequence number
|
2023-06-06 11:52:25 +00:00
|
|
|
*/
|
|
|
|
export async function insertSequenceNumber(model, sequenceNumber) {
|
|
|
|
const tx = await models.sequelize.transaction();
|
|
|
|
try {
|
|
|
|
await models.sequenceNumber.upsert({
|
|
|
|
model: model,
|
|
|
|
maxSequenceNumber: sequenceNumber,
|
|
|
|
}, { transaction: tx });
|
|
|
|
await tx.commit();
|
|
|
|
} catch (err) {
|
|
|
|
await tx.rollback();
|
|
|
|
throw err;
|
|
|
|
}
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
2023-06-06 11:52:25 +00:00
|
|
|
|
2023-05-11 06:39:02 +00:00
|
|
|
/**
|
2023-06-14 10:22:34 +00:00
|
|
|
* Insert trade items and dependencies in the database.
|
2023-05-19 11:47:36 +00:00
|
|
|
*
|
2023-06-14 10:22:34 +00:00
|
|
|
* @param {Array} tradeItems An array containing the trade item data to be inserted
|
2023-05-11 06:39:02 +00:00
|
|
|
*/
|
2023-06-14 10:22:34 +00:00
|
|
|
export async function insertTradeItems(tradeItems) {
|
2023-05-19 11:47:36 +00:00
|
|
|
const tx = await models.sequelize.transaction();
|
2023-04-24 10:46:06 +00:00
|
|
|
try {
|
2023-06-29 12:53:19 +00:00
|
|
|
const dbData = await models.tradeItem.findAll();
|
2023-06-29 05:50:05 +00:00
|
|
|
let filteredTradeItems = [];
|
|
|
|
|
2023-06-29 12:53:19 +00:00
|
|
|
tradeItems.forEach(value => {
|
|
|
|
if (!value.isDeleted || dbData.includes(value.tradeItemId))
|
|
|
|
filteredTradeItems.push(value);
|
|
|
|
});
|
|
|
|
|
2023-06-29 05:50:05 +00:00
|
|
|
const tradeItemsData = filteredTradeItems.map((tradeItem) => ({
|
2023-05-09 12:13:19 +00:00
|
|
|
...tradeItem,
|
2023-05-17 08:24:10 +00:00
|
|
|
organizationId: tradeItem.supplierOrganizationId,
|
2023-06-14 10:22:34 +00:00
|
|
|
lastSync: moment().format('YYYY-MM-DD HH:mm:ss'),
|
|
|
|
}));
|
|
|
|
|
|
|
|
await models.tradeItem.bulkCreate(tradeItemsData, {
|
|
|
|
updateOnDuplicate: [
|
|
|
|
'tradeItemId',
|
|
|
|
'code',
|
|
|
|
'gtin',
|
|
|
|
'vbnProductCode',
|
|
|
|
'name',
|
|
|
|
'isDeleted',
|
|
|
|
'sequenceNumber',
|
|
|
|
'tradeItemVersion',
|
|
|
|
'isCustomerSpecific',
|
|
|
|
'isHiddenInCatalog',
|
|
|
|
'organizationId',
|
|
|
|
],
|
|
|
|
transaction: tx,
|
|
|
|
});
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-06-14 10:22:34 +00:00
|
|
|
const characteristics = [];
|
|
|
|
const seasonalPeriods = [];
|
|
|
|
const photos = [];
|
|
|
|
const packingConfigurations = [];
|
|
|
|
const countryOfOriginIsoCodes = [];
|
|
|
|
const botanicalNames = [];
|
|
|
|
|
|
|
|
for (const tradeItem of tradeItemsData) {
|
|
|
|
if (tradeItem.characteristics?.length)
|
|
|
|
for (const characteristic of tradeItem.characteristics)
|
|
|
|
characteristics.push({
|
2023-05-15 12:19:43 +00:00
|
|
|
tradeItemId: tradeItem.tradeItemId,
|
2023-05-19 11:47:36 +00:00
|
|
|
...characteristic,
|
2023-06-14 10:22:34 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
if (tradeItem.seasonalPeriods?.length)
|
|
|
|
for (const seasonalPeriod of tradeItem.seasonalPeriods)
|
|
|
|
seasonalPeriods.push({
|
2023-05-15 12:19:43 +00:00
|
|
|
tradeItemId: tradeItem.tradeItemId,
|
2023-05-19 11:47:36 +00:00
|
|
|
...seasonalPeriod,
|
2023-06-14 10:22:34 +00:00
|
|
|
});
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-06-14 10:22:34 +00:00
|
|
|
if (tradeItem.photos?.length)
|
|
|
|
for (const photo of tradeItem.photos)
|
|
|
|
photos.push({
|
2023-05-15 12:19:43 +00:00
|
|
|
...photo,
|
|
|
|
tradeItemId: tradeItem.tradeItemId,
|
2023-06-14 10:22:34 +00:00
|
|
|
});
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-06-14 10:22:34 +00:00
|
|
|
if (tradeItem.packingConfigurations?.length) {
|
2023-05-15 12:19:43 +00:00
|
|
|
for (const packingConfiguration of tradeItem.packingConfigurations) {
|
|
|
|
const uuid = uuidv4();
|
|
|
|
|
2023-06-14 10:22:34 +00:00
|
|
|
packingConfigurations.push({
|
2023-05-15 12:19:43 +00:00
|
|
|
packingConfigurationId: uuid,
|
|
|
|
...packingConfiguration,
|
|
|
|
additionalPricePerPiece_currency: packingConfiguration.additionalPricePerPiece.currency,
|
|
|
|
additionalPricePerPiece_value: packingConfiguration.additionalPricePerPiece.value,
|
|
|
|
tradeItemId: tradeItem.tradeItemId,
|
2023-06-14 10:22:34 +00:00
|
|
|
});
|
2023-05-15 12:19:43 +00:00
|
|
|
|
2023-06-14 10:22:34 +00:00
|
|
|
models.package.upsert({
|
2023-05-15 12:19:43 +00:00
|
|
|
...packingConfiguration.package,
|
2023-05-17 10:39:43 +00:00
|
|
|
packingConfigurationId: uuid,
|
2023-05-15 12:19:43 +00:00
|
|
|
}, { transaction: tx });
|
|
|
|
}
|
2023-06-14 10:22:34 +00:00
|
|
|
}
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-06-14 10:22:34 +00:00
|
|
|
if (tradeItem.countryOfOriginIsoCodes?.length)
|
|
|
|
for (const isoCode of tradeItem.countryOfOriginIsoCodes)
|
|
|
|
countryOfOriginIsoCodes.push({
|
2023-05-15 12:19:43 +00:00
|
|
|
isoCode,
|
|
|
|
tradeItemId: tradeItem.tradeItemId,
|
2023-06-14 10:22:34 +00:00
|
|
|
});
|
2023-04-24 10:46:06 +00:00
|
|
|
|
2023-06-14 10:22:34 +00:00
|
|
|
if (tradeItem.botanicalNames?.length)
|
|
|
|
for (const botanicalName of tradeItem.botanicalNames)
|
|
|
|
botanicalNames.push({
|
2023-05-17 10:39:43 +00:00
|
|
|
botanicalNameId: uuidv4(),
|
2023-05-15 12:19:43 +00:00
|
|
|
name: botanicalName,
|
|
|
|
tradeItemId: tradeItem.tradeItemId,
|
2023-06-14 10:22:34 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (characteristics?.length)
|
|
|
|
await models.characteristic.bulkCreate(characteristics, {
|
|
|
|
updateOnDuplicate: ['tradeItemId', 'vbnCode', 'vbnValueCode'],
|
|
|
|
transaction: tx,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (seasonalPeriods?.length)
|
|
|
|
await models.seasonalPeriod.bulkCreate(seasonalPeriods, {
|
|
|
|
updateOnDuplicate: ['tradeItemId', 'startWeek', 'endWeek'],
|
|
|
|
transaction: tx,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (photos?.length)
|
|
|
|
await models.photo.bulkCreate(photos, {
|
|
|
|
updateOnDuplicate: ['tradeItemId', 'url', 'type', 'primary'],
|
|
|
|
transaction: tx,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (packingConfigurations?.length)
|
|
|
|
await models.packingConfiguration.bulkCreate(packingConfigurations, {
|
|
|
|
updateOnDuplicate: [
|
|
|
|
'packingConfigurationId',
|
|
|
|
'piecesPerPackage',
|
|
|
|
'bunchesPerPackage',
|
|
|
|
'photoUrl',
|
|
|
|
'packagesPerLayer',
|
|
|
|
'layersPerLoadCarrier',
|
|
|
|
'additionalPricePerPiece_currency',
|
|
|
|
'additionalPricePerPiece_value',
|
|
|
|
'transportHeightInCm',
|
|
|
|
'loadCarrierType',
|
|
|
|
'isPrimary',
|
|
|
|
],
|
|
|
|
transaction: tx,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (countryOfOriginIsoCodes?.length)
|
|
|
|
await models.countryOfOriginIsoCode.bulkCreate(countryOfOriginIsoCodes, {
|
|
|
|
updateOnDuplicate: ['tradeItemId', 'isoCode'],
|
|
|
|
transaction: tx,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (botanicalNames?.length)
|
|
|
|
await models.botanicalName.bulkCreate(botanicalNames, {
|
|
|
|
updateOnDuplicate: ['botanicalNameId', 'name', 'tradeItemId'],
|
|
|
|
transaction: tx,
|
|
|
|
});
|
2023-04-24 10:46:06 +00:00
|
|
|
|
|
|
|
await tx.commit();
|
2023-05-09 12:13:19 +00:00
|
|
|
} catch (err) {
|
2023-04-24 10:46:06 +00:00
|
|
|
await tx.rollback();
|
2023-05-11 06:39:02 +00:00
|
|
|
throw err;
|
2023-04-24 10:46:06 +00:00
|
|
|
}
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
2023-02-14 12:34:54 +00:00
|
|
|
|
2023-05-30 09:22:39 +00:00
|
|
|
/**
|
|
|
|
* Insert clock presales supply in the database.
|
|
|
|
*
|
2023-06-14 10:22:34 +00:00
|
|
|
* @param {Array} clockPresalesSupply An array containing the clockPresaleSupplies data to be inserted
|
2023-05-30 09:22:39 +00:00
|
|
|
*/
|
2023-06-14 10:22:34 +00:00
|
|
|
export async function insertClockPresalesSupply(clockPresalesSupply) {
|
2023-05-30 09:22:39 +00:00
|
|
|
const tx = await models.sequelize.transaction();
|
|
|
|
try {
|
2023-06-29 12:53:19 +00:00
|
|
|
const dbData = await models.clockPresaleSupply.findAll();
|
2023-06-29 05:50:05 +00:00
|
|
|
let filteredclockPresalesSupply = [];
|
2023-06-29 12:53:19 +00:00
|
|
|
|
|
|
|
clockPresalesSupply.forEach(value => {
|
|
|
|
if (!(value.status === 'UNAVAILABLE') || dbData.includes(value.supplyLineId))
|
|
|
|
filteredclockPresalesSupply.push(value);
|
|
|
|
});
|
2023-06-29 05:50:05 +00:00
|
|
|
|
|
|
|
const clockPresalesSupplyWithDefaults = filteredclockPresalesSupply.map((clockPresaleSupply) => ({
|
2023-05-30 09:46:46 +00:00
|
|
|
...clockPresaleSupply,
|
|
|
|
pricePerPiece_currency: clockPresaleSupply.pricePerPiece.currency,
|
|
|
|
pricePerPiece_value: clockPresaleSupply.pricePerPiece.value,
|
|
|
|
organizationId: clockPresaleSupply.supplierOrganizationId,
|
2023-06-14 10:22:34 +00:00
|
|
|
}));
|
|
|
|
|
|
|
|
await models.clockPresaleSupply.bulkCreate(clockPresalesSupplyWithDefaults, {
|
|
|
|
updateOnDuplicate: [
|
|
|
|
'supplyLineId',
|
|
|
|
'status',
|
|
|
|
'tradeItemId',
|
|
|
|
'pricePerPiece_currency',
|
|
|
|
'pricePerPiece_value',
|
|
|
|
'deliveryNoteReference',
|
|
|
|
'numberOfPieces',
|
|
|
|
'tradePeriod_startDateTime',
|
|
|
|
'tradePeriod_endDateTime',
|
|
|
|
'organizationId',
|
|
|
|
'tradeInstrument',
|
|
|
|
'salesChannel',
|
|
|
|
'sequenceNumber',
|
|
|
|
'creationDateTime',
|
|
|
|
'lastModifiedDateTime',
|
|
|
|
],
|
|
|
|
transaction: tx,
|
|
|
|
});
|
|
|
|
|
2023-06-29 05:50:05 +00:00
|
|
|
let packingConfigurations = [];
|
|
|
|
|
|
|
|
for (const clockPresale of clockPresalesSupplyWithDefaults) {
|
|
|
|
if (clockPresale.packingConfigurations?.length) {
|
|
|
|
for (const packingConfiguration of clockPresale.packingConfigurations) {
|
|
|
|
const uuid = uuidv4();
|
|
|
|
|
|
|
|
packingConfigurations.push({
|
|
|
|
clockPresaleSupplyPackingConfigurationId: uuid,
|
|
|
|
...packingConfiguration,
|
|
|
|
additionalPricePerPieceCurrency: packingConfiguration.additionalPricePerPiece?.currency,
|
|
|
|
additionalPricePerPieceValue: packingConfiguration.additionalPricePerPiece?.value,
|
|
|
|
supplyLineClockPresaleSupplyId: clockPresale.supplyLineId,
|
|
|
|
});
|
|
|
|
|
|
|
|
models.clockPresaleSupplyPackage.upsert({
|
|
|
|
...packingConfiguration.package,
|
|
|
|
packingConfigurationId: uuid,
|
|
|
|
}, { transaction: tx });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (packingConfigurations?.length)
|
|
|
|
await models.clockPresaleSupplyPackingConfiguration.bulkCreate(packingConfigurations, {
|
|
|
|
updateOnDuplicate: [
|
|
|
|
'clockPresaleSupplyPackingConfigurationId',
|
|
|
|
'piecesPerPackage',
|
|
|
|
'bunchesPerPackage',
|
|
|
|
'packagesPerLayer',
|
|
|
|
'layersPerLoadCarrier',
|
|
|
|
'loadCarrier',
|
|
|
|
'additionalPricePerPieceCurrency',
|
|
|
|
'additionalPricePerPieceValue',
|
|
|
|
'photoUrl',
|
|
|
|
],
|
|
|
|
transaction: tx,
|
|
|
|
});
|
|
|
|
|
2023-05-30 09:22:39 +00:00
|
|
|
await tx.commit();
|
|
|
|
} catch (err) {
|
|
|
|
await tx.rollback();
|
|
|
|
throw err;
|
|
|
|
}
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
2023-05-30 09:22:39 +00:00
|
|
|
|
2023-05-17 11:28:09 +00:00
|
|
|
/**
|
2023-06-14 10:22:34 +00:00
|
|
|
* Insert warehouses in the database.
|
2023-05-19 11:47:36 +00:00
|
|
|
*
|
2023-06-14 10:22:34 +00:00
|
|
|
* @param {Array} warehouses An array containing the warehouses data to be inserted
|
2023-05-17 11:28:09 +00:00
|
|
|
*/
|
2023-06-14 10:22:34 +00:00
|
|
|
export async function insertWarehouses(warehouses) {
|
2023-05-19 11:47:36 +00:00
|
|
|
const tx = await models.sequelize.transaction();
|
2023-05-17 11:28:09 +00:00
|
|
|
try {
|
2023-06-29 12:53:19 +00:00
|
|
|
const dbData = await models.warehouse.findAll();
|
2023-06-29 05:50:05 +00:00
|
|
|
let filteredWarehouses = [];
|
2023-06-29 12:53:19 +00:00
|
|
|
|
|
|
|
warehouses.forEach(value => {
|
|
|
|
if (!value.isDeleted || dbData.includes(value.warehouseId))
|
|
|
|
filteredWarehouses.push(value);
|
|
|
|
});
|
2023-06-29 05:50:05 +00:00
|
|
|
|
|
|
|
const warehousesWithDefaults = filteredWarehouses.map((warehouse) => ({
|
2023-05-17 11:28:09 +00:00
|
|
|
...warehouse,
|
|
|
|
location_gln: warehouse.location.gln,
|
|
|
|
location_address_addressLine: warehouse.location.address.addressLine,
|
|
|
|
location_address_city: warehouse.location.address.city,
|
|
|
|
location_address_countryCode: warehouse.location.address.countryCode,
|
|
|
|
location_address_postalCode: warehouse.location.address.postalCode,
|
|
|
|
location_address_stateOrProvince: warehouse.location.address.stateOrProvince,
|
2023-06-14 10:22:34 +00:00
|
|
|
lastSync: moment().format('YYYY-MM-DD HH:mm:ss'),
|
|
|
|
}));
|
|
|
|
|
|
|
|
await models.warehouse.bulkCreate(warehousesWithDefaults, {
|
2023-06-14 12:02:15 +00:00
|
|
|
updateOnDuplicate: [
|
|
|
|
'warehouseId',
|
|
|
|
'name',
|
|
|
|
'location_gln',
|
|
|
|
'location_address_addressLine',
|
|
|
|
'location_address_city',
|
|
|
|
'location_address_countryCode',
|
|
|
|
'location_address_postalCode',
|
|
|
|
'location_address_stateOrProvince',
|
|
|
|
'isDeleted',
|
|
|
|
'sequenceNumber',
|
|
|
|
'organizationId',
|
|
|
|
'lastSync',
|
|
|
|
],
|
2023-06-14 10:22:34 +00:00
|
|
|
transaction: tx,
|
|
|
|
});
|
|
|
|
|
2023-05-17 11:28:09 +00:00
|
|
|
await tx.commit();
|
|
|
|
} catch (err) {
|
|
|
|
await tx.rollback();
|
|
|
|
throw err;
|
|
|
|
}
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
2023-05-17 11:28:09 +00:00
|
|
|
|
2023-05-18 12:06:11 +00:00
|
|
|
/**
|
2023-06-14 10:22:34 +00:00
|
|
|
* Insert organizations in the database.
|
2023-05-19 11:47:36 +00:00
|
|
|
*
|
2023-06-14 10:22:34 +00:00
|
|
|
* @param {Array} organizations An array containing the organizations data to be inserted
|
2023-05-18 12:06:11 +00:00
|
|
|
*/
|
2023-06-14 10:22:34 +00:00
|
|
|
export async function insertOrganizations(organizations) {
|
2023-05-19 11:47:36 +00:00
|
|
|
const tx = await models.sequelize.transaction();
|
2023-05-18 12:06:11 +00:00
|
|
|
try {
|
2023-06-14 10:22:34 +00:00
|
|
|
const organizationsWithDefaults = organizations.map((organization) => ({
|
2023-05-18 12:06:11 +00:00
|
|
|
...organization,
|
2023-05-19 11:47:36 +00:00
|
|
|
isConnected: JSON.parse(env.ORGS_ALWAYS_CONN),
|
2023-06-14 10:22:34 +00:00
|
|
|
lastSync: moment().format('YYYY-MM-DD HH:mm:ss'),
|
|
|
|
}));
|
|
|
|
|
|
|
|
await models.organization.bulkCreate(organizationsWithDefaults, {
|
2023-06-14 12:02:15 +00:00
|
|
|
updateOnDuplicate: [
|
|
|
|
'organizationId',
|
|
|
|
'sequenceNumber',
|
|
|
|
'companyGln',
|
|
|
|
'name',
|
|
|
|
'commercialName',
|
|
|
|
'email',
|
|
|
|
'phone',
|
|
|
|
'website',
|
|
|
|
'rfhRelationId',
|
|
|
|
'paymentProviders',
|
|
|
|
'endDate',
|
|
|
|
'mailingAddress',
|
|
|
|
'physicalAddress',
|
|
|
|
'isConnected',
|
|
|
|
'lastSync',
|
|
|
|
],
|
2023-06-14 10:22:34 +00:00
|
|
|
transaction: tx,
|
|
|
|
});
|
|
|
|
|
2023-05-30 12:18:02 +00:00
|
|
|
await tx.commit();
|
|
|
|
} catch (err) {
|
|
|
|
await tx.rollback();
|
|
|
|
throw err;
|
|
|
|
}
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
2023-05-30 12:18:02 +00:00
|
|
|
|
2023-06-14 10:22:34 +00:00
|
|
|
/* Checkear dependecias supply line
|
|
|
|
|
2023-06-12 12:42:33 +00:00
|
|
|
// Check if the warehouse exists, and if it doesn't, create it
|
|
|
|
let warehouse = await models.warehouse.findOne({
|
2023-06-12 12:51:34 +00:00
|
|
|
where: { warehouseId: supplyLine.warehouseId }
|
2023-06-12 12:42:33 +00:00
|
|
|
});
|
|
|
|
if (!warehouse) {
|
2023-06-13 10:51:59 +00:00
|
|
|
let warehouse = (await vnRequest('GET', `${env.API_URL}/warehouses/${supplyLine.warehouseId}`)).data;
|
2023-06-12 12:42:33 +00:00
|
|
|
|
|
|
|
// Check if the organization exists, and if it doesn't, create it
|
|
|
|
let organization = await models.organization.findOne({
|
|
|
|
where: { organizationId: warehouse.organizationId }
|
|
|
|
}, { transaction: tx });
|
|
|
|
if (!organization) {
|
2023-06-13 10:51:59 +00:00
|
|
|
let organization = (await vnRequest('GET', `${env.API_URL}/organizations/${warehouse.organizationId}`)).data;
|
2023-06-12 12:42:33 +00:00
|
|
|
await insertOrganization(organization);
|
|
|
|
}
|
|
|
|
|
|
|
|
await insertWarehouse(warehouse);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the trade item exists, and if it doesn't, create it
|
|
|
|
let tradeItem = await models.tradeItem.findOne({
|
|
|
|
where: { tradeItemId: supplyLine.tradeItemId }
|
|
|
|
}, { transaction: tx });
|
|
|
|
if (!tradeItem) {
|
2023-06-13 10:51:59 +00:00
|
|
|
let tradeItem = (await vnRequest('GET', `${env.API_URL}/trade-items/${supplyLine.tradeItemId}`)).data;
|
2023-06-12 12:42:33 +00:00
|
|
|
await insertTradeItem(tradeItem);
|
|
|
|
}
|
|
|
|
|
2023-06-14 10:22:34 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Insert supply lines and dependencies in the database.
|
|
|
|
*
|
|
|
|
* @param {Array} supplyLines An array containing the supply line data to be inserted
|
|
|
|
*/
|
|
|
|
export async function insertSupplyLines(supplyLines) {
|
|
|
|
const tx = await models.sequelize.transaction();
|
|
|
|
try {
|
2023-06-29 12:53:19 +00:00
|
|
|
const dbData = await models.supplyLine.findAll();
|
2023-06-29 05:50:05 +00:00
|
|
|
let filteredSupplyLines = [];
|
2023-06-29 12:53:19 +00:00
|
|
|
|
|
|
|
supplyLines.forEach(value => {
|
|
|
|
if ((!value.isDeleted && !(value.status === 'UNAVAILABLE')) || dbData.includes(value.supplyLineId))
|
|
|
|
filteredSupplyLines.push(value);
|
|
|
|
});
|
2023-06-29 05:50:05 +00:00
|
|
|
|
|
|
|
const supplyLinesData = filteredSupplyLines.map((supplyLine) => ({
|
2023-05-30 12:18:02 +00:00
|
|
|
...supplyLine,
|
|
|
|
organizationId: supplyLine.supplierOrganizationId,
|
|
|
|
deliveryPeriodStartDateTime: supplyLine.deliveryPeriod?.startDateTime ?? null,
|
|
|
|
deliveryPeriodEndDateTime: supplyLine.deliveryPeriod?.endDateTime ?? null,
|
|
|
|
orderPeriodStartDateTime: supplyLine.orderPeriod?.startDateTime ?? null,
|
|
|
|
orderPeriodEndDateTime: supplyLine.orderPeriod?.endDateTime ?? null,
|
|
|
|
agreementReference_code: supplyLine.agreementReference?.code ?? null,
|
|
|
|
agreementReference_description: supplyLine.agreementReference?.description ?? null,
|
2023-06-14 10:22:34 +00:00
|
|
|
lastSync: moment().format('YYYY-MM-DD HH:mm:ss'),
|
|
|
|
}));
|
2023-05-30 12:18:02 +00:00
|
|
|
|
2023-06-14 12:02:15 +00:00
|
|
|
await models.supplyLine.bulkCreate(supplyLinesData, {
|
|
|
|
updateOnDuplicate: [
|
|
|
|
'supplyLineId',
|
|
|
|
'status',
|
|
|
|
'numberOfPieces',
|
|
|
|
'deliveryPeriodStartDateTime',
|
|
|
|
'deliveryPeriodEndDateTime',
|
|
|
|
'orderPeriodStartDateTime',
|
|
|
|
'orderPeriodEndDateTime',
|
|
|
|
'warehouseId',
|
|
|
|
'sequenceNumber',
|
|
|
|
'type',
|
|
|
|
'isDeleted',
|
|
|
|
'salesUnit',
|
|
|
|
'agreementReferenceCode',
|
|
|
|
'agreementReferenceDescription',
|
|
|
|
'isLimited',
|
|
|
|
'isCustomerSpecific',
|
|
|
|
'tradeItemId',
|
|
|
|
'organizationId',
|
|
|
|
'lastSync',
|
|
|
|
],
|
|
|
|
transaction: tx,
|
|
|
|
});
|
2023-06-14 10:22:34 +00:00
|
|
|
|
|
|
|
const packingConfigurations = [];
|
|
|
|
const volumePrices = [];
|
|
|
|
|
|
|
|
for (const supplyLine of supplyLinesData)
|
|
|
|
if (supplyLine.packingConfigurations?.length) {
|
|
|
|
for (const packingConfiguration of supplyLine.packingConfigurations)
|
|
|
|
packingConfigurations.push({
|
|
|
|
packingConfigurationId: uuidv4(),
|
|
|
|
...packingConfiguration,
|
|
|
|
packageVbnPackageCode: packingConfiguration.package.vbnPackageCode,
|
|
|
|
packageCustomPackageId: packingConfiguration.package.customPackageId,
|
|
|
|
additionalPricePerPieceCurrency: packingConfiguration.additionalPricePerPiece.currency,
|
|
|
|
additionalPricePerPieceValue: packingConfiguration.additionalPricePerPiece.value,
|
|
|
|
supplyLineId: supplyLine.supplyLineId,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (supplyLine.volumePrices?.length)
|
|
|
|
for (const volumePrice of supplyLine.volumePrices)
|
|
|
|
volumePrices.push({
|
|
|
|
supplyLineId: supplyLine.supplyLineId,
|
|
|
|
...volumePrice,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (packingConfigurations.length)
|
|
|
|
await models.supplyLinePackingConfiguration.bulkCreate(packingConfigurations, {
|
|
|
|
updateOnDuplicate: [
|
|
|
|
'packingConfigurationId',
|
|
|
|
'packageVbnPackageCode',
|
|
|
|
'packageCustomPackageId',
|
|
|
|
'piecesPerPackage',
|
|
|
|
'bunchesPerPackage',
|
|
|
|
'photoUrl',
|
|
|
|
'packagesPerLayer',
|
|
|
|
'layersPerLoadCarrier',
|
|
|
|
'transportHeightInCm',
|
|
|
|
'loadCarrierType',
|
|
|
|
'additionalPricePerPieceCurrency',
|
|
|
|
'additionalPricePerPieceValue',
|
|
|
|
'isPrimary'
|
|
|
|
],
|
|
|
|
transaction: tx,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (volumePrices.length)
|
|
|
|
await models.volumePrice.bulkCreate(volumePrices, {
|
|
|
|
updateOnDuplicate: ['supplyLineId', 'unit', 'pricePerPiece'],
|
|
|
|
transaction: tx,
|
|
|
|
});
|
2023-05-30 12:18:02 +00:00
|
|
|
|
2023-05-18 12:06:11 +00:00
|
|
|
await tx.commit();
|
|
|
|
} catch (err) {
|
|
|
|
await tx.rollback();
|
|
|
|
throw err;
|
|
|
|
}
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
2023-05-18 12:06:11 +00:00
|
|
|
|
2023-06-15 09:29:19 +00:00
|
|
|
/**
|
|
|
|
* Create the connections in Floriday of the connected organizations.
|
|
|
|
**/
|
|
|
|
export async function createConnections() {
|
|
|
|
try {
|
|
|
|
const flConnections = (await vnRequest('GET', `${env.API_URL}${methods.connections.base.url}`)).data;
|
|
|
|
const dbConnections = await models.organization.findAll({
|
|
|
|
where: { isConnected: true }
|
|
|
|
});
|
|
|
|
|
2023-06-15 12:51:58 +00:00
|
|
|
let connectionsToPut = [], i = 0;
|
2023-06-16 09:50:40 +00:00
|
|
|
dbConnections.forEach(value => {
|
|
|
|
if (!flConnections.includes(value.organizationId))
|
|
|
|
connectionsToPut.push(value.organizationId);
|
2023-06-15 09:29:19 +00:00
|
|
|
});
|
|
|
|
|
2023-06-16 11:43:24 +00:00
|
|
|
if (connectionsToPut.length && !spinner) await startSpin('Creating connections...');
|
2023-06-16 09:50:40 +00:00
|
|
|
|
2023-06-15 09:29:19 +00:00
|
|
|
for (let connection of connectionsToPut) {
|
|
|
|
await vnRequest('PUT', `${env.API_URL}${methods.connections.base.url}${connection}`);
|
2023-06-16 11:43:24 +00:00
|
|
|
await txtSpin(`Creating ${i++} connections, ${connectionsToPut.length - i} missing...`);
|
2023-06-15 09:29:19 +00:00
|
|
|
}
|
2023-06-16 11:43:24 +00:00
|
|
|
if (spinner && i) await okSpin(`Creating ${i++} connections...`);
|
2023-06-15 09:29:19 +00:00
|
|
|
} catch (err) {
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-05 12:46:25 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Removes Floriday connections that we don't have in the database.
|
2023-05-11 12:36:55 +00:00
|
|
|
**/
|
|
|
|
export async function deleteConnections() {
|
|
|
|
try {
|
2023-06-15 09:29:19 +00:00
|
|
|
const flConnections = (await vnRequest('GET', `${env.API_URL}/connections`)).data;
|
|
|
|
const dbConnections = await models.organization.findAll({
|
2023-06-16 09:50:40 +00:00
|
|
|
where: { isConnected: false }
|
2023-05-17 08:24:10 +00:00
|
|
|
});
|
2023-05-16 12:12:34 +00:00
|
|
|
|
2023-06-15 12:51:58 +00:00
|
|
|
let ghostConnections = [], i = 0;
|
2023-06-16 09:50:40 +00:00
|
|
|
dbConnections.forEach(value => {
|
|
|
|
if (flConnections.includes(value.organizationId))
|
|
|
|
ghostConnections.push(value.organizationId);
|
2023-06-15 09:29:19 +00:00
|
|
|
});
|
2023-05-17 08:24:10 +00:00
|
|
|
|
2023-06-16 11:43:24 +00:00
|
|
|
if (ghostConnections.length && !spinner) await startSpin('Deleting connections...');
|
2023-06-16 09:50:40 +00:00
|
|
|
|
2023-05-16 12:12:34 +00:00
|
|
|
for (let connection of ghostConnections) {
|
2023-06-13 10:51:59 +00:00
|
|
|
await vnRequest('DELETE', `${env.API_URL}/connections/${connection}`);
|
2023-06-16 11:43:24 +00:00
|
|
|
await txtSpin(`Deleting ${i++} connections, ${ghostConnections.length - i} missing...`)
|
2023-05-11 12:36:55 +00:00
|
|
|
}
|
2023-06-16 09:50:40 +00:00
|
|
|
|
2023-06-16 11:43:24 +00:00
|
|
|
if (spinner && i) await okSpin(`Deleting ${i++} connections...`);
|
2023-05-11 12:36:55 +00:00
|
|
|
} catch (err) {
|
2023-07-03 05:35:18 +00:00
|
|
|
throw err;
|
2023-05-11 12:36:55 +00:00
|
|
|
}
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
2023-05-11 12:36:55 +00:00
|
|
|
|
2023-05-12 07:13:03 +00:00
|
|
|
/**
|
2023-05-19 11:47:36 +00:00
|
|
|
* Perform a REST request.
|
|
|
|
*
|
2023-06-13 10:51:59 +00:00
|
|
|
* @param {String} url The URL of the REST request
|
|
|
|
* @param {String} method The HTTP method of the request (e.g., GET, POST, PUT, DELETE)
|
|
|
|
* @param {Array} body The body of the request, typically an array of data to be sent
|
|
|
|
* @param {Array} header The headers of the request, typically an array of key-value pairs
|
2023-05-19 11:47:36 +00:00
|
|
|
*
|
2023-06-13 10:51:59 +00:00
|
|
|
* @return {Array} An array containing the response data from the REST request
|
2023-05-12 07:13:03 +00:00
|
|
|
**/
|
2023-06-13 10:51:59 +00:00
|
|
|
export async function vnRequest(method, url, data, headers) {
|
2023-05-17 08:59:27 +00:00
|
|
|
if (!headers)
|
|
|
|
headers = {
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
'Authorization': `Bearer ${await getCurrentToken()}`,
|
|
|
|
'X-Api-Key': process.env.API_KEY,
|
|
|
|
};
|
|
|
|
|
2023-05-19 10:20:43 +00:00
|
|
|
while(true) {
|
2023-05-22 07:47:16 +00:00
|
|
|
try {
|
2023-05-19 06:47:32 +00:00
|
|
|
return (['GET', 'DELETE'].includes(method))
|
|
|
|
? await axios({method, url, headers})
|
2023-05-19 10:20:43 +00:00
|
|
|
: await axios({method, url, data, headers});
|
2023-05-22 07:47:16 +00:00
|
|
|
} catch (err) {
|
|
|
|
switch (err.code) {
|
|
|
|
case 'ECONNRESET': // Client network socket TLS
|
2023-06-21 09:16:47 +00:00
|
|
|
await warnSpin(null, err);
|
|
|
|
await sleep(1000);
|
|
|
|
break;
|
2023-06-16 12:26:24 +00:00
|
|
|
case 'EAI_AGAIN': // getaddrinfo || Lost connection
|
2023-06-21 09:16:47 +00:00
|
|
|
await warnSpin(null, err);
|
2023-06-16 12:26:24 +00:00
|
|
|
await sleep(env.MS_RETRY_LOST_CONNECTION);
|
2023-05-22 07:47:16 +00:00
|
|
|
break;
|
|
|
|
case 'ECONNABORTED':
|
|
|
|
case 'ECONNREFUSED':
|
|
|
|
case 'ERR_BAD_REQUEST':
|
|
|
|
switch (err.response.status) {
|
2023-06-16 11:43:24 +00:00
|
|
|
case 404, 403: // Not found and Forbidden
|
2023-05-30 09:22:39 +00:00
|
|
|
return err;
|
2023-05-22 07:47:16 +00:00
|
|
|
case 504:
|
|
|
|
case 502:
|
2023-06-21 09:16:47 +00:00
|
|
|
await warnSpin(null, err,);
|
2023-06-16 11:43:24 +00:00
|
|
|
await sleep(1000);
|
2023-05-22 07:47:16 +00:00
|
|
|
break;
|
|
|
|
case 429: // Too Many Requests
|
2023-06-21 09:16:47 +00:00
|
|
|
await warnSpin(null, err);
|
2023-06-16 11:43:24 +00:00
|
|
|
await sleep(60000);
|
2023-05-22 07:47:16 +00:00
|
|
|
break;
|
|
|
|
case 401: // Unauthorized
|
2023-06-21 09:16:47 +00:00
|
|
|
await warnSpin(null, err);
|
2023-06-15 10:50:58 +00:00
|
|
|
await checkToken(true);
|
2023-05-22 07:47:16 +00:00
|
|
|
headers.Authorization
|
|
|
|
? headers.Authorization = `Bearer ${await getCurrentToken()}`
|
2023-06-19 10:01:31 +00:00
|
|
|
: handler('critical', err);
|
2023-05-22 07:47:16 +00:00
|
|
|
break;
|
|
|
|
default:
|
2023-06-21 09:16:47 +00:00
|
|
|
await warnSpin(null, err);
|
2023-06-16 11:43:24 +00:00
|
|
|
await sleep(env.MS_RETRY_UNHANDLED_ERROR);
|
2023-05-22 07:47:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2023-06-21 09:16:47 +00:00
|
|
|
await warnSpin(null, err);
|
2023-06-16 11:43:24 +00:00
|
|
|
await sleep(env.MS_RETRY_UNHANDLED_ERROR);
|
2023-05-22 07:47:16 +00:00
|
|
|
break;
|
|
|
|
}
|
2023-06-16 12:26:24 +00:00
|
|
|
if (!err?.code === 'EAI_AGAIN') await startSpin();
|
2023-05-15 12:19:43 +00:00
|
|
|
}
|
2023-05-19 11:47:36 +00:00
|
|
|
}
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the text of spinner.
|
|
|
|
*
|
|
|
|
* @param {String} msg Text of spinner
|
|
|
|
* @param {Boolean} isNew Reinstantiate the object
|
|
|
|
**/
|
2023-06-16 11:43:24 +00:00
|
|
|
export async function startSpin(msg, isKeep) {
|
2023-06-13 10:51:59 +00:00
|
|
|
if (JSON.parse(env.TIME_STAMPS) && msg)
|
|
|
|
msg = `${chalk.gray(`[${new moment().format('YYYY-MM-DD hh:mm:ss A')}]`)} ${msg}`;
|
|
|
|
|
2023-06-16 09:50:40 +00:00
|
|
|
(isKeep)
|
2023-06-14 10:22:34 +00:00
|
|
|
? spinner.start()
|
|
|
|
: spinner = ora({
|
|
|
|
text: msg,
|
|
|
|
spinner: 'arc',
|
|
|
|
interval: 40,
|
|
|
|
color: 'white',
|
|
|
|
}).start();
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the text of spinner.
|
|
|
|
*
|
|
|
|
* @param {String} msg Text of spinner
|
|
|
|
**/
|
2023-06-16 11:43:24 +00:00
|
|
|
export async function txtSpin(msg) {
|
2023-06-19 10:01:31 +00:00
|
|
|
if (!spinner) {
|
|
|
|
startSpin(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-06-13 10:51:59 +00:00
|
|
|
if (JSON.parse(env.TIME_STAMPS) && msg)
|
|
|
|
msg = `${chalk.gray(`[${new moment().format('YYYY-MM-DD hh:mm:ss A')}]`)} ${msg}`;
|
|
|
|
|
2023-06-19 10:01:31 +00:00
|
|
|
spinner.text = msg;
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the spinner to ok.
|
|
|
|
*
|
|
|
|
* @param {String} msg Text of spinner
|
|
|
|
**/
|
2023-06-16 11:43:24 +00:00
|
|
|
export async function okSpin(msg) {
|
2023-06-13 10:51:59 +00:00
|
|
|
if (JSON.parse(env.TIME_STAMPS) && msg)
|
|
|
|
msg = `${chalk.gray(`[${new moment().format('YYYY-MM-DD hh:mm:ss A')}]`)} ${msg ?? ''}`;
|
|
|
|
|
|
|
|
if (spinner) {
|
|
|
|
spinner.succeed(msg);
|
2023-06-16 09:50:40 +00:00
|
|
|
spinner = null;
|
2023-06-13 10:51:59 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the spinner to waning and throw a warning.
|
|
|
|
*
|
|
|
|
* @param {String} msg Text of spinner
|
|
|
|
* @param {Error} err Error object
|
|
|
|
**/
|
2023-06-21 09:16:47 +00:00
|
|
|
export async function warnSpin(msg, err) {
|
2023-06-13 10:51:59 +00:00
|
|
|
if (JSON.parse(env.TIME_STAMPS) && msg)
|
|
|
|
msg = `${chalk.gray(`[${new moment().format('YYYY-MM-DD hh:mm:ss A')}]`)} ${msg}`;
|
|
|
|
|
|
|
|
if (spinner) {
|
|
|
|
spinner.warn(msg);
|
2023-06-21 09:16:47 +00:00
|
|
|
spinner = null;
|
2023-06-13 10:51:59 +00:00
|
|
|
}
|
2023-06-16 11:43:24 +00:00
|
|
|
if (err) await handler('warning', err);
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the spinner to fail and throw a error.
|
|
|
|
*
|
|
|
|
* @param {Error} err Error object
|
|
|
|
**/
|
2023-06-16 11:43:24 +00:00
|
|
|
export async function failSpin(err) {
|
2023-06-13 10:51:59 +00:00
|
|
|
if (spinner) {
|
|
|
|
spinner.fail();
|
2023-06-16 09:50:40 +00:00
|
|
|
spinner = null;
|
2023-06-13 10:51:59 +00:00
|
|
|
}
|
2023-06-14 10:22:34 +00:00
|
|
|
if (err) throw err;
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the spinner to fail and throw a critical error.
|
|
|
|
*
|
|
|
|
* @param {Error} err Error object
|
|
|
|
**/
|
2023-06-16 11:43:24 +00:00
|
|
|
export async function criticalSpin(err) {
|
2023-06-13 10:51:59 +00:00
|
|
|
if (spinner) {
|
|
|
|
spinner.fail();
|
2023-06-15 10:50:58 +00:00
|
|
|
spinner = null;
|
2023-06-13 10:51:59 +00:00
|
|
|
}
|
2023-06-16 11:43:24 +00:00
|
|
|
handler('critical', err);
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|
2023-05-12 07:13:03 +00:00
|
|
|
|
2023-06-16 09:50:40 +00:00
|
|
|
/**
|
|
|
|
* Separator.
|
|
|
|
*
|
|
|
|
* @param {String} msg String to show
|
|
|
|
**/
|
2023-06-16 11:43:24 +00:00
|
|
|
export async function separator(msg) {
|
|
|
|
console.log(chalk.gray(`↺ ──────────────────────── ${msg}`));
|
2023-06-16 09:50:40 +00:00
|
|
|
};
|
|
|
|
|
2023-05-11 12:36:55 +00:00
|
|
|
/**
|
2023-06-16 11:43:24 +00:00
|
|
|
* Function to handle error messages.
|
2023-05-19 11:47:36 +00:00
|
|
|
*
|
2023-06-16 11:43:24 +00:00
|
|
|
* @param {String} type Type name
|
2023-06-13 10:51:59 +00:00
|
|
|
* @param {Error} err Error object
|
2023-04-05 12:46:25 +00:00
|
|
|
**/
|
2023-06-16 11:43:24 +00:00
|
|
|
export async function handler(type, err) {
|
|
|
|
let header = (`${arrow} [${type.toUpperCase()}]`);
|
|
|
|
let msg = (err.response?.status && err.response?.data?.message)
|
|
|
|
? `${err.response.status} - ${err.response?.data?.message}`
|
|
|
|
: err.message;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case 'critical':
|
|
|
|
header = chalk.red.bold(header);
|
|
|
|
msg = chalk.red(msg);
|
|
|
|
break;
|
|
|
|
case 'warning':
|
|
|
|
header = chalk.yellow.bold(header);
|
|
|
|
msg = chalk.yellow(msg);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
console.error('Unhandled handler type');
|
|
|
|
process.exit();
|
|
|
|
};
|
|
|
|
|
|
|
|
console.log(header, msg);
|
|
|
|
if (type === 'critical') process.exit();
|
2023-06-13 10:51:59 +00:00
|
|
|
};
|