diff --git a/.gitignore b/.gitignore index a60b41b70d05fc80340cc59824669e40ab84e290..257fef438d8ff3d29a9502b4ed1d02bc79953a98 100644 --- a/.gitignore +++ b/.gitignore @@ -389,3 +389,6 @@ Temporary Items # Local .env dist + +# Migrations +.migrate \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index fe3585896802626aeef6f70056c64594d82e9e7d..725ec6d07d4dbca4494853cccc6883cdcfa6cdf3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9731,6 +9731,33 @@ } } }, + "migrate": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/migrate/-/migrate-1.7.0.tgz", + "integrity": "sha512-I63YykITgWyI+ET4KO8xGePYkR9U7CtSe/RrR13vLbZSpUcAh4/ry2GswNv7Lywcsp3BaDHj7YdjC7ihVYCFmw==", + "requires": { + "chalk": "^2.4.1", + "commander": "^2.19.0", + "dateformat": "^3.0.3", + "dotenv": "^6.1.0", + "inherits": "^2.0.3", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "slug": "^0.9.2" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "dotenv": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz", + "integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==" + } + } + }, "miller-rabin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", @@ -11975,6 +12002,14 @@ "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" }, + "slug": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/slug/-/slug-0.9.4.tgz", + "integrity": "sha512-3YHq0TeJ4+AIFbJm+4UWSQs5A1mmeWOTQqydW3OoPmQfNKxlO96NDRTIrp+TBkmvEsEFrd+Z/LXw8OD/6OlZ5g==", + "requires": { + "unicode": ">= 0.3.1" + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -13260,6 +13295,11 @@ "debug": "^2.2.0" } }, + "unicode": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/unicode/-/unicode-13.0.0.tgz", + "integrity": "sha512-osNPLT4Lqna/sV6DQikrB8m4WxR61/k0fnhfKnkPGcZImczW3IysRXvWxfdqGUjh0Ju2o/tGGgu46mlfc/cpZw==" + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", diff --git a/package.json b/package.json index c241c3f401eecdef10bc1888cd8b9331afa67f8a..9b6dd0f58f43fc2678cf05629322edd0117b052c 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,10 @@ "test:watch": "jest --watch", "test:cov": "jest --config ./test/jest.json --coverage --ci --reporters=default --reporters=jest-junit", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json" + "test:e2e": "jest --config ./test/jest-e2e.json", + "migrate:create": "migrate create --template-file ./src/migrations-utils/template.ts --migrations-dir=\"./src/migrations\" --compiler=\"ts:./src/migrations-utils/ts-compiler.js\"", + "migrate:up": "migrate --migrations-dir=\"./src/migrations\" --compiler=\"ts:./src/migrations-utils/ts-compiler.js\" up", + "migrate:down": "migrate --migrations-dir=\"./src/migrations\" --compiler=\"ts:./src/migrations-utils/ts-compiler.js\" down" }, "dependencies": { "@nestjs/common": "^7.6.13", @@ -39,6 +42,7 @@ "ejs": "^3.1.5", "form-data": "^3.0.0", "luxon": "^1.25.0", + "migrate": "^1.7.0", "mongoose": "^5.10.15", "passport": "^0.4.1", "passport-jwt": "^4.0.0", diff --git a/src/categories/categories.module.ts b/src/categories/categories.module.ts index 380468396bceb0829ce6ad1a93e5b029a4722799..bc6f3c86841008cb9bed60f0c70bbd517b947403 100644 --- a/src/categories/categories.module.ts +++ b/src/categories/categories.module.ts @@ -19,6 +19,7 @@ import { CategoriesAccompagnement, CategoriesAccompagnementSchema } from './sche ]), ], controllers: [CategoriesFormationsController, CategoriesAccompagnementController, CategoriesOthersController], + exports: [CategoriesFormationsService], providers: [CategoriesFormationsService, CategoriesAccompagnementService, CategoriesOthersService], }) export class CategoriesModule {} diff --git a/src/categories/services/categories-formations.service.ts b/src/categories/services/categories-formations.service.ts index 05ecae4af07f33c273ecdd2ded6a89c734358c8c..71a6c1073ad5245541c651836b272195dd96397c 100644 --- a/src/categories/services/categories-formations.service.ts +++ b/src/categories/services/categories-formations.service.ts @@ -16,4 +16,8 @@ export class CategoriesFormationsService { public async findAll(): Promise<CategoriesFormations[]> { return this.structureModel.find().exec(); } + + public findOne(categoryId: string): Promise<any> { + return this.structureModel.findOne({ id: categoryId }).select({ 'modules.id': 1 }).exec(); + } } diff --git a/src/migrations-utils/db.ts b/src/migrations-utils/db.ts new file mode 100644 index 0000000000000000000000000000000000000000..4a5284a096c12204c8e3d1b9e3b5b620729d0092 --- /dev/null +++ b/src/migrations-utils/db.ts @@ -0,0 +1,11 @@ +import { MongoClient } from 'mongodb'; +import { config } from 'dotenv'; + +export const getDb = async () => { + config(); // Required for reading .env + const client: MongoClient = await MongoClient.connect( + `mongodb://${process.env.MONGO_NON_ROOT_USERNAME}:${process.env.MONGO_NON_ROOT_PASSWORD}@${process.env.MONGO_DB_HOST_AND_PORT}/ram`, + { useUnifiedTopology: true } + ); + return client.db(); +}; diff --git a/src/migrations-utils/template.ts b/src/migrations-utils/template.ts new file mode 100644 index 0000000000000000000000000000000000000000..d3f00960db09db7b37081642dadc328fa876ce13 --- /dev/null +++ b/src/migrations-utils/template.ts @@ -0,0 +1,13 @@ +import { Db } from 'mongodb'; +import { getDb } from '../migrations-utils/db'; + +export const up = async () => { + const db: Db = await getDb(); +}; + +export const down = async () => { + const db: Db = await getDb(); + /* + Code you downgrade script here! + */ +}; diff --git a/src/migrations-utils/ts-compiler.js b/src/migrations-utils/ts-compiler.js new file mode 100644 index 0000000000000000000000000000000000000000..1424f7e7c44aef954e45570d48ac921490e0f583 --- /dev/null +++ b/src/migrations-utils/ts-compiler.js @@ -0,0 +1,3 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const tsNode = require('ts-node'); +module.exports = tsNode.register; diff --git a/src/migrations/1617284203579-apticid.ts b/src/migrations/1617284203579-apticid.ts new file mode 100644 index 0000000000000000000000000000000000000000..fe85bfe04310d30c97fca8b0e2edc7ab69a47245 --- /dev/null +++ b/src/migrations/1617284203579-apticid.ts @@ -0,0 +1,394 @@ +import { getDb } from '../migrations-utils/db'; + +class ApticModule { + id: string; + display_id: string; + display_name: string; + url: string; + last_update_time: string; + receipt_time: string; +} + +class ApticDoc { + id: string; + modules: ApticModule[]; +} + +export const up = async () => { + const db = await getDb(); + await updateStructuresId(db); + await updateApticReferential(db); +}; + +export const down = async () => { + const db = await getDb(); + await downgradeApticReferential(db); + await downgradeStructuresId(db); +}; + +async function downgradeStructuresId(db) { + await db + .collection('structures') + .find({}) + .forEach((doc) => { + const newDoc = downgradeStructure(doc); + db.collection('structures').updateMany({ _id: doc._id }, [{ $set: newDoc }]); + }); +} + +async function updateStructuresId(db) { + await db + .collection('structures') + .find({}) + .forEach((doc) => { + const newDoc = updateStructure(doc); + db.collection('structures').updateMany({ _id: doc._id }, [{ $set: newDoc }]); + }); +} + +function updateStructure(structure): any { + let newArray = []; + // Social and professional + newArray.push(switchStructureId(structure.socialAndProfessional, '254', '6')); + newArray.push(switchStructureId(structure.socialAndProfessional, '240', '20')); + newArray.push(switchStructureId(structure.socialAndProfessional, '194', '66')); + newArray.push(switchStructureId(structure.socialAndProfessional, '193', '67')); + newArray.push(switchStructureId(structure.socialAndProfessional, '192', '68')); + newArray.push(switchStructureId(structure.socialAndProfessional, '191', '69')); + newArray.push(switchStructureId(structure.socialAndProfessional, '262', '124')); + newArray.push(switchStructureId(structure.socialAndProfessional, '263', '125')); + newArray.push(switchStructureId(structure.socialAndProfessional, '003', '127')); + // Remove null cases + newArray = newArray.filter((obj) => obj); + structure.socialAndProfessional = newArray; + + // Base Skills + newArray = []; + newArray.push(switchStructureId(structure.baseSkills, '260', '260')); //TODO: + newArray.push(switchStructureId(structure.baseSkills, '259', '1')); + newArray.push(switchStructureId(structure.baseSkills, '261', '11')); + newArray.push(switchStructureId(structure.baseSkills, '222', '38')); + newArray.push(switchStructureId(structure.baseSkills, '212', '48')); + newArray.push(switchStructureId(structure.baseSkills, '186', '74')); + newArray.push(switchStructureId(structure.baseSkills, '183', '77')); + // Remove null cases + newArray = newArray.filter((obj) => obj); + structure.baseSkills = newArray; + + // Access Right + newArray = []; + newArray.push(switchStructureId(structure.accessRight, '176', '84')); + newArray.push(switchStructureId(structure.accessRight, '175', '85')); + newArray.push(switchStructureId(structure.accessRight, '174', '86')); + newArray.push(switchStructureId(structure.accessRight, '173', '87')); + newArray.push(switchStructureId(structure.accessRight, '172', '88')); + newArray.push(switchStructureId(structure.accessRight, '171', '89')); + newArray.push(switchStructureId(structure.accessRight, '167', '93')); + newArray.push(switchStructureId(structure.accessRight, '165', '95')); + // Remove null cases + newArray = newArray.filter((obj) => obj); + structure.accessRight = newArray; + + // Parenting + newArray = []; + newArray.push(switchStructureId(structure.parentingHelp, '257', '3')); + newArray.push(switchStructureId(structure.parentingHelp, '238', '22')); + newArray.push(switchStructureId(structure.parentingHelp, '178', '82')); + newArray.push(switchStructureId(structure.parentingHelp, '166', '94')); + // Remove null cases + newArray = newArray.filter((obj) => obj); + structure.parentingHelp = newArray; + + // Digital Security + newArray = []; + newArray.push(switchStructureId(structure.digitalCultureSecurity, '264', '2')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '255', '5')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '265', '9')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '232', '28')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '225', '34')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '221', '39')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '218', '42')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '209', '51')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '208', '52')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '206', '54')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '195', '65')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '164', '96')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '163', '97')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '162', '98')); + // Remove null cases + newArray = newArray.filter((obj) => obj); + structure.digitalCultureSecurity = newArray; + + return structure; +} + +function downgradeStructure(structure): any { + let newArray = []; + // Social and professional + newArray.push(switchStructureId(structure.socialAndProfessional, '6', '254')); + newArray.push(switchStructureId(structure.socialAndProfessional, '20', '240')); + newArray.push(switchStructureId(structure.socialAndProfessional, '66', '194')); + newArray.push(switchStructureId(structure.socialAndProfessional, '67', '193')); + newArray.push(switchStructureId(structure.socialAndProfessional, '68', '192')); + newArray.push(switchStructureId(structure.socialAndProfessional, '69', '191')); + newArray.push(switchStructureId(structure.socialAndProfessional, '124', '262')); + newArray.push(switchStructureId(structure.socialAndProfessional, '125', '263')); + newArray.push(switchStructureId(structure.socialAndProfessional, '127', '003')); + // Remove null cases + newArray = newArray.filter((obj) => obj); + structure.socialAndProfessional = newArray; + + // Base Skills + newArray = []; + newArray.push(switchStructureId(structure.baseSkills, '260', '260')); //TODO: + newArray.push(switchStructureId(structure.baseSkills, '1', '259')); + newArray.push(switchStructureId(structure.baseSkills, '11', '261')); + newArray.push(switchStructureId(structure.baseSkills, '38', '222')); + newArray.push(switchStructureId(structure.baseSkills, '48', '212')); + newArray.push(switchStructureId(structure.baseSkills, '74', '186')); + newArray.push(switchStructureId(structure.baseSkills, '77', '183')); + // Remove null cases + newArray = newArray.filter((obj) => obj); + structure.baseSkills = newArray; + + // Access Right + newArray = []; + newArray.push(switchStructureId(structure.accessRight, '84', '176')); + newArray.push(switchStructureId(structure.accessRight, '85', '175')); + newArray.push(switchStructureId(structure.accessRight, '86', '174')); + newArray.push(switchStructureId(structure.accessRight, '87', '173')); + newArray.push(switchStructureId(structure.accessRight, '88', '172')); + newArray.push(switchStructureId(structure.accessRight, '89', '171')); + newArray.push(switchStructureId(structure.accessRight, '93', '167')); + newArray.push(switchStructureId(structure.accessRight, '95', '165')); + // Remove null cases + newArray = newArray.filter((obj) => obj); + structure.accessRight = newArray; + + // Parenting + newArray = []; + newArray.push(switchStructureId(structure.parentingHelp, '3', '257')); + newArray.push(switchStructureId(structure.parentingHelp, '22', '238')); + newArray.push(switchStructureId(structure.parentingHelp, '82', '178')); + newArray.push(switchStructureId(structure.parentingHelp, '94', '166')); + // Remove null cases + newArray = newArray.filter((obj) => obj); + structure.parentingHelp = newArray; + + // Digital Security + newArray = []; + newArray.push(switchStructureId(structure.digitalCultureSecurity, '2', '264')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '5', '255')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '9', '265')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '28', '232')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '34', '225')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '39', '221')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '42', '218')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '51', '209')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '52', '208')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '54', '206')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '65', '195')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '96', '164')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '97', '163')); + newArray.push(switchStructureId(structure.digitalCultureSecurity, '98', '162')); + // Remove null cases + newArray = newArray.filter((obj) => obj); + structure.digitalCultureSecurity = newArray; + + return structure; +} + +async function updateApticReferential(db) { + // Base Skills + let newModule = { modules: [] }; + const baseSkills: ApticDoc = await db.collection('categoriesformations').findOne({ id: 'baseSkills' }); + newModule.modules.push(switchId(baseSkills.modules, '260', '260')); //TODO: + newModule.modules.push(switchId(baseSkills.modules, '259', '1')); + newModule.modules.push(switchId(baseSkills.modules, '261', '11')); + newModule.modules.push(switchId(baseSkills.modules, '222', '38')); + newModule.modules.push(switchId(baseSkills.modules, '212', '48')); + newModule.modules.push(switchId(baseSkills.modules, '186', '74')); + newModule.modules.push(switchId(baseSkills.modules, '183', '77')); + db.collection('categoriesformations').findOneAndUpdate( + { id: 'baseSkills' }, + { $set: { modules: newModule.modules } } + ); + + // Access Right + newModule = { modules: [] }; + const accessRight: ApticDoc = await db.collection('categoriesformations').findOne({ id: 'accessRight' }); + newModule.modules.push(switchId(accessRight.modules, '176', '84')); + newModule.modules.push(switchId(accessRight.modules, '175', '85')); + newModule.modules.push(switchId(accessRight.modules, '174', '86')); + newModule.modules.push(switchId(accessRight.modules, '173', '87')); + newModule.modules.push(switchId(accessRight.modules, '172', '88')); + newModule.modules.push(switchId(accessRight.modules, '171', '89')); + newModule.modules.push(switchId(accessRight.modules, '167', '93')); + newModule.modules.push(switchId(accessRight.modules, '165', '95')); + db.collection('categoriesformations').findOneAndUpdate( + { id: 'accessRight' }, + { $set: { modules: newModule.modules } } + ); + + // Parenting + newModule = { modules: [] }; + const parentingHelp: ApticDoc = await db.collection('categoriesformations').findOne({ id: 'parentingHelp' }); + newModule.modules.push(switchId(parentingHelp.modules, '257', '3')); + newModule.modules.push(switchId(parentingHelp.modules, '238', '22')); + newModule.modules.push(switchId(parentingHelp.modules, '178', '82')); + newModule.modules.push(switchId(parentingHelp.modules, '166', '94')); + db.collection('categoriesformations').findOneAndUpdate( + { id: 'parentingHelp' }, + { $set: { modules: newModule.modules } } + ); + + // Social and professional + newModule = { modules: [] }; + const socialAndProfessional: ApticDoc = await db + .collection('categoriesformations') + .findOne({ id: 'socialAndProfessional' }); + newModule.modules.push(switchId(socialAndProfessional.modules, '254', '6')); + newModule.modules.push(switchId(socialAndProfessional.modules, '240', '20')); + newModule.modules.push(switchId(socialAndProfessional.modules, '194', '66')); + newModule.modules.push(switchId(socialAndProfessional.modules, '193', '67')); + newModule.modules.push(switchId(socialAndProfessional.modules, '192', '68')); + newModule.modules.push(switchId(socialAndProfessional.modules, '191', '69')); + newModule.modules.push(switchId(socialAndProfessional.modules, '262', '124')); + newModule.modules.push(switchId(socialAndProfessional.modules, '263', '125')); + newModule.modules.push(switchId(socialAndProfessional.modules, '003', '127')); + db.collection('categoriesformations').findOneAndUpdate( + { id: 'socialAndProfessional' }, + { $set: { modules: newModule.modules } } + ); + + // Digital security + newModule = { modules: [] }; + const digitalCultureSecurity: ApticDoc = await db + .collection('categoriesformations') + .findOne({ id: 'digitalCultureSecurity' }); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '264', '2')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '255', '5')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '265', '9')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '232', '28')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '225', '34')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '221', '39')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '218', '42')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '209', '51')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '208', '52')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '206', '54')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '195', '65')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '164', '96')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '163', '97')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '162', '98')); + db.collection('categoriesformations').findOneAndUpdate( + { id: 'digitalCultureSecurity' }, + { $set: { modules: newModule.modules } } + ); +} + +async function downgradeApticReferential(db) { + // Base Skills + let newModule = { modules: [] }; + const baseSkills: ApticDoc = await db.collection('categoriesformations').findOne({ id: 'baseSkills' }); + newModule.modules.push(switchId(baseSkills.modules, '260', '260')); //TODO: + newModule.modules.push(switchId(baseSkills.modules, '1', '259')); + newModule.modules.push(switchId(baseSkills.modules, '11', '261')); + newModule.modules.push(switchId(baseSkills.modules, '38', '222')); + newModule.modules.push(switchId(baseSkills.modules, '48', '212')); + newModule.modules.push(switchId(baseSkills.modules, '74', '186')); + newModule.modules.push(switchId(baseSkills.modules, '77', '183')); + db.collection('categoriesformations').findOneAndUpdate( + { id: 'baseSkills' }, + { $set: { modules: newModule.modules } } + ); + + // Access Right + newModule = { modules: [] }; + const accessRight: ApticDoc = await db.collection('categoriesformations').findOne({ id: 'accessRight' }); + newModule.modules.push(switchId(accessRight.modules, '84', '176')); + newModule.modules.push(switchId(accessRight.modules, '85', '175')); + newModule.modules.push(switchId(accessRight.modules, '86', '174')); + newModule.modules.push(switchId(accessRight.modules, '87', '173')); + newModule.modules.push(switchId(accessRight.modules, '88', '172')); + newModule.modules.push(switchId(accessRight.modules, '89', '171')); + newModule.modules.push(switchId(accessRight.modules, '93', '167')); + newModule.modules.push(switchId(accessRight.modules, '95', '165')); + db.collection('categoriesformations').findOneAndUpdate( + { id: 'accessRight' }, + { $set: { modules: newModule.modules } } + ); + + // Parenting + newModule = { modules: [] }; + const parentingHelp: ApticDoc = await db.collection('categoriesformations').findOne({ id: 'parentingHelp' }); + newModule.modules.push(switchId(parentingHelp.modules, '3', '257')); + newModule.modules.push(switchId(parentingHelp.modules, '22', '238')); + newModule.modules.push(switchId(parentingHelp.modules, '82', '178')); + newModule.modules.push(switchId(parentingHelp.modules, '94', '166')); + db.collection('categoriesformations').findOneAndUpdate( + { id: 'parentingHelp' }, + { $set: { modules: newModule.modules } } + ); + + // Social and professional + newModule = { modules: [] }; + const socialAndProfessional: ApticDoc = await db + .collection('categoriesformations') + .findOne({ id: 'socialAndProfessional' }); + newModule.modules.push(switchId(socialAndProfessional.modules, '6', '254')); + newModule.modules.push(switchId(socialAndProfessional.modules, '20', '240')); + newModule.modules.push(switchId(socialAndProfessional.modules, '66', '194')); + newModule.modules.push(switchId(socialAndProfessional.modules, '67', '193')); + newModule.modules.push(switchId(socialAndProfessional.modules, '68', '192')); + newModule.modules.push(switchId(socialAndProfessional.modules, '69', '191')); + newModule.modules.push(switchId(socialAndProfessional.modules, '124', '262')); + newModule.modules.push(switchId(socialAndProfessional.modules, '125', '263')); + newModule.modules.push(switchId(socialAndProfessional.modules, '127', '003')); + db.collection('categoriesformations').findOneAndUpdate( + { id: 'socialAndProfessional' }, + { $set: { modules: newModule.modules } } + ); + + // Digital security + newModule = { modules: [] }; + const digitalCultureSecurity: ApticDoc = await db + .collection('categoriesformations') + .findOne({ id: 'digitalCultureSecurity' }); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '2', '264')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '5', '255')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '9', '265')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '28', '232')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '34', '225')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '39', '221')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '42', '218')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '51', '209')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '52', '208')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '54', '206')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '65', '195')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '96', '164')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '97', '163')); + newModule.modules.push(switchId(digitalCultureSecurity.modules, '98', '162')); + db.collection('categoriesformations').findOneAndUpdate( + { id: 'digitalCultureSecurity' }, + { $set: { modules: newModule.modules } } + ); +} + +function switchStructureId(moduleArray: [], originalId: string, newId: string): string { + const module = moduleArray.find((id) => id === originalId); + if (module) { + return newId; + } + return null; +} + +function switchId(moduleArray: ApticModule[], originalId: string, newId: string): ApticModule { + const module = moduleArray.find((skill) => skill.id === originalId); + module.id = newId; + module.display_id = newId; + module.display_name = `Modules APTIC - n°${newId}`; + delete module.receipt_time; + delete module.last_update_time; + delete module.url; + return module; +} diff --git a/src/structures/schemas/aptic-catalog.schema.ts b/src/structures/schemas/aptic-catalog.schema.ts new file mode 100644 index 0000000000000000000000000000000000000000..fa98f194fb72dbf15ae1888b07753845d97c1614 --- /dev/null +++ b/src/structures/schemas/aptic-catalog.schema.ts @@ -0,0 +1,4 @@ +export class ApticCatalog { + code: string; + label: string; +} diff --git a/src/structures/schemas/aptic-structure.schema.ts b/src/structures/schemas/aptic-structure.schema.ts index afb95430cb303041c529327f43d417c6d46792b3..cd8b6b56dcb6af349084b8c7cec68346e01853e6 100644 --- a/src/structures/schemas/aptic-structure.schema.ts +++ b/src/structures/schemas/aptic-structure.schema.ts @@ -1,43 +1,37 @@ -export class ApticStructure { - presence_id: string; - - presence_name: string; - - presence_phone: string; - - presence_address: string; - - organization_id: string; - - organization_legal_status: string; - - organization_type: string; +import { ApticCatalog } from './aptic-catalog.schema'; - gps_lat: number; - - gps_lng: number; - - postal_code: string; - - city: string; - - city_lat: number; - - city_lng: number; +export class ApticStructure { + id: string; - department: string; + name: string; - department_code: string; + phone: string; - region_name: string; + website: string; - region_code: string; + address: { + main: string; + extra: string; + zip: string; + city: string; + gpsLat: number; + gpsLng: number; + department: string; + departmentCode: string; + region: string; + regionCode: string; + }; - catalog_id: string; + organization: { + id: string; + name: string; + type: string; + legalStatus: string; + }; - service_count: number; + catalogs: string[]; - created: string; + services: ApticCatalog[]; - updated: string; + serviceCount: number; } diff --git a/src/structures/services/aptic-structures.service.ts b/src/structures/services/aptic-structures.service.ts index fff5c3326547acc5f68bebbc93d0e81ef050b198..87ab19d74d06d59d56c7adfe46afe198c044c1ab 100644 --- a/src/structures/services/aptic-structures.service.ts +++ b/src/structures/services/aptic-structures.service.ts @@ -11,47 +11,63 @@ import { Structure, StructureDocument } from '../schemas/structure.schema'; import { ApticStructure } from '../schemas/aptic-structure.schema'; import { Address } from '../schemas/address.schema'; import { UsersService } from '../../users/users.service'; +import { ApticCatalog } from '../schemas/aptic-catalog.schema'; +import { CategoriesFormationsService } from '../../categories/services/categories-formations.service'; @Injectable() export class ApticStructuresService { constructor( private readonly httpService: HttpService, private readonly userService: UsersService, + private readonly categoriesFormationsService: CategoriesFormationsService, @InjectModel(Structure.name) private structureModel: Model<StructureDocument> ) {} - public formatApticStructures(postalCodeData: any[]): any { + /** + * Get all aptic structures in the given postal codes. + * @param postalCodeData + */ + public formatApticStructures(postalCodeData: any[]): void { // Get all postal code in one array - const postalCodeArray = _.flatten( - postalCodeData.map((data) => { - return data.codesPostaux; - }) - ); + const postalCodeArray = _.flatten(postalCodeData.map((data) => data.codesPostaux)); // Call APTIC Api's - const postalCodePromises = postalCodeArray.map((postalCode) => { - return this.getApticStructures(postalCode).toPromise(); - }); - - Promise.all(postalCodePromises).then((data) => { - const structuresData = _.flatten( - data.map((tmp: { data }) => { - return tmp.data.data; - }) + postalCodeArray.map((postalCode) => { + return this.getApticStructures(postalCode).subscribe( + (res) => { + res.data.presencePoints.forEach((structure) => { + // Call aptic api for offer + this.getApticStructureOffer(structure.catalogs[0]).subscribe( + (serviceData) => { + structure.services = serviceData.data.services; + // Create structure + this.createApticStructures(structure); + }, + (err) => { + Logger.log(err); + } + ); + }); + }, + (err) => { + Logger.log(`getApticStructures error on postal code: ${postalCode}. Code: ${err}`); + } ); - // Create structures if possible - structuresData.forEach((structure) => this.createApticStructures(structure)); }); } + /** + * Create a structure for app database given an aptic structure + * @param structure ApticStructure + */ private async createApticStructures(structure: ApticStructure): Promise<any> { - this.structureAlreadyExist(structure).then((exist) => { + this.structureAlreadyExist(structure).then(async (exist) => { if (!exist) { - Logger.log(`Create structure : ${structure.presence_name}`, 'ApticStructuresService - createApticStructures'); + Logger.log(`Create structure : ${structure.name}`, 'ApticStructuresService - createApticStructures'); const createdStructure = new this.structureModel(); // Known fields - createdStructure.structureName = structure.presence_name; - createdStructure.contactPhone = structure.presence_phone; + createdStructure.structureName = structure.name; + createdStructure.contactPhone = structure.phone; // Unkown fields (but mandatory) createdStructure.contactMail = 'unknown@unknown.com'; createdStructure.labelsQualifications = ['passNumerique']; @@ -67,8 +83,14 @@ export class ApticStructuresService { createdStructure.nbTablets = null; createdStructure.nbNumericTerminal = null; // Address - createdStructure.coord = [structure.gps_lng, structure.gps_lat]; + createdStructure.coord = [structure.address.gpsLng, structure.address.gpsLat]; createdStructure.address = this.formatAddress(structure); + // Set structure offer + createdStructure.parentingHelp = await this.setModules(structure, 'parentingHelp'); + createdStructure.baseSkills = await this.setModules(structure, 'baseSkills'); + createdStructure.accessRight = await this.setModules(structure, 'accessRight'); + createdStructure.socialAndProfessional = await this.setModules(structure, 'socialAndProfessional'); + createdStructure.digitalCultureSecurity = await this.setModules(structure, 'digitalCultureSecurity'); createdStructure.save(); // Send admin weird structure mail this.verifyDuplication(createdStructure); @@ -76,23 +98,74 @@ export class ApticStructuresService { }); } + /** + * Given an aptic structure, this method return the corresponding services id by category. + * @param structure ApticStructure + * @param moduleCategory string + * @returns Promise<string[]> + */ + private async setModules(structure: ApticStructure, moduleCategory: string): Promise<string[]> { + const referentialIds = await this.categoriesFormationsService + .findOne(moduleCategory) + .then((referential) => referential.modules.map((element) => element.id)); + + return structure.services + .filter((service) => referentialIds.includes(service.code)) + .map((service) => { + return service.code; + }); + } + + /** + * Verifiy if an aptic structure already exist in database. + * - If it's true return true and update fields if needed + * - If it's false return false + * @param structure ApticStructure + * @returns boolean + */ private async structureAlreadyExist(structure: ApticStructure): Promise<boolean> { let existingStructure = await this.structureModel .findOne({ - structureName: { $regex: structure.presence_name, $options: 'i' }, + structureName: { $regex: structure.name, $options: 'i' }, }) .exec(); // Check without regex for case like 'TINEBRA*DANIEL/DANIEL/' if (!existingStructure) { - existingStructure = await this.structureModel.findOne({ structureName: structure.presence_name }).exec(); + existingStructure = await this.structureModel.findOne({ structureName: structure.name }).exec(); } if (existingStructure) { // Add aptic label if it's not the case if (!existingStructure.labelsQualifications.includes('passNumerique')) { existingStructure.labelsQualifications.push('passNumerique'); - existingStructure.save(); } + // Update service offer + existingStructure.parentingHelp = _.unionWith( + existingStructure.parentingHelp, + await this.setModules(structure, 'parentingHelp'), + _.isEqual + ); + existingStructure.baseSkills = _.unionWith( + existingStructure.baseSkills, + await this.setModules(structure, 'baseSkills'), + _.isEqual + ); + existingStructure.accessRight = _.unionWith( + existingStructure.accessRight, + await this.setModules(structure, 'accessRight'), + _.isEqual + ); + existingStructure.socialAndProfessional = _.unionWith( + existingStructure.socialAndProfessional, + await this.setModules(structure, 'socialAndProfessional'), + _.isEqual + ); + existingStructure.digitalCultureSecurity = _.unionWith( + existingStructure.digitalCultureSecurity, + await this.setModules(structure, 'digitalCultureSecurity'), + _.isEqual + ); + existingStructure.save(); return true; } return false; @@ -128,11 +201,21 @@ export class ApticStructuresService { return this.httpService.get(encodeURI(req)); } - public getApticStructures(postalCodeData: string): Observable<AxiosResponse<any>> { + public getApticStructures(postalCodeData: string): Observable<AxiosResponse<{ presencePoints: ApticStructure[] }>> { + const req = `https://aptisearch-api.aptic.fr/v1/postal-code/${postalCodeData}`; + Logger.log(`Request : ${req}`, 'ApticStructuresService'); + return this.httpService.get(req, { + headers: { + api_key: process.env.APTIC_TOKEN, + }, + }); + } + + public getApticStructureOffer(catalogId: string): Observable<AxiosResponse<{ services: ApticCatalog[] }>> { const agent = new https.Agent({ rejectUnauthorized: false, }); - const req = `https://presence.aptic.fr/postal_code/${postalCodeData}`; + const req = `https://aptisearch-api.aptic.fr/v1/catalog/${catalogId}/services`; Logger.log(`Request : ${req}`, 'ApticStructuresService'); return this.httpService.get(req, { httpsAgent: agent, @@ -161,13 +244,13 @@ export class ApticStructuresService { const address = new Address(); const regexWithSpace = /\d+\s/g; // NOSONAR const regex = /\d+/g; // NOSONAR - if (structure.presence_address.match(regex)) { - address.numero = structure.presence_address.match(regex)[0]; - address.street = structure.presence_address.replace(regexWithSpace, ''); + if (structure.address.main.match(regex)) { + address.numero = structure.address.main.match(regex)[0]; + address.street = structure.address.main.replace(regexWithSpace, ''); } else { - address.street = structure.presence_address; + address.street = structure.address.main; } - address.commune = structure.city; + address.commune = structure.address.city; return address; } } diff --git a/src/structures/structures.module.ts b/src/structures/structures.module.ts index 51ec4d11ca7c6d9640944f69b156cfaac87f1aa3..1445999d289ff826e1504e3754d814f10ed06322 100644 --- a/src/structures/structures.module.ts +++ b/src/structures/structures.module.ts @@ -10,6 +10,7 @@ import { ApticStructuresService } from './services/aptic-structures.service'; import { StructureTypeController } from './structure-type/structure-type.controller'; import { StructureTypeService } from './structure-type/structure-type.service'; import { StructureType, StructureTypeSchema } from './structure-type/structure-type.schema'; +import { CategoriesModule } from '../categories/categories.module'; @Module({ imports: [ @@ -20,6 +21,7 @@ import { StructureType, StructureTypeSchema } from './structure-type/structure-t HttpModule, MailerModule, forwardRef(() => UsersModule), + CategoriesModule, TempUserModule, ], controllers: [StructuresController, StructureTypeController],