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.upsert({ id: 1, clientId: clientId, clientSecret: clientSecret, currentToken: accessToken, tokenExpiration: tokenExpirationDate, requestLimit: 500, }); 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 { let sequence = await models.sequenceNumber.findOrCreate({ where: { model: model }, defaults: { model: model, sequenceNumber: current, maximumSequenceNumber: maximumSequenceNumber }, transaction: tx }); if (sequence[1] == false){ await models.sequenceNumber.update({ sequenceNumber: current, maximumSequenceNumber: maximumSequenceNumber }, { where: { model: model }, 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 queryMaxSeqNumber = `${BASE_CUSTOMER_URL}organizations/current-max-sequence`; let responseMaxSequence = await fetch(queryMaxSeqNumber, { method: 'GET', headers: headers }); let dataMaxSequence = await responseMaxSequence.json(); console.log('Maximum sequence number: ', dataMaxSequence); } 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 itemdata = await request.json(); let results = itemdata.results; if (results.length == 0) { console.log('No more trade items to sync'); break; } } } } export { getClientToken, updateClientConfig, getJWT, sleep, asyncQueue, syncSequence, syncSuppliers, syncTradeItems };