Merge pull request '#4823-changes' (#4) from #4823-changes into master

Reviewed-on: #4
This commit is contained in:
Guillermo Bonet 2023-04-04 18:16:25 +00:00
commit 76029cda3c
8 changed files with 617 additions and 258 deletions

View File

@ -1,7 +1,5 @@
# Floriday
Requires [Node.js](https://nodejs.org/en/) v15.14.0 or higher.
The Floriday service project should perform the following tasks:
1. Create / mantain the table structure to allow the storage of the data provided by the Floriday API.
@ -12,13 +10,51 @@ The Floriday service project should perform the following tasks:
This is done using the [Sequelize](https://sequelize.org/) ORM and storing the data as it is received from the API,
so it can be compared with the previous data.
## Requeriments
* Git
* Nodejs (v15.14.0 or higher)
## Installation
Pull from repository.
Run this commands on project root directory to install Node dependencies.
```bash
npm i
```
### .env file template
```bash
#FLORIDAY DATA
CLIENT_ID = xxxxxxxxxx
CLIENT_SECRET = xxxxxxxxxx
API_KEY = xxxxxxxxxx
#SEQUELIZE CONFIG
DB_SCHEMA = schema
DB_USER = root
DB_PWD = root
DB_HOST = localhost
DB_DIALECT = mariadb
#GENERAL CONFIG
IS_PRODUCTION = false
SECRETS = true
FORCE_SYNC = true
SYNC_SEQUENCE = true
SYNC_SUPPLIER = true
SYNC_TRADEITEM = true
```
## Guidelines
- In case a new model is created, it should follow the following structure:
- /models
- index.js
- main.js
- foo.js
### Foo.js
@ -51,7 +87,7 @@ export default (sequelize) => {
};
```
### Index.js
### main.js
```javascript
import foo from "./foo";
@ -62,32 +98,7 @@ let models = {
```
To install dependencies:
## Built With
```bash
npm install
```
To run:
```bash
npm start # run the service
npm dev-sync # run and create the db structure
```
### .env file
```bash
# FLORIDAY SERVICE CONFIG
CLIENT_ID = floriday-client_id
CLIENT_SECRET = floriday-client_secret
STATUS = production_or_development
# SEQUELIZE CONFIG
DB_SCHEMA = edi
DB_USER = root
DB_PWD = root
DB_HOST = localhost
DB_DIALECT = mariadb
FORCE_SYNC = false
```
* [nodejs](https://nodejs.org/)
* [sequalize](https://sequelize.org/)

View File

@ -1,51 +0,0 @@
import moment from 'moment';
import * as vnUtils from './utils.js';
//import cliProgress from 'cli-progress';
import dotenv from 'dotenv';
dotenv.config();
import { models } from './models/index.js';
console.log = function () {
let args = Array.prototype.slice.call(arguments);
args.unshift(new moment().format('HH:mm:ss') + ' -');
console.info.apply(console, args);
};
let tokenExpirationDate = await vnUtils.getClientToken(models);
process.env.SYNC_SEQUENCE ? await vnUtils.syncSequence() : null;
process.env.SYNC_SUPPLIER ? await vnUtils.syncSuppliers() : null;
await vnUtils.syncConnections();
process.env.SYNC_TRADEITEM ? await vnUtils.syncTradeItems() : null;
console.log('Synced trade items');
try {
// eslint-disable-next-line no-constant-condition
while (true) {
try{
console.log('Querying the API to check for new data...');
console.log('Current token expiration date: ', tokenExpirationDate);
if (moment().isAfter(tokenExpirationDate)) {
console.log('Token expired, getting a new one...');
tokenExpirationDate = await vnUtils.getClientToken(models);
}
await vnUtils.syncSupplyLines();
} catch (error) {
console.error(error);
}
if (process.env.STATUS == 'development') {
await vnUtils.sleep(120000);
} else {
await vnUtils.sleep(300000);
}
}
} catch (error) {
console.error('Unable to connect to the database:', error);
}

60
main.js Normal file
View File

@ -0,0 +1,60 @@
import * as vnUtils from './utils.js';
import { models } from './models/index.js';
import moment from 'moment';
// Añade la hora a todos los console.log
// console.log = (...args) => console.info(`${new moment().format('HH:mm:ss')} -`, ...args);
async function main() {
try {
const env = process.env;
let tokenExpirationDate = await vnUtils.getClientToken(models);
if (JSON.parse(env.SYNC_SEQUENCE)) await vnUtils.syncSequence()
if (JSON.parse(env.SYNC_SUPPLIER)) await vnUtils.syncSuppliers();
await vnUtils.syncConnections();
if (JSON.parse(env.SYNC_TRADEITEM)) await vnUtils.syncTradeItems();
console.log('Synced trade items');
try {
while (true) {
try{
console.log('Querying the API to check for new data...');
if (moment().isAfter(tokenExpirationDate)) {
console.log('Token expired, getting a new one...');
tokenExpirationDate = await vnUtils.getClientToken(models);
}
await vnUtils.syncSupplyLines();
} catch (err) {
console.error(err);
}
if (JSON.parse(env.IS_PRODUCTION))
await vnUtils.sleep(300000);
else
await vnUtils.sleep(120000);
}
} catch (err) {
myErr(err);
}
} catch (err) {
myErr(err);
}
}
/**
* Creates the connection to the database.
*
* @param {err}
**/
function myErr(err) {
console.log(`[ERROR]`.red.bold, `${err.name}: ${err.message}`.red);
process.exit();
}
main()

View File

@ -1,8 +1,29 @@
import { Sequelize } from 'sequelize';
import dotenv from 'dotenv';
import colors from 'colors';
import ora from 'ora';
dotenv.config();
console.clear()
console.log(
`
`.green
)
let sequelize = createConnection();
const conSpinner = ora('Creating connection...').start();
try {
await sequelize.authenticate();
conSpinner.succeed();
} catch (err) {
conSpinner.fail();
myErr(err);
}
// Supply Line Models
import supplyLine from './supplyLine/supplyLine.js';
@ -26,9 +47,6 @@ import photos from './tradeItem/photos.js';
import seasonalPeriod from './tradeItem/seasonalPeriod.js';
import characteristics from './tradeItem/characteristics.js';
/**
* Contains all the models that are related to the application.
*
@ -142,24 +160,45 @@ models.tradeItem.belongsTo(models.supplier, {
targetKey: 'organizationId',
});
if (process.env.FORCE_SYNC === 'true') {
console.log('Forcing the models...');
await sequelize.sync({ force: true });
let action, isForce;
if (JSON.parse(process.env.FORCE_SYNC)) {
action = 'Forcing'
isForce = true
} else {
console.log('Altering the models...');
await sequelize.sync({ alter: true });
action = 'Altering'
isForce = false
}
if (process.env.SECRETS) {
await models.clientConfig.findOrCreate({
where: {
id: 1,
},
defaults: {
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
},
});
const modSpinner = ora(`${action} models...`).start();
try {
await sequelize.sync({ force: isForce });
if (process.env.SECRETS) {
await models.clientConfig.findOrCreate({
where: {
id: 1,
},
defaults: {
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
},
});
}
modSpinner.succeed();
}
catch (err) {
modSpinner.fail();
myErr(err);
}
/**
* Creates the connection to the database.
*
* @param {err}
**/
function myErr(err) {
console.log(`[ERROR]`.red.bold, `${err.name}: ${err.message}`.red);
process.exit();
}
/**
@ -171,7 +210,6 @@ if (process.env.SECRETS) {
* @returns {Sequelize} Sequelize instance with the connection to the database.
*/
function createConnection() {
console.log('Creating the connection...');
return new Sequelize(process.env.DB_SCHEMA, process.env.DB_USER, process.env.DB_PWD, {
host: process.env.DB_HOST,
dialect: process.env.DB_DIALECT,
@ -185,4 +223,4 @@ function createConnection() {
});
}
export { models } ;
export { models } ;

View File

@ -1,9 +1,15 @@
import { Sequelize } from 'sequelize';
const suppliers = {
isConnected: {
type: Sequelize.BOOLEAN,
defaultValue: false,
sequenceNumber: {
type: Sequelize.INTEGER,
allowNull: false,
},
companyGln: {
type: Sequelize.STRING,
},
name: {
type: Sequelize.STRING,
},
commercialName: {
type: Sequelize.STRING,
@ -18,6 +24,19 @@ const suppliers = {
website: {
type: Sequelize.STRING,
},
organizationId: {
type: Sequelize.STRING,
primaryKey: true,
},
rfhRelationId: {
type: Sequelize.INTEGER,
},
paymentProviders: {
type: Sequelize.STRING,
},
endDate: {
type: Sequelize.DATE,
},
mailingAddress: {
type: Sequelize.JSON,
},
@ -27,31 +46,12 @@ const suppliers = {
pythosanitaryNumber: {
type: Sequelize.STRING,
},
sequenceNumber: {
type: Sequelize.INTEGER,
allowNull: false,
},
organizationId: {
type: Sequelize.STRING,
primaryKey: true,
},
companyGln: {
type: Sequelize.STRING,
},
name: {
type: Sequelize.STRING,
},
endDate: {
type: Sequelize.DATE,
},
rfhRelationId: {
type: Sequelize.INTEGER,
},
organizationType: {
type: Sequelize.STRING,
},
paymentProviders: {
type: Sequelize.STRING,
isConnected: {
type: Sequelize.BOOLEAN,
defaultValue: false,
},
};

354
package-lock.json generated
View File

@ -6,12 +6,12 @@
"": {
"name": "floriday",
"dependencies": {
"cli-progress": "^3.11.2",
"colors": "^1.4.0",
"dotenv": "^16.0.3",
"mariadb": "^3.0.2",
"moment": "^2.29.4",
"node-fetch": "^3.3.0",
"ora": "^6.3.0",
"sequelize": "^6.26.0",
"uuid": "^9.0.0"
},
@ -179,6 +179,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -210,6 +211,35 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/bl": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz",
"integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==",
"dependencies": {
"buffer": "^6.0.3",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -220,6 +250,29 @@
"concat-map": "0.0.1"
}
},
"node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@ -245,15 +298,37 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/cli-progress": {
"version": "3.11.2",
"resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.11.2.tgz",
"integrity": "sha512-lCPoS6ncgX4+rJu5bS3F/iCz17kZ9MPZ6dpuTtI0KXKABkhyXIdYB3Inby1OpaGti3YlI3EeEkM9AuWpelJrVA==",
"node_modules/cli-cursor": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
"integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
"dependencies": {
"string-width": "^4.2.3"
"restore-cursor": "^4.0.0"
},
"engines": {
"node": ">=4"
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/cli-spinners": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz",
"integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==",
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/clone": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
"integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
"engines": {
"node": ">=0.8"
}
},
"node_modules/color-convert": {
@ -332,6 +407,17 @@
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true
},
"node_modules/defaults": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
"integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
"dependencies": {
"clone": "^1.0.2"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
@ -365,11 +451,6 @@
"resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz",
"integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg=="
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@ -732,6 +813,25 @@
"node": ">=0.10.0"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/ignore": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@ -787,8 +887,7 @@
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/is-extglob": {
"version": "2.1.1",
@ -799,14 +898,6 @@
"node": ">=0.10.0"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"engines": {
"node": ">=8"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@ -819,6 +910,17 @@
"node": ">=0.10.0"
}
},
"node_modules/is-interactive": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
"integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-path-inside": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
@ -828,6 +930,17 @@
"node": ">=8"
}
},
"node_modules/is-unicode-supported": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz",
"integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@ -907,6 +1020,32 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
},
"node_modules/log-symbols": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz",
"integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==",
"dependencies": {
"chalk": "^5.0.0",
"is-unicode-supported": "^1.1.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/log-symbols/node_modules/chalk": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz",
"integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==",
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/lru-cache": {
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz",
@ -931,6 +1070,14 @@
"node": ">= 12"
}
},
"node_modules/mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"engines": {
"node": ">=6"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -1017,6 +1164,20 @@
"wrappy": "1"
}
},
"node_modules/onetime": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
"dependencies": {
"mimic-fn": "^2.1.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/optionator": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@ -1034,6 +1195,64 @@
"node": ">= 0.8.0"
}
},
"node_modules/ora": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/ora/-/ora-6.3.0.tgz",
"integrity": "sha512-1/D8uRFY0ay2kgBpmAwmSA404w4OoPVhHMqRqtjvrcK/dnzcEZxMJ+V4DUbyICu8IIVRclHcOf5wlD1tMY4GUQ==",
"dependencies": {
"chalk": "^5.0.0",
"cli-cursor": "^4.0.0",
"cli-spinners": "^2.6.1",
"is-interactive": "^2.0.0",
"is-unicode-supported": "^1.1.0",
"log-symbols": "^5.1.0",
"stdin-discarder": "^0.1.0",
"strip-ansi": "^7.0.1",
"wcwidth": "^1.0.1"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ora/node_modules/ansi-regex": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"node_modules/ora/node_modules/chalk": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz",
"integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==",
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/ora/node_modules/strip-ansi": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz",
"integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==",
"dependencies": {
"ansi-regex": "^6.0.1"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@ -1146,6 +1365,19 @@
}
]
},
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/regexpp": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
@ -1167,6 +1399,21 @@
"node": ">=4"
}
},
"node_modules/restore-cursor": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
"integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
"dependencies": {
"onetime": "^5.1.0",
"signal-exit": "^3.0.2"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/retry-as-promised": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-6.1.0.tgz",
@ -1220,6 +1467,25 @@
"queue-microtask": "^1.2.2"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@ -1348,23 +1614,38 @@
"node": ">=8"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
},
"node_modules/stdin-discarder": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz",
"integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
"bl": "^5.0.0"
},
"engines": {
"node": ">=8"
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@ -1440,6 +1721,11 @@
"punycode": "^2.1.0"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
@ -1456,6 +1742,14 @@
"node": ">= 0.10"
}
},
"node_modules/wcwidth": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
"integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
"dependencies": {
"defaults": "^1.0.3"
}
},
"node_modules/web-streams-polyfill": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",

View File

@ -2,20 +2,13 @@
"name": "floriday",
"module": "index.ts",
"type": "module",
"scripts": {
"start": "node index.js",
"dev-sync": "FORCE_SYNC=true node --max-old-space-size=4096 index.js",
"dev-secrets": "SECRETS=true node --max-old-space-size=4096 index.js",
"dev-both": "FORCE_SYNC=true SECRETS=true node --max-old-space-size=4096 index.js",
"dev-query": "QUERYSUPPLIERS=true SECRETS=true node --max-old-space-size=4096 index.js"
},
"dependencies": {
"cli-progress": "^3.11.2",
"colors": "^1.4.0",
"dotenv": "^16.0.3",
"mariadb": "^3.0.2",
"moment": "^2.29.4",
"node-fetch": "^3.3.0",
"ora": "^6.3.0",
"sequelize": "^6.26.0",
"uuid": "^9.0.0"
},

198
utils.js
View File

@ -1,18 +1,15 @@
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 colors from 'colors';
//import cliProgress from 'cli-progress';
dotenv.config();
import ora from 'ora';
/**
* The Endpoint where the Access Token is requested
*/
const _accessTokenEndpoint = 'https://idm.staging.floriday.io/oauth2/ausmw6b47z1BnlHkw0h7/v1/token';
// The Endpoint where the Token is requested
const tokenEndpoint = 'https://idm.staging.floriday.io/oauth2/ausmw6b47z1BnlHkw0h7/v1/token';
const BASE_CUSTOMER_URL = 'https://api.staging.floriday.io/customers-api/2022v2/';
// URL of API
const url = 'https://api.staging.floriday.io/customers-api/2022v2';
/**
* Gets the Access Token from the client config table
@ -24,8 +21,10 @@ async function getClientToken() {
const clientConfigData = await models.clientConfig.findAll();
if (!clientConfigData[0])
throw colors.red.bold('Token has expired')
throw colors.red.bold('No data found in the configuration table, ',
'if you have configured the .env file, declare the variable SECRET as true')
const spinner = ora(`Requesting token...`).start();
const now = moment().format('YYYY-MM-DD HH:mm:ss');
const tokenExpirationDate = clientConfigData[0].tokenExpiration;
@ -33,7 +32,7 @@ async function getClientToken() {
let clientId = clientConfigData[0].clientId;
let clientSecret = clientConfigData[0].clientSecret;
const tokenRequest = await fetch(_accessTokenEndpoint, {
const tokenRequest = await fetch(tokenEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
@ -44,11 +43,11 @@ async function getClientToken() {
const tokenResponse = await tokenRequest.json();
if (tokenRequest.status === 200) {
console.log('Token request successful');
spinner.succeed();
} else {
throw new Error(
`Token request failed with status ${tokenRequest.status}`
);
spinner.fail();
throw new Error(`Token request failed with status: ${tokenRequest.status} - ${tokenRequest.statusText}`);
}
const accessToken = tokenResponse.access_token;
@ -67,7 +66,8 @@ async function getClientToken() {
return tokenExpirationDate;
} else {
console.log('Using the current token...');
spinner.text = 'Using stored token...'
spinner.succeed();
return tokenExpirationDate;
}
}
@ -83,7 +83,7 @@ async function getClientToken() {
*/
async function updateClientConfig(clientId, clientSecret, accessToken, tokenExpirationDate) {
try {
console.log('Updating the client config with the new token...');
const spinner = ora(`Updating config...`).start();
await models.clientConfig.upsert({
id: 1,
clientId: clientId,
@ -92,9 +92,9 @@ async function updateClientConfig(clientId, clientSecret, accessToken, tokenExpi
tokenExpiration: tokenExpirationDate,
requestLimit: 500,
});
console.log('Client config updated, new Token set');
console.log('New token expiration date: ', tokenExpirationDate);
spinner.succeed();
} catch (error) {
spinner.fail();
console.log('There was a error while updating the client config');
console.log(error);
}
@ -168,23 +168,29 @@ async function asyncQueue(fnArray, concurrency = 1) {
* @param {Number} maximumSequenceNumber - maximum sequence number
* @returns
*/
async function syncSequence(current = 0, model = null ,maximumSequenceNumber = 0){
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);
try {
let mockModels = [
'suppliers',
'tradeItems',
'supplyLines',
];
const spinner = ora(`Syncing sequence...`).start();
for (let mockModel in mockModels) {
const element = mockModels[mockModel];
await syncSequence(0, element);
}
spinner.succeed();
} catch (err) {
spinner.fail();
throw(err);
}
} else {
} else if (current) {
let tx = await models.sequelize.transaction();
try {
let sequence = await models.sequenceNumber.findOrCreate({
where: {
model: model
@ -213,63 +219,74 @@ async function syncSequence(current = 0, model = null ,maximumSequenceNumber = 0
} catch (error) {
await tx.rollback();
console.log('Error while syncing sequence number for: ', model);
console.log(`Error while syncing sequence number for: ${model}: ${error}`);
}
}
}
async function syncSuppliers(){
let headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${await getJWT()}`,
'X-Api-Key': process.env.API_KEY
};
let maximumSequenceNumber = await fetch(`${BASE_CUSTOMER_URL}organizations/current-max-sequence`, {
method: 'GET',
headers: headers
});
maximumSequenceNumber = await maximumSequenceNumber.json();
console.log('Maximum sequence number: ', maximumSequenceNumber);
for (let i = 0; i < maximumSequenceNumber; i++) {
let query = `${BASE_CUSTOMER_URL}organizations/sync/${i}?organizationType=SUPPLIER&limit=1000`;
let response = await fetch(query, {
try {
const spinner = ora('Preparing to load suppliers...').start();
let headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${await getJWT()}`,
'X-Api-Key': process.env.API_KEY
};
const response = await fetch(`${url}/organizations/current-max-sequence`, {
method: 'GET',
headers: headers
});
let data = await response.json();
let suppliers = data.results;
for (let supplier of suppliers) {
i = supplier.sequenceNumber;
await models.supplier.upsert({
isConnected: false,
commercialName: supplier.commercialName,
email: supplier.email,
phone: supplier.phone,
website: supplier.website,
mailingAddress: supplier.mailingAddress,
physicalAddress: supplier.physicalAddress,
pythosanitaryNumber: supplier.pythosanitaryNumber,
sequenceNumber: supplier.sequenceNumber,
organizationId: supplier.organizationId,
companyGln: supplier.companyGln,
name: supplier.name,
endDate: supplier.endDate,
rfhRelationId: supplier.rfhRelationId,
organizationType: supplier.organizationType,
paymentProviders: `${supplier.paymentProviders}`,
const maxSequenceNumber = await response.json();
let timeFinish, timeToGoSec, timeToGoMin, timeLeft;
for (let curSequenceNumber = 0; curSequenceNumber <= maxSequenceNumber; curSequenceNumber++) {
let timeStart = new moment();
let query = `${url}/organizations/sync/${curSequenceNumber}?organizationType=SUPPLIER`;
let response = await fetch(query, {
method: 'GET',
headers: headers
});
console.log('INSERTED:\t', supplier.commercialName, '\nsequenceNumber:\t', supplier.sequenceNumber);
let data = await response.json();
let suppliers = data.results;
for (let supplier of suppliers) {
curSequenceNumber = supplier.sequenceNumber;
spinner.text = `Loading suppliers, ${maxSequenceNumber - curSequenceNumber} are missing`
if (timeFinish)
spinner.text = spinner.text + ` (${timeLeft})`
await models.supplier.upsert({
sequenceNumber: supplier.sequenceNumber,
companyGln: supplier.companyGln,
name: supplier.name,
commercialName: supplier.commercialName,
email: supplier.email,
phone: supplier.phone,
website: supplier.website,
organizationId: supplier.organizationId,
rfhRelationId: supplier.rfhRelationId,
paymentProviders: `${supplier.paymentProviders}`,
endDate: supplier.endDate,
mailingAddress: supplier.mailingAddress,
physicalAddress: supplier.physicalAddress,
pythosanitaryNumber: supplier.pythosanitaryNumber,
organizationType: supplier.organizationType
});
}
await syncSequence(curSequenceNumber, 'suppliers', maxSequenceNumber);
timeFinish = new moment();
timeToGoSec = (timeFinish.diff(timeStart, 'seconds') * (maxSequenceNumber - curSequenceNumber) / 1000)
timeToGoMin = Math.trunc(timeToGoSec / 60)
if (!timeToGoMin)
timeLeft = `${Math.trunc(timeToGoSec)} sec`
else
timeLeft = `${timeToGoMin} min`
}
await syncSequence(i, 'suppliers', maximumSequenceNumber);
spinner.succeed()
}
catch (err) {
spinner.fail();
throw(err);
}
}
@ -282,7 +299,7 @@ async function syncConnections(){
'X-Api-Key': process.env.API_KEY
};
let remoteConnections = await fetch(`${BASE_CUSTOMER_URL}connections`, {
let remoteConnections = await fetch(`${url}/connections`, {
method: 'GET',
headers: headers
});
@ -298,7 +315,7 @@ async function syncConnections(){
if (remoteConnection == undefined){
console.log('Connection: ', connection, 'does not exist in the remote server');
console.log('Creating remote connection');
await fetch(`${BASE_CUSTOMER_URL}connections/${connection.organizationId}`, {
await fetch(`${url}/connections/${connection.organizationId}`, {
method: 'PUT',
headers: headers
});
@ -331,27 +348,23 @@ async function syncConnections(){
}
async function syncTradeItems(){
const suppliers = await models.supplier.findAll();
let headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${await getJWT()}`,
'X-Api-Key': process.env.API_KEY
};
let i = 0;
console.log('Syncing trade items');
for (let supplier of suppliers) {
i++;
if (!supplier.isConnected){
console.log('Supplier: ', supplier.commercialName, 'is not connected');
console.log('Skipping supplier', supplier.commercialName, '(', i, '/', suppliers.length, ')');
continue;
}
let query = `${BASE_CUSTOMER_URL}trade-items?supplierOrganizationId=${supplier.organizationId}`;
let query = `${url}/trade-items?supplierOrganizationId=${supplier.organizationId}`;
let headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${await getJWT()}`,
'X-Api-Key': process.env.API_KEY
};
try {
let request = await fetch(query, {
@ -416,7 +429,8 @@ async function syncSupplyLines(){
'Content-Type': 'application/json',
'Authorization': `Bearer ${await getJWT()}`,
'X-Api-Key': process.env.API_KEY
};
};
// Launch a promise for each supplier
for (let tradeItem of tradeItems) {
let supplier = suppliers.find(supplier => supplier.organizationId == tradeItem.supplierOrganizationId);
@ -425,7 +439,7 @@ async function syncSupplyLines(){
let promise = new Promise(async (resolve) => {
try {
let url = `${BASE_CUSTOMER_URL}supply-lines/sync/0?supplierOrganizationId=${supplier.organizationId}&tradeItemId=${tradeItem.tradeItemId}&limit=100&postFilterSelectedTradeItems=false`;
let url = `${url}/supply-lines/sync/0?supplierOrganizationId=${supplier.organizationId}&tradeItemId=${tradeItem.tradeItemId}&limit=100&postFilterSelectedTradeItems=false`;
let request = await fetch(url, {
method: 'GET',
@ -472,7 +486,7 @@ async function syncSupplyLines(){
console.log('Trade item not found for supply line: ', line.supplyLineId);
console.log('Requesting data for trade item id: ', line.tradeItemId);
let urlTradeItem = `${BASE_CUSTOMER_URL}trade-items?tradeItemIds=${line.tradeItemId}`;
let urlTradeItem = `${url}/trade-items?tradeItemIds=${line.tradeItemId}`;
let queryTradeItem = await fetch(urlTradeItem, {
method: 'GET',