floriday/utils.js

413 lines
13 KiB
JavaScript

import moment from 'moment';
import fetch from 'node-fetch';
import dotenv from 'dotenv';
import { models } from './models/index.js';
//import { v4 as uuidv4 } from 'uuid';
//import cliProgress from 'cli-progress';
import suppliersGln from './suppliersGln.js';
dotenv.config();
/**
* The Endpoint where the Access Token is requested
*/
const _accessTokenEndpoint = 'https://idm.staging.floriday.io/oauth2/ausmw6b47z1BnlHkw0h7/v1/token';
const BASE_CUSTOMER_URL = 'https://api.staging.floriday.io/customers-api/2022v2/';
/**
* Gets the Access Token from the client config table
*
* @param {sequelize.models} models
* @returns {Date} tokenExpirationDate formated as YYYY-MM-DD HH:mm:ss
*/
async function getClientToken() {
const clientConfigData = await models.clientConfig.findAll();
const now = moment().format('YYYY-MM-DD HH:mm:ss');
const tokenExpirationDate = clientConfigData[0].tokenExpiration;
if (clientConfigData[0].tokenExpiration == null || moment(now).isAfter(tokenExpirationDate)) {
let clientId = clientConfigData[0].clientId;
let clientSecret = clientConfigData[0].clientSecret;
const tokenRequest = await fetch(_accessTokenEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `grant_type=client_credentials&client_id=${clientId}&client_secret=${clientSecret}&scope=role:app catalog:read supply:read organization:read network:write network:read`,
});
const tokenResponse = await tokenRequest.json();
if (tokenRequest.status === 200) {
console.log('Token request successful');
} else {
throw new Error(
`Token request failed with status ${tokenRequest.status}`
);
}
const accessToken = tokenResponse.access_token;
let now = moment().format('YYYY-MM-DD HH:mm:ss');
let tokenExpirationDate = moment(now)
.add(tokenResponse.expires_in, 's')
.format('YYYY-MM-DD HH:mm:ss');
await updateClientConfig(
clientId,
clientSecret,
accessToken,
tokenExpirationDate
);
return tokenExpirationDate;
} else {
console.log('Using the current token...');
return tokenExpirationDate;
}
}
/**
* Updates the Access Token in the client config table
*
* @param {sequelize.models} models
* @param {String} clientId
* @param {String} clientSecret
* @param {String} accessToken
* @param {String} tokenExpirationDate
*/
async function updateClientConfig(clientId, clientSecret, accessToken, tokenExpirationDate) {
try {
console.log('Updating the client config with the new token...');
await models.clientConfig.update(
{
clientId: clientId,
clientSecret: clientSecret,
currentToken: accessToken,
tokenExpiration: tokenExpirationDate,
requestLimit : 500
},
{
where: {
id: 1,
},
}
);
console.log('Client config updated, new Token set');
console.log('New token expiration date: ', tokenExpirationDate);
} catch (error) {
console.log('There was a error while updating the client config');
console.log(error);
}
}
/**
* returns the current Access Token
*
* @returns
*/
async function getJWT() {
const clientConfigData = await models.clientConfig.findAll();
return clientConfigData[0].currentToken;
}
/**
* pauses the execution of the script for the given amount of milliseconds
*
* @param {integer} ms
* @returns A promise that resolves after ms milliseconds.
*/
async function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
/**
* Recieves an array of functions and executes them in a queue with the given concurrency
*
* @param {Array} fnArray
* @param {Number} concurrency
* @returns
*/
async function asyncQueue(fnArray, concurrency = 1) {
const results = []; // 1
const queue = fnArray.map((fn, index) => () => fn().then((result) => results[index] = result));
const run = async () => { // 2
const fn = queue.shift();
if (fn) {
await fn();
await run();
}
};
const promises = []; // 3
while (concurrency--) { // 4
promises.push(run());
}
await Promise.all(promises); // 5
return results;
}
// 1. Create an array of functions that will be executed in a queue
// 2. Create a function that will execute the functions in the queue
// 3. Create an array of promises that will execute the run function
// 4. Execute the run function while the concurrency is greater than 0
// 5. Return the results
/**
* Syncs the sequence number for the given model
* if no params are given it will reset all the sequence number to 0
*
* @param {Number} current - current sequence number
* @param {String} model - model name
* @param {Number} maximumSequenceNumber - maximum sequence number
* @returns
*/
async function syncSequence(current = 0, model = null ,maximumSequenceNumber = 0){
if (model == null && current == 0){
let mockModels = ['suppliers','tradeItems','supplyLines',];
for (let i = 0; i < mockModels.length; i++) {
const element = mockModels[i];
console.log('Syncing sequence for: ', element);
await syncSequence(0, element);
}
} else {
let tx = await models.sequelize.transaction();
try {
await models.sequenceNumber.upsert({
model: model,
sequenceNumber: current,
maximumSequenceNumber: maximumSequenceNumber
},{ transaction: tx });
await tx.commit();
} catch (error) {
await tx.rollback();
console.log('Error while syncing sequence number for: ', model);
}
}
}
async function syncSuppliers(){
let headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${await getJWT()}`,
'X-Api-Key': process.env.API_KEY
};
let rFloraHolland = suppliersGln.floraholland;
let queryConnections = `${BASE_CUSTOMER_URL}connections`;
let responseConnections = await fetch(queryConnections, {
method: 'GET',
headers: headers
});
let dataConnections = await responseConnections.json();
for(let connection of dataConnections){
await models.connections.findOrCreate({
where: {
organizationId: connection
},
defaults: {
organizationId: connection,
connect : true
}
});
}
for(let producer of rFloraHolland){
let query = `${BASE_CUSTOMER_URL}organizations/gln/${producer}`;
let response = await fetch(query, {
method: 'GET',
headers: headers
});
let data = await response.json();
if (response.status === 200) {
console.log('Supplier request successful');
let connection = await models.connections.findOne({
where: {
organizationId: data.organizationId,
connect: true
}
});
let isConnected = false;
if (connection != null) {
isConnected = true;
let where = {
companyGln: producer,
isConnected: isConnected
};
let defaults = {
isConnected: isConnected,
commercialName: data.commercialName,
email: data.email,
phone: data.phone,
website: data.website,
mailingAddress: data.mailingAddress,
physicalAddress: data.physicalAddress,
pythosanitaryNumber: data.phytosanitaryNumber,
sequenceNumber: data.sequenceNumber,
organizationId: data.organizationId,
companyGln: producer,
name: data.name,
endDate: data.endDate,
rfhRelationId: data.rfhRelationId,
organizationType: data.organizationType,
paymentProviders: JSON.stringify(data.paymentProviders),
};
await models.suppliers.destroy({
where: {
companyGln: producer,
isConnected: false
}
});
await models.suppliers.findOrCreate({
where : where,
defaults: defaults
});
} else {
let where = {
companyGln: producer,
isConnected: isConnected
};
let defaults = {
isConnected: isConnected,
commercialName: data.commercialName,
email: data.email,
phone: data.phone,
website: data.website,
mailingAddress: data.mailingAddress,
physicalAddress: data.physicalAddress,
pythosanitaryNumber: data.phytosanitaryNumber,
sequenceNumber: data.sequenceNumber,
organizationId: data.organizationId,
companyGln: producer,
name: data.name,
endDate: data.endDate,
rfhRelationId: data.rfhRelationId,
organizationType: data.organizationType,
paymentProviders: JSON.stringify(data.paymentProviders),
};
await models.suppliers.destroy({
where: {
companyGln: producer,
isConnected: true
}
});
await models.suppliers.findOrCreate({
where: where,
defaults: defaults
});
}
console.log(`DATA FOR SUPPLIER: ${data.name} OK`);
} else {
console.log('Supplier request failed with status ', response.status);
console.log('Supplier: ', data);
console.log('response: ', response);
console.log('query: ', query);
}
}
}
async function syncTradeItems(){
let suppliers = await models.suppliers.findAll({
where: {
isConnected: true
}
});
let headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${await getJWT()}`,
'X-Api-Key': process.env.API_KEY
};
for (let i = 0; i < suppliers.length; i++) {
let queryMaxSequence = `${BASE_CUSTOMER_URL}trade-items/current-max-sequence`;
let responseMaxSequence = await fetch(queryMaxSequence, {
method: 'GET',
headers: headers
});
let maximumSequenceNumber = await responseMaxSequence.json();
await syncSequence(0, 'tradeItems', maximumSequenceNumber);
let currentSequence = await models.sequenceNumber.findOne({
where: {
model: 'tradeItems'
}
});
currentSequence = currentSequence.sequenceNumber;
let estimatedIterations = Math.ceil(maximumSequenceNumber / 1000);
let supplier = suppliers[i];
console.log('Syncing trade items for: ', supplier.name);
console.log('Supplier Id: ', supplier.organizationId);
console.log('Current sequence number: ', currentSequence);
console.log('Maximum sequence number: ', maximumSequenceNumber);
console.log('Estimated iterations: ', estimatedIterations);
for (let j = 0; j < estimatedIterations; j++) {
let query = `${BASE_CUSTOMER_URL}trade-items/sync/${currentSequence}?supplierOrganizationId=${supplier.organizationId}&limit=1000`;
let request = await fetch(query, {
method: 'GET',
headers: headers
});
let data = await request.json();
let results = data.results;
if (results.length == 0) {
console.log('No more trade items to sync');
break;
}
}
}
}
export { getClientToken, updateClientConfig, getJWT, sleep, asyncQueue, syncSequence, syncSuppliers, syncTradeItems };