From c3d933d111597d22f3cb2b9e161a5f586302e25e Mon Sep 17 00:00:00 2001 From: PavloDolia Date: Thu, 7 Nov 2024 12:28:18 +0200 Subject: [PATCH 01/10] added fields to cooperations --- migrate-mongo-config.js | 18 + ...241106160527-add-fields-to-cooperations.js | 48 +++ package-lock.json | 314 +++++++++++++++++- package.json | 1 + src/models/cooperation.js | 31 +- src/services/cooperation.js | 19 +- 6 files changed, 424 insertions(+), 7 deletions(-) create mode 100644 migrate-mongo-config.js create mode 100644 migrations/20241106160527-add-fields-to-cooperations.js diff --git a/migrate-mongo-config.js b/migrate-mongo-config.js new file mode 100644 index 00000000..f400aba6 --- /dev/null +++ b/migrate-mongo-config.js @@ -0,0 +1,18 @@ +const config = { + mongodb: { + url: 'mongodb://127.0.0.1:27017/s2s', + + options: { + useNewUrlParser: true, + useUnifiedTopology: true + } + }, + + migrationsDir: 'migrations', + changelogCollectionName: 'changelog', + migrationFileExtension: '.js', + useFileHash: false, + moduleSystem: 'commonjs' +} + +module.exports = config diff --git a/migrations/20241106160527-add-fields-to-cooperations.js b/migrations/20241106160527-add-fields-to-cooperations.js new file mode 100644 index 00000000..2dcca58f --- /dev/null +++ b/migrations/20241106160527-add-fields-to-cooperations.js @@ -0,0 +1,48 @@ +module.exports = { + async up(db) { + await db + .collection('cooperation') + .aggregate([ + { + $lookup: { + from: 'offers', + localField: 'offer', + foreignField: '_id', + as: '_tmp_offers_array' + } + }, + { $set: { _tmp_offer: { $first: '$_tmp_offers_array' } } }, + { + $set: { + subject: '$_tmp_offer.subject', + category: '$_tmp_offer.category', + description: '$_tmp_offer.description', + languages: '$_tmp_offer.languages', + proficiencyLevel: '$_tmp_offer.proficiencyLevel' + } + }, + { + $project: { + subject: 1, + category: 1, + description: 1, + languages: 1, + proficiencyLevel: 1 + } + }, + { $merge: { into: 'cooperation', on: '_id' } } + ]) + .toArray() + }, + + async down(db) { + await db.collection('cooperation').updateMany({}, [ + { + $unset: ['subject', 'category', 'description', 'languages'] + }, + { + $set: { proficiencyLevel: { $arrayElemAt: ['$proficiencyLevel', 0] } } + } + ]) + } +} diff --git a/package-lock.json b/package-lock.json index 183d8c9c..13887253 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "azure-storage": "^2.10.7", "bcrypt": "^5.0.1", + "cookie": "^0.6.0", "cookie-parser": "^1.4.6", "cors": "^2.8.5", "cron": "^2.1.0", @@ -26,6 +27,7 @@ "multer": "^1.4.5-lts.1", "nodemailer": "^6.7.7", "pug": "^3.0.2", + "socket.io": "^4.7.5", "swagger-jsdoc": "^6.1.0", "swagger-ui-express": "^4.3.0", "winston": "^3.8.0", @@ -38,6 +40,7 @@ "jest": "^28.1.3", "jest-sonar-reporter": "^2.0.0", "lint-staged": "^13.0.2", + "migrate-mongo": "^11.0.0", "nodemon": "^2.0.15", "prettier": "2.5.1", "supertest": "^6.2.4" @@ -1370,6 +1373,18 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.23.9", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", @@ -2750,6 +2765,11 @@ "node": ">=14.0.0" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -2791,6 +2811,19 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -3320,6 +3353,14 @@ } ] }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/bcrypt": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", @@ -3792,6 +3833,60 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-table3/node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/cli-table3/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==", + "dev": true + }, + "node_modules/cli-table3/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-table3/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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/cli-truncate": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", @@ -4053,9 +4148,9 @@ "dev": true }, "node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } @@ -4072,6 +4167,14 @@ "node": ">= 0.8.0" } }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -4160,6 +4263,22 @@ "node": ">=0.10" } }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -4490,6 +4609,42 @@ "node": ">=8.10.0" } }, + "node_modules/engine.io": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", + "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/entities": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", @@ -5093,6 +5248,15 @@ "integrity": "sha512-noqGuLw158+DuD9UPRKHpJ2hGxpFyDlYYrfM0mWt4XhT4n0lwzTLh70Tkdyy4kyTmyTT9Bv7bWAJqw7cgkEXDg==", "dev": true }, + "node_modules/fn-args": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fn-args/-/fn-args-5.0.0.tgz", + "integrity": "sha512-CtbfI3oFFc3nbdIoHycrfbrxiGgxXBXXuyOl49h47JawM1mYrqpiRqnH5CB2mBatdXvHHOUO6a+RiAuuvKt0lw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/fn.name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", @@ -5150,6 +5314,20 @@ "node": ">= 0.6" } }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -6864,6 +7042,18 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonparse": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.2.0.tgz", @@ -7721,6 +7911,39 @@ "node": ">=8.6" } }, + "node_modules/migrate-mongo": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/migrate-mongo/-/migrate-mongo-11.0.0.tgz", + "integrity": "sha512-GB/gHzUwp/fL1w6ksNGihTyb+cSrm6NbVLlz1OSkQKaLlzAXMwH7iKK2ZS7W5v+I8vXiY2rL58WTUZSAL6QR+A==", + "dev": true, + "dependencies": { + "cli-table3": "^0.6.1", + "commander": "^9.1.0", + "date-fns": "^2.28.0", + "fn-args": "^5.0.0", + "fs-extra": "^10.0.1", + "lodash": "^4.17.21", + "p-each-series": "^2.2.0" + }, + "bin": { + "migrate-mongo": "bin/migrate-mongo.js" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "mongodb": "^4.4.1 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/migrate-mongo/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -8305,6 +8528,18 @@ "node": ">= 0.8.0" } }, + "node_modules/p-each-series": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-event": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", @@ -9007,6 +9242,12 @@ "node": ">=8.10.0" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, "node_modules/request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -9613,6 +9854,44 @@ "npm": ">= 3.0.0" } }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/socks": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.3.tgz", @@ -10330,6 +10609,15 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -10815,6 +11103,26 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xml": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", diff --git a/package.json b/package.json index 588a4e36..dcd054a8 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "jest": "^28.1.3", "jest-sonar-reporter": "^2.0.0", "lint-staged": "^13.0.2", + "migrate-mongo": "^11.0.0", "nodemon": "^2.0.15", "prettier": "2.5.1", "supertest": "^6.2.4" diff --git a/src/models/cooperation.js b/src/models/cooperation.js index f6834f10..c1e5a86a 100644 --- a/src/models/cooperation.js +++ b/src/models/cooperation.js @@ -7,14 +7,15 @@ const { FIELD_MUST_BE_SELECTED, VALUE_MUST_BE_ABOVE } = require('~/consts/errors') -const { USER, OFFER, COOPERATION, FINISHED_QUIZ, QUIZ } = require('~/consts/models') +const { USER, OFFER, COOPERATION, FINISHED_QUIZ, QUIZ, SUBJECT, CATEGORY } = require('~/consts/models') const { enums: { COOPERATION_STATUS_ENUM, PROFICIENCY_LEVEL_ENUM, MAIN_ROLE_ENUM, RESOURCES_TYPES_ENUM, - RESOURCE_AVAILABILITY_STATUS_ENUM + RESOURCE_AVAILABILITY_STATUS_ENUM, + SPOKEN_LANG_ENUM } } = require('~/consts/validation') const { REQUESTED, UPDATED } = require('~/consts/notificationTypes') @@ -66,7 +67,7 @@ const cooperationSchema = new Schema( maxLength: [1000, FIELD_CANNOT_BE_LONGER('additional info', 1000)] }, proficiencyLevel: { - type: String, + type: [String], enum: { values: PROFICIENCY_LEVEL_ENUM, message: ENUM_CAN_BE_ONE_OF('proficiency level', PROFICIENCY_LEVEL_ENUM) @@ -102,6 +103,30 @@ const cooperationSchema = new Schema( type: [Schema.Types.ObjectId], ref: FINISHED_QUIZ }, + subject: { + type: Schema.Types.ObjectId, + ref: SUBJECT, + required: true + }, + category: { + type: Schema.Types.ObjectId, + ref: CATEGORY, + required: true + }, + description: { + type: String, + minLength: [1, FIELD_CANNOT_BE_SHORTER('description', 1)], + maxLength: [1000, FIELD_CANNOT_BE_LONGER('description', 1000)], + required: [true, FIELD_CANNOT_BE_EMPTY('description')] + }, + languages: { + type: [String], + enum: { + values: SPOKEN_LANG_ENUM, + message: ENUM_CAN_BE_ONE_OF('language', SPOKEN_LANG_ENUM) + }, + required: [true, FIELD_MUST_BE_SELECTED('language(s)')] + }, sections: [ { title: { diff --git a/src/services/cooperation.js b/src/services/cooperation.js index 26219301..1b713e0d 100644 --- a/src/services/cooperation.js +++ b/src/services/cooperation.js @@ -44,7 +44,20 @@ const cooperationService = { }, createCooperation: async (initiator, initiatorRole, data) => { - const { offer, proficiencyLevel, additionalInfo, receiver, receiverRole, price, title, sections } = data + const { + offer, + proficiencyLevel, + additionalInfo, + receiver, + receiverRole, + price, + title, + sections, + subject, + category, + description, + languages + } = data return await Cooperation.create({ initiator, @@ -57,6 +70,10 @@ const cooperationService = { price, proficiencyLevel, additionalInfo, + subject, + category, + description, + languages, needAction: receiverRole }) }, From e90fb62e70125583807ce64a8f4804ea3e65aba0 Mon Sep 17 00:00:00 2001 From: PavloDolia Date: Thu, 7 Nov 2024 12:28:58 +0200 Subject: [PATCH 02/10] fixed tests --- src/test/integration/controllers/cooperation.spec.js | 6 +++++- .../integration/controllers/coursesCooperations.spec.js | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/test/integration/controllers/cooperation.spec.js b/src/test/integration/controllers/cooperation.spec.js index 6c27263b..99cd3886 100644 --- a/src/test/integration/controllers/cooperation.spec.js +++ b/src/test/integration/controllers/cooperation.spec.js @@ -57,8 +57,10 @@ const anotherUserData = { const testCooperationData = { price: 99, receiverRole: 'tutor', - proficiencyLevel: 'Beginner', + proficiencyLevel: ['Beginner'], title: 'First-class teacher. Director of the Hogwarts school of magic', + description: 'I will teach you how to protect yourself and your family from dark arts', + languages: ['English'], sections: [ { title: 'Solving Quadratic Equations Using the Quadratic Formula', @@ -216,6 +218,8 @@ describe('Cooperation controller', () => { const updatedTestCooperationData = { ...testCooperationData, + subject: subject._id, + category: category._id, sections: [ { ...testCooperationData.sections[0], diff --git a/src/test/integration/controllers/coursesCooperations.spec.js b/src/test/integration/controllers/coursesCooperations.spec.js index 059e256e..5987dac1 100644 --- a/src/test/integration/controllers/coursesCooperations.spec.js +++ b/src/test/integration/controllers/coursesCooperations.spec.js @@ -68,7 +68,9 @@ const testCooperation = { receiver: 'tutorId', receiverRole: 'tutor', title: 'Cooperation title', - proficiencyLevel: 'Beginner', + proficiencyLevel: ['Beginner'], + description: 'I will teach you how to protect yourself and your family from dark arts', + languages: ['English'], price: 500, status: 'active', availableQuizzes: [], @@ -122,6 +124,8 @@ describe('User controller', () => { testCooperation.receiver = userId testCooperation.offer = offerId + testCooperation.subject = testSubject._id + testCooperation.category = categoryId }) afterEach(async () => { From a4d34fa61977d35905ae4e63891fa15b6b4e3a08 Mon Sep 17 00:00:00 2001 From: PavloDolia Date: Thu, 7 Nov 2024 13:44:10 +0200 Subject: [PATCH 03/10] fixed tests 2 --- .../controllers/cooperation.spec.js | 31 +-------- .../controllers/coursesCooperations.spec.js | 31 +++------ src/test/integration/controllers/note.spec.js | 69 +++++++++++++++---- src/test/test-utils.js | 36 +++++++++- 4 files changed, 99 insertions(+), 68 deletions(-) diff --git a/src/test/integration/controllers/cooperation.spec.js b/src/test/integration/controllers/cooperation.spec.js index 99cd3886..cdcaaabd 100644 --- a/src/test/integration/controllers/cooperation.spec.js +++ b/src/test/integration/controllers/cooperation.spec.js @@ -10,6 +10,7 @@ const { serverCleanup, serverInit, stopServer } = require('~/test/setup') const { expectError } = require('~/test/helpers') const testUserAuthentication = require('~/utils/testUserAuth') const TokenService = require('~/services/token') +const { testCooperation: testCooperationData } = require('~/test/test-utils') const { DOCUMENT_NOT_FOUND, UNAUTHORIZED, VALIDATION_ERROR, FORBIDDEN } = require('~/consts/errors') const { @@ -54,36 +55,6 @@ const anotherUserData = { lastLogin: new Date().toJSON() } -const testCooperationData = { - price: 99, - receiverRole: 'tutor', - proficiencyLevel: ['Beginner'], - title: 'First-class teacher. Director of the Hogwarts school of magic', - description: 'I will teach you how to protect yourself and your family from dark arts', - languages: ['English'], - sections: [ - { - title: 'Solving Quadratic Equations Using the Quadratic Formula', - description: 'Solving Quadratic Equations Using the Quadratic Formula', - resources: [ - { - resource: { - _id: '6684179479e5232bce4579fa', - author: '6658f73f93885febb491e08b', - content: '

Solving Quadratic Equations Using the Quadratic Formula

', - description: 'The quadratic formula', - title: 'Solving Quadratic Equations Using the Quadratic Formula', - category: '6684175179e5232bce4579ed', - resourceType: RESOURCES_TYPES_ENUM[0] - }, - resourceType: RESOURCES_TYPES_ENUM[0], - availability: { status: 'open', date: null } - } - ] - } - ] -} - const testOfferData = { authorRole: 'tutor', price: 99, diff --git a/src/test/integration/controllers/coursesCooperations.spec.js b/src/test/integration/controllers/coursesCooperations.spec.js index 5987dac1..13297151 100644 --- a/src/test/integration/controllers/coursesCooperations.spec.js +++ b/src/test/integration/controllers/coursesCooperations.spec.js @@ -1,5 +1,5 @@ const { serverCleanup, serverInit, stopServer } = require('~/test/setup') -const { createUser, getCategory } = require('~/test/test-utils') +const { createUser, getCategory, testCooperation: testCooperationData } = require('~/test/test-utils') const subjectService = require('~/services/subject') const Cooperation = require('~/models/cooperation') const { @@ -62,27 +62,14 @@ const testOffer = { } } -const testCooperation = { - offer: 'offerId', - initiatorRole: 'student', - receiver: 'tutorId', - receiverRole: 'tutor', - title: 'Cooperation title', - proficiencyLevel: ['Beginner'], - description: 'I will teach you how to protect yourself and your family from dark arts', - languages: ['English'], - price: 500, - status: 'active', - availableQuizzes: [], - finishedQuizzes: [], - sections: [ - { - title: 'Section 1', - description: 'description', - resources: [] - } - ] -} +const testCooperation = { ...testCooperationData } +testCooperation.sections = [ + { + title: 'Section 1', + description: 'description', + resources: [] + } +] describe('User controller', () => { let app, server, accessToken, userId, testLessonResponse, testLessonId, category, categoryId, testSubject, offerId diff --git a/src/test/integration/controllers/note.spec.js b/src/test/integration/controllers/note.spec.js index 8038430a..558936a4 100644 --- a/src/test/integration/controllers/note.spec.js +++ b/src/test/integration/controllers/note.spec.js @@ -3,9 +3,12 @@ const { expectError } = require('~/test/helpers') const { UNAUTHORIZED, FORBIDDEN, DOCUMENT_NOT_FOUND } = require('~/consts/errors') const testUserAuthentication = require('~/utils/testUserAuth') const TokenService = require('~/services/token') +const subjectService = require('~/services/subject') +const { getCategory, testCooperation: testCooperationData } = require('~/test/test-utils') const Cooperation = require('~/models/cooperation') const Note = require('~/models/note') +const User = require('~/models/user') const endpointUrl = (id = ':id', noteId = '') => `/cooperations/${id}/notes/${noteId}` @@ -13,19 +16,6 @@ const nonExistingCooperationId = '19cf23e07281224fbbee3241' const mockedInitiatorId = '649c1fc9c75d3e44440e3a15' -const testCooperationData = { - price: 99, - receiverRole: 'tutor', - proficiencyLevel: 'Beginner', - additionalInfo: - "I don't like both Dark Arts and Voldemort that's why i want to learn your subject and became your student", - receiver: '649c147ac75d3e44440e3a12', - offer: '649c148cc75d3e44440e3a13', - title: 'First class teacher. Director of the Hogwarts school witchcraft and wizardry.', - initiatorRole: 'student', - needAction: 'tutor' -} - const testNoteData = { text: 'my comment', isPrivate: false @@ -36,18 +26,67 @@ const updateNoteData = { isPrivate: true } +const subjectBody = { name: 'English' } + +const testOffer = { + price: 330, + proficiencyLevel: ['Beginner'], + title: 'Test Title', + author: '', + authorRole: 'tutor', + FAQ: [{ question: 'question1', answer: 'answer1' }], + description: 'description', + languages: ['Ukrainian'], + enrolledUsers: [], + subject: '', + category: { + _id: '', + appearance: { icon: 'mocked-path-to-icon', color: '#66C42C' } + } +} + +const tutorUserData = { + role: ['tutor'], + firstName: 'albus', + lastName: 'dumbledore', + email: 'lovemagic@gmail.com', + password: 'supermagicpass123', + appLanguage: 'en', + isEmailConfirmed: true, + lastLogin: new Date().toJSON() +} + describe('Note controller', () => { - let app, server, accessToken, testUser, testCooperation, testNote + let app, server, accessToken, testUser, testCooperation, testNote, category, testSubject beforeAll(async () => { - ; ({ app, server } = await serverInit()) + ;({ app, server } = await serverInit()) }) beforeEach(async () => { accessToken = await testUserAuthentication(app) + const testTutorUser = await User.create(tutorUserData) testUser = TokenService.validateAccessToken(accessToken) + category = await getCategory() + + subjectBody.category = category._id + testSubject = await subjectService.addSubject(subjectBody) + + testOffer.category = category._id + testOffer.subject = testSubject._id + const testOfferResponse = await app + .post('/offers/') + .set('Cookie', [`accessToken=${accessToken}`]) + .send(testOffer) + const offerId = testOfferResponse.body._id + + testCooperationData.category = category._id + testCooperationData.subject = testSubject._id + testCooperationData.offer = offerId + testCooperationData.receiver = testTutorUser._id + testCooperation = await Cooperation.create({ initiator: testUser.id, ...testCooperationData diff --git a/src/test/test-utils.js b/src/test/test-utils.js index 9c04d2ad..80c7bdac 100644 --- a/src/test/test-utils.js +++ b/src/test/test-utils.js @@ -2,6 +2,9 @@ const testUserAuthentication = require('~/utils/testUserAuth') const TokenService = require('~/services/token') const checkCategoryExistence = require('~/seed/checkCategoryExistence') const Category = require('~/models/category') +const { + enums: { RESOURCES_TYPES_ENUM } +} = require('~/consts/validation') const createUser = async (app, testUser = {}) => { const accessToken = await testUserAuthentication(app, testUser) @@ -17,4 +20,35 @@ const getCategory = async () => { return category } -module.exports = { createUser, getCategory } +const testCooperation = { + price: 400, + receiverRole: 'tutor', + proficiencyLevel: ['Beginner'], + title: 'Test cooperation title', + description: 'Some description...', + languages: ['English'], + needAction: 'tutor', + sections: [ + { + title: 'Section 1', + description: 'Section 1 description', + resources: [ + { + resource: { + _id: '6684179479e5232bce4579fa', + author: '6658f73f93885febb491e08b', + content: '

Solving Quadratic Equations Using the Quadratic Formula

', + description: 'The quadratic formula', + title: 'Solving Quadratic Equations Using the Quadratic Formula', + category: '6684175179e5232bce4579ed', + resourceType: RESOURCES_TYPES_ENUM[0] + }, + resourceType: RESOURCES_TYPES_ENUM[0], + availability: { status: 'open', date: null } + } + ] + } + ] +} + +module.exports = { createUser, getCategory, testCooperation } From 93df4746b5ea138231ce39e5fdde112b8bc925b3 Mon Sep 17 00:00:00 2001 From: PavloDolia Date: Thu, 7 Nov 2024 13:59:10 +0200 Subject: [PATCH 04/10] added migrate-mongo-config.js to sonar exclusions --- sonar-project.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonar-project.properties b/sonar-project.properties index 6b55c105..e82b52e1 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -8,7 +8,7 @@ sonar.projectVersion=1.0 # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. sonar.sources=. -sonar.exclusions=**/src/test/**/*.js, node_modules/**, jest.config.js, swagger-settings.js, docs/**, src/emails/** +sonar.exclusions=**/src/test/**/*.js, node_modules/**, jest.config.js, swagger-settings.js, docs/**, src/emails/**, migrate-mongo-config.js sonar.language=js From e588f9b457044328bd2651d9f3f6012fb3935de7 Mon Sep 17 00:00:00 2001 From: PavloDolia Date: Fri, 8 Nov 2024 09:58:48 +0200 Subject: [PATCH 05/10] added tests for the migration --- jest.config.js | 9 +- module-aliases.js | 1 + .../controllers/cooperation.spec.js | 2 +- .../controllers/coursesCooperations.spec.js | 3 +- src/test/integration/controllers/note.spec.js | 3 +- ...6160527-add-fields-to-cooperations.spec.js | 89 +++++++++++++++++++ src/test/test-constants.js | 53 +++++++++++ src/test/test-utils.js | 36 +------- 8 files changed, 156 insertions(+), 40 deletions(-) create mode 100644 src/test/migrations/20241106160527-add-fields-to-cooperations.spec.js create mode 100644 src/test/test-constants.js diff --git a/jest.config.js b/jest.config.js index e4d74660..dc7157fe 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,7 +1,8 @@ module.exports = { roots: ['/src/test'], moduleNameMapper: { - '^~/(.*)$': '/src/$1' + '^~/(.*)$': '/src/$1', + '@root/(.*)': '/$1' }, verbose: true, testEnvironment: 'node', @@ -27,6 +28,10 @@ module.exports = { coverageReporters: ['html', 'lcov'], coverageDirectory: '/src/test/coverage', testTimeout: 12000, - testMatch: ['/src/test/integration/**/*.spec.js', '/src/test/unit/**/*.spec.js'], + testMatch: [ + '/src/test/integration/**/*.spec.js', + '/src/test/unit/**/*.spec.js', + '/src/test/migrations/*.spec.js' + ], testResultsProcessor: 'jest-sonar-reporter' } diff --git a/module-aliases.js b/module-aliases.js index 22ce7124..3d3a1c66 100644 --- a/module-aliases.js +++ b/module-aliases.js @@ -1,3 +1,4 @@ const moduleAlias = require('module-alias') moduleAlias.addAlias('~', __dirname + '/src') +moduleAlias.addAlias('@root', __dirname) diff --git a/src/test/integration/controllers/cooperation.spec.js b/src/test/integration/controllers/cooperation.spec.js index cdcaaabd..e8a83881 100644 --- a/src/test/integration/controllers/cooperation.spec.js +++ b/src/test/integration/controllers/cooperation.spec.js @@ -10,7 +10,7 @@ const { serverCleanup, serverInit, stopServer } = require('~/test/setup') const { expectError } = require('~/test/helpers') const testUserAuthentication = require('~/utils/testUserAuth') const TokenService = require('~/services/token') -const { testCooperation: testCooperationData } = require('~/test/test-utils') +const { testCooperationData } = require('~/test/test-constants') const { DOCUMENT_NOT_FOUND, UNAUTHORIZED, VALIDATION_ERROR, FORBIDDEN } = require('~/consts/errors') const { diff --git a/src/test/integration/controllers/coursesCooperations.spec.js b/src/test/integration/controllers/coursesCooperations.spec.js index 13297151..839a38bf 100644 --- a/src/test/integration/controllers/coursesCooperations.spec.js +++ b/src/test/integration/controllers/coursesCooperations.spec.js @@ -1,5 +1,6 @@ const { serverCleanup, serverInit, stopServer } = require('~/test/setup') -const { createUser, getCategory, testCooperation: testCooperationData } = require('~/test/test-utils') +const { createUser, getCategory } = require('~/test/test-utils') +const { testCooperationData } = require('~/test/test-constants') const subjectService = require('~/services/subject') const Cooperation = require('~/models/cooperation') const { diff --git a/src/test/integration/controllers/note.spec.js b/src/test/integration/controllers/note.spec.js index 558936a4..e19c3dc6 100644 --- a/src/test/integration/controllers/note.spec.js +++ b/src/test/integration/controllers/note.spec.js @@ -4,7 +4,8 @@ const { UNAUTHORIZED, FORBIDDEN, DOCUMENT_NOT_FOUND } = require('~/consts/errors const testUserAuthentication = require('~/utils/testUserAuth') const TokenService = require('~/services/token') const subjectService = require('~/services/subject') -const { getCategory, testCooperation: testCooperationData } = require('~/test/test-utils') +const { getCategory } = require('~/test/test-utils') +const { testCooperationData } = require('~/test/test-constants') const Cooperation = require('~/models/cooperation') const Note = require('~/models/note') diff --git a/src/test/migrations/20241106160527-add-fields-to-cooperations.spec.js b/src/test/migrations/20241106160527-add-fields-to-cooperations.spec.js new file mode 100644 index 00000000..531b8a67 --- /dev/null +++ b/src/test/migrations/20241106160527-add-fields-to-cooperations.spec.js @@ -0,0 +1,89 @@ +const { MongoClient } = require('mongodb') + +const { up, down } = require('@root/migrations/20241106160527-add-fields-to-cooperations.js') + +require('~/initialization/envSetup') +const { + config: { MONGODB_URL } +} = require('~/configs/config') +const { + testCategoryData, + testSubjectData, + collectionNames: { SUBJECTS, CATEGORIES, OFFERS, COOPERATIONS } +} = require('~/test/test-constants') + +const url = MONGODB_URL.slice(0, MONGODB_URL.lastIndexOf('/')) +const databaseName = MONGODB_URL.slice(MONGODB_URL.lastIndexOf('/') + 1) + +const testOfferData = { + subject: 'subjectId', + category: 'categoryId', + description: 'Offer description', + languages: ['English'], + proficiencyLevel: ['Beginner'] +} + +const testCooperationData = { + title: 'Cooperation title', + proficiencyLevel: 'Beginner' +} + +describe('20241106160527-add-fields-to-cooperations:', () => { + let client, database, testSubjectId, testCategoryId, testCooperationId + + beforeAll(() => { + client = new MongoClient(url) + database = client.db(databaseName) + }) + + beforeEach(async () => { + const testCategory = await database.collection(CATEGORIES).insertOne(testCategoryData) + testCategoryId = testCategory.insertedId + + const testSubjectDataCopy = { ...testSubjectData, category: testCategory.insertedId } + const testSubject = await database.collection(SUBJECTS).insertOne(testSubjectDataCopy) + testSubjectId = testSubject.insertedId + + testOfferData.subject = testSubject.insertedId + testOfferData.category = testCategory.insertedId + const testOffer = await database.collection(OFFERS).insertOne(testOfferData) + + const testCooperation = await database + .collection(COOPERATIONS) + .insertOne({ ...testCooperationData, offer: testOffer.insertedId }) + testCooperationId = testCooperation.insertedId + }) + + afterEach(async () => { + await database.dropDatabase() + }) + + afterAll(async () => { + await client.close() + }) + + it('should migrate up', async () => { + await up(database) + + const cooperationData = await database.collection(COOPERATIONS).findOne({ _id: testCooperationId }) + + expect(cooperationData.category.toString()).toBe(testCategoryId.toString()) + expect(cooperationData.subject.toString()).toBe(testSubjectId.toString()) + expect(cooperationData.proficiencyLevel).toEqual(expect.arrayContaining(testOfferData.proficiencyLevel)) + expect(cooperationData.description).toBe(testOfferData.description) + expect(cooperationData.languages).toEqual(expect.arrayContaining(testOfferData.languages)) + }) + + it('should migrate down', async () => { + await up(database) + await down(database) + + const cooperationData = await database.collection(COOPERATIONS).findOne({ _id: testCooperationId }) + + expect(cooperationData.category).toBeUndefined() + expect(cooperationData.subject).toBeUndefined() + expect(cooperationData.proficiencyLevel).toEqual(testOfferData.proficiencyLevel[0]) + expect(cooperationData.description).toBeUndefined() + expect(cooperationData.languages).toBeUndefined() + }) +}) diff --git a/src/test/test-constants.js b/src/test/test-constants.js new file mode 100644 index 00000000..2b722ae0 --- /dev/null +++ b/src/test/test-constants.js @@ -0,0 +1,53 @@ +const { + enums: { RESOURCES_TYPES_ENUM } +} = require('~/consts/validation') + +const collectionNames = { + SUBJECTS: 'subjects', + CATEGORIES: 'categories', + OFFERS: 'offers', + COOPERATIONS: 'cooperation' +} + +const testCategoryData = { + name: 'IT', + appearance: { + icon: 'AccountTreeRoundedIcon', + color: '#47B8B8' + } +} + +const testCooperationData = { + price: 400, + receiverRole: 'tutor', + proficiencyLevel: ['Beginner'], + title: 'Test cooperation title', + description: 'Some description...', + languages: ['English'], + needAction: 'tutor', + sections: [ + { + title: 'Section 1', + description: 'Section 1 description', + resources: [ + { + resource: { + _id: '6684179479e5232bce4579fa', + author: '6658f73f93885febb491e08b', + content: '

Solving Quadratic Equations Using the Quadratic Formula

', + description: 'The quadratic formula', + title: 'Solving Quadratic Equations Using the Quadratic Formula', + category: '6684175179e5232bce4579ed', + resourceType: RESOURCES_TYPES_ENUM[0] + }, + resourceType: RESOURCES_TYPES_ENUM[0], + availability: { status: 'open', date: null } + } + ] + } + ] +} + +const testSubjectData = { name: 'Web Development' } + +module.exports = { testCategoryData, testSubjectData, testCooperationData, collectionNames } diff --git a/src/test/test-utils.js b/src/test/test-utils.js index 80c7bdac..9c04d2ad 100644 --- a/src/test/test-utils.js +++ b/src/test/test-utils.js @@ -2,9 +2,6 @@ const testUserAuthentication = require('~/utils/testUserAuth') const TokenService = require('~/services/token') const checkCategoryExistence = require('~/seed/checkCategoryExistence') const Category = require('~/models/category') -const { - enums: { RESOURCES_TYPES_ENUM } -} = require('~/consts/validation') const createUser = async (app, testUser = {}) => { const accessToken = await testUserAuthentication(app, testUser) @@ -20,35 +17,4 @@ const getCategory = async () => { return category } -const testCooperation = { - price: 400, - receiverRole: 'tutor', - proficiencyLevel: ['Beginner'], - title: 'Test cooperation title', - description: 'Some description...', - languages: ['English'], - needAction: 'tutor', - sections: [ - { - title: 'Section 1', - description: 'Section 1 description', - resources: [ - { - resource: { - _id: '6684179479e5232bce4579fa', - author: '6658f73f93885febb491e08b', - content: '

Solving Quadratic Equations Using the Quadratic Formula

', - description: 'The quadratic formula', - title: 'Solving Quadratic Equations Using the Quadratic Formula', - category: '6684175179e5232bce4579ed', - resourceType: RESOURCES_TYPES_ENUM[0] - }, - resourceType: RESOURCES_TYPES_ENUM[0], - availability: { status: 'open', date: null } - } - ] - } - ] -} - -module.exports = { createUser, getCategory, testCooperation } +module.exports = { createUser, getCategory } From 701253861198eaad3f692eea3eb082338fe297ab Mon Sep 17 00:00:00 2001 From: PavloDolia Date: Fri, 8 Nov 2024 12:41:57 +0200 Subject: [PATCH 06/10] changed to --- migrations/20241106160527-add-fields-to-cooperations.js | 2 +- module-aliases.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/migrations/20241106160527-add-fields-to-cooperations.js b/migrations/20241106160527-add-fields-to-cooperations.js index 2dcca58f..c738d02e 100644 --- a/migrations/20241106160527-add-fields-to-cooperations.js +++ b/migrations/20241106160527-add-fields-to-cooperations.js @@ -41,7 +41,7 @@ module.exports = { $unset: ['subject', 'category', 'description', 'languages'] }, { - $set: { proficiencyLevel: { $arrayElemAt: ['$proficiencyLevel', 0] } } + $set: { proficiencyLevel: { $first: '$proficiencyLevel' } } } ]) } diff --git a/module-aliases.js b/module-aliases.js index 3d3a1c66..22ce7124 100644 --- a/module-aliases.js +++ b/module-aliases.js @@ -1,4 +1,3 @@ const moduleAlias = require('module-alias') moduleAlias.addAlias('~', __dirname + '/src') -moduleAlias.addAlias('@root', __dirname) From 6d347c671fee81f224f949ba02c8742d09835b6c Mon Sep 17 00:00:00 2001 From: PavloDolia Date: Fri, 8 Nov 2024 20:45:48 +0200 Subject: [PATCH 07/10] fixed swagger docs --- docs/cooperation/cooperation-schema.yaml | 34 ++++++++++--- docs/cooperation/cooperation.yaml | 51 +++++++++++++++++-- .../coursesCooperations-schema.yaml | 12 +++++ .../coursesCooperations.yaml | 6 ++- 4 files changed, 91 insertions(+), 12 deletions(-) diff --git a/docs/cooperation/cooperation-schema.yaml b/docs/cooperation/cooperation-schema.yaml index 6c8fc989..765520c6 100644 --- a/docs/cooperation/cooperation-schema.yaml +++ b/docs/cooperation/cooperation-schema.yaml @@ -6,17 +6,28 @@ definitions: properties: _id: type: string + proficiencyLevel: + type: array + items: + type: string + subject: + type: string + category: + type: string + description: + type: string + languages: + type: array + items: + type: string offer: type: string - ref: '#components/offer' initiator: type: string - ref: '#/components/user' initiatorRole: type: string receiver: type: string - ref: '#components/user' receiverRole: type: string price: @@ -48,15 +59,26 @@ definitions: type: string offer: type: string - ref: '#components/offer' initiator: type: string - ref: '#/components/user' initiatorRole: type: string + proficiencyLevel: + type: array + items: + type: string + subject: + type: string + category: + type: string + description: + type: string + languages: + type: array + items: + type: string receiver: type: string - ref: '#components/user' receiverRole: type: string price: diff --git a/docs/cooperation/cooperation.yaml b/docs/cooperation/cooperation.yaml index f68472de..d2e0e589 100644 --- a/docs/cooperation/cooperation.yaml +++ b/docs/cooperation/cooperation.yaml @@ -24,7 +24,11 @@ paths: receiver: 6255bc080a75adf9223df100 receiverRole: student title: 'Violin lessons' - proficiencyLevel: Intermediate + proficiencyLevel: ['Intermediate'] + subject: 6255bc080a75adf9223df101 + category: 6255bc080a75adf9223df102 + description: "I'll teach you how to play violin" + languages: ['English'] price: 200 status: active needAction: student @@ -40,7 +44,11 @@ paths: receiver: 6255bc080a75adf9223df100 receiverRole: student title: 'Violin lessons' - proficiencyLevel: Intermediate + proficiencyLevel: ['Intermediate'] + subject: 6255bc080a75adf9223df103 + category: 6255bc080a75adf9223df104 + description: "I'll teach you how to play violin" + languages: ['English'] price: 300 status: closed needAction: tutor @@ -77,9 +85,18 @@ paths: $ref: '#/definitions/cooperation' example: offer: 63ebc6fbd2f34037d0aba791 + initiator: 6255bc080a75adf9223df212 + initiatorRole: tutor receiver: 6255bc080a75adf9223df100 receiverRole: student + title: 'Violin lessons' + proficiencyLevel: ['Intermediate'] price: 300 + needAction: tutor + subject: 6255bc080a75adf9223df103 + category: 6255bc080a75adf9223df104 + description: "I'll teach you how to play violin" + languages: ['English'] responses: 201: description: Created @@ -89,13 +106,21 @@ paths: $ref: '#/definitions/cooperation' example: _id: 8755bc080a00adr9243df104 - offer: 63ebc6fbd2f34037d0aba791 + offer: + { + _id: '66ec53d40d9d9983a9525421', + price: 400, + title: 'Violin lessons', + subject: { _id: '656605be8bebd72f0be56a3d', name: 'Violin' }, + category: + { appearance: { icon: 'StarRoundedIcon', color: '#607D8B' }, _id: '6566040f8bebd72f0be55a28' } + } initiator: 6255bc080a75adf9223df212 initiatorRole: tutor receiver: 6255bc080a75adf9223df100 receiverRole: student title: 'Violin lessons' - proficiencyLevel: Intermediate + proficiencyLevel: ['Intermediate'] price: 300 status: pending needAction: student @@ -104,6 +129,18 @@ paths: sections: [] createdAt: 2021-04-09T11:34:53.243+00:00 updatedAt: 2022-09-02T11:59:53.243+00:00 + category: '6566040f8bebd72f0be55a28' + description: "I'll teach you how to play violin" + languages: ['English'] + subject: { _id: '656605be8bebd72f0be56a3d', name: 'Violin' } + user: + { + _id: '66b0aecdadd1fe775238c7d5', + firstName: 'John', + lastName: 'Doe', + photo: '1726302778023-pexels-olly-3769706.jpg', + role: 'student' + } 400: description: Bad Request content: @@ -150,6 +187,10 @@ paths: $ref: '#/definitions/cooperationResponse' example: _id: 8755bc080a00adr9243df106 + description: "I'll teach you how to play violin" + languages: ['English'] + subject: '656605be8bebd72f0be55a3d' + category: '6566040f8bebd72f0be55a28' offer: _id: 63ebc6fbd2f34037d0aba791 proficiencyLevel: @@ -279,7 +320,7 @@ paths: professionalSummary: 'Recently graduated from the University of Cambridge with a degree in Mathematics. I have been tutoring students in Mathematics for over 5 years and have helped many students achieve their academic goals. I am passionate about teaching and enjoy helping students understand complex mathematical concepts. I believe that every student has the potential to succeed and I am committed to helping them reach their full potential.' receiverRole: 'tutor' title: 'Understanding Quadratic Equations' - proficiencyLevel: 'Intermediate' + proficiencyLevel: ['Intermediate'] price: 150 status: 'active' needAction: 'tutor' diff --git a/docs/coursesCooperations/coursesCooperations-schema.yaml b/docs/coursesCooperations/coursesCooperations-schema.yaml index 959b54ee..171b226b 100644 --- a/docs/coursesCooperations/coursesCooperations-schema.yaml +++ b/docs/coursesCooperations/coursesCooperations-schema.yaml @@ -100,7 +100,19 @@ definitions: price: type: number proficiencyLevel: + type: array + items: + type: string + subject: + type: string + category: type: string + description: + type: string + languages: + type: array + items: + type: string receiver: type: string receiverRole: diff --git a/docs/coursesCooperations/coursesCooperations.yaml b/docs/coursesCooperations/coursesCooperations.yaml index ea8885f1..fa1a0f12 100644 --- a/docs/coursesCooperations/coursesCooperations.yaml +++ b/docs/coursesCooperations/coursesCooperations.yaml @@ -28,7 +28,11 @@ paths: needAction: 'tutor', offer: '66ec53d40d9d9983a9525421', price: 500, - proficiencyLevel: 'Beginner', + proficiencyLevel: ['Beginner'], + description: "I'll teach you how to follow healthy diet", + languages: ['English'], + subject: '656605be8bebd72f0be55a3d', + category: '6566040f8bebd72f0be55a28', receiver: '66a7abbab3168fa64a8f5af1', receiverRole: 'tutor', sections: From f884f0af2069741d540d18e8ea095d751ea61a31 Mon Sep 17 00:00:00 2001 From: PavloDolia Date: Sat, 9 Nov 2024 22:01:54 +0200 Subject: [PATCH 08/10] fixed cooperation aggregation pipelines --- src/services/cooperation.js | 18 ++++---- .../cooperations/coopsAggregateOptions.js | 44 +++++++++---------- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/services/cooperation.js b/src/services/cooperation.js index 1b713e0d..f1bc98d9 100644 --- a/src/services/cooperation.js +++ b/src/services/cooperation.js @@ -16,23 +16,23 @@ const cooperationService = { await Cooperation.findById(id) ).populate([ { path: 'sections.resources.resource', select: '-createdAt -updatedAt' }, + { + path: 'category', + select: ['name', 'appearance'] + }, + { + path: 'subject', + select: 'name' + }, { path: 'offer', populate: [ - { - path: 'category', - select: ['name', 'appearance'] - }, - { - path: 'subject', - select: 'name' - }, { path: 'author', select: ['firstName', 'lastName', 'photo', 'professionalSummary', 'totalReviews', 'FAQ', 'averageRating'] } ], - select: ['id', 'author', 'category', 'subject', 'title', 'languages', 'proficiencyLevel', 'description'] + select: ['id', 'author'] }, { path: 'initiator' diff --git a/src/utils/cooperations/coopsAggregateOptions.js b/src/utils/cooperations/coopsAggregateOptions.js index 4eacc4a0..b4523dbb 100644 --- a/src/utils/cooperations/coopsAggregateOptions.js +++ b/src/utils/cooperations/coopsAggregateOptions.js @@ -65,43 +65,41 @@ const coopsAggregateOptions = (query, params = {}) => { }, { $lookup: { - from: 'offers', - localField: 'offer', + from: 'subjects', + localField: 'subject', foreignField: '_id', pipeline: [ - { $project: { title: 1, subject: 1, category: 1, price: 1 } }, { - $lookup: { - from: 'subjects', - let: { subjectId: '$subject' }, - pipeline: [{ $match: { $expr: { $eq: ['$_id', '$$subjectId'] } } }, { $project: { name: 1 } }], - as: 'subject' - } - }, - { - $lookup: { - from: 'categories', - let: { categoryId: '$category' }, - pipeline: [{ $match: { $expr: { $eq: ['$_id', '$$categoryId'] } } }, { $project: { appearance: 1 } }], - as: 'category' + $project: { + name: 1 } - }, - { $unwind: '$subject' }, - { $unwind: '$category' } + } ], - as: 'offer' + as: 'subject' } }, { - $unwind: { - path: '$user' + $lookup: { + from: 'categories', + localField: 'category', + foreignField: '_id', + pipeline: [ + { + $project: { + appearance: 1 + } + } + ], + as: 'category' } }, { $unwind: { - path: '$offer' + path: '$user' } }, + { $unwind: '$category' }, + { $unwind: '$subject' }, { $unset: 'sections' }, From 5d88becf498854a1a475ac8351266a34f57f230f Mon Sep 17 00:00:00 2001 From: PavloDolia Date: Sat, 9 Nov 2024 23:01:11 +0200 Subject: [PATCH 09/10] fixed swagger gocs 2 --- docs/cooperation/cooperation-schema.yaml | 330 +++++------------------ docs/cooperation/cooperation.yaml | 72 +++-- 2 files changed, 106 insertions(+), 296 deletions(-) diff --git a/docs/cooperation/cooperation-schema.yaml b/docs/cooperation/cooperation-schema.yaml index 765520c6..20ec1f4b 100644 --- a/docs/cooperation/cooperation-schema.yaml +++ b/docs/cooperation/cooperation-schema.yaml @@ -11,9 +11,33 @@ definitions: items: type: string subject: - type: string + type: object + properties: + name: + type: string category: - type: string + type: object + properties: + appearance: + type: object + properties: + color: + type: string + icon: + type: string + user: + type: object + properties: + _id: + type: string + firstName: + type: string + lastName: + type: string + photo: + type: string + role: + type: string description: type: string languages: @@ -59,26 +83,15 @@ definitions: type: string offer: type: string + ref: '#components/offer' initiator: type: string + ref: '#/components/user' initiatorRole: type: string - proficiencyLevel: - type: array - items: - type: string - subject: - type: string - category: - type: string - description: - type: string - languages: - type: array - items: - type: string receiver: type: string + ref: '#components/user' receiverRole: type: string price: @@ -207,28 +220,11 @@ definitions: properties: _id: type: string - proficiencyLevel: - type: array - items: - type: string - title: - type: string - description: - type: string - languages: - type: array - items: - type: string author: type: object properties: - totalReviews: - type: object - properties: - student: - type: integer - tutor: - type: integer + _id: + type: string averageRating: type: object properties: @@ -236,8 +232,6 @@ definitions: type: number tutor: type: number - _id: - type: string firstName: type: string lastName: @@ -246,246 +240,57 @@ definitions: type: string professionalSummary: type: string - subject: - type: object - properties: - _id: - type: string - name: - type: string - category: - type: object - properties: - appearance: + totalReviews: type: object properties: - color: - type: string - icon: - type: string - _id: - type: string - name: - type: string + student: + type: number + tutor: + type: number initiator: + type: string + initiatorRole: + type: string + proficiencyLevel: + type: array + items: + type: string + subject: type: object properties: - address: - type: object - properties: - country: - type: string - city: - type: string - mainSubjects: - type: object - properties: - student: - type: array - items: - type: string - tutor: - type: array - items: - type: string - totalReviews: - type: object - properties: - student: - type: integer - tutor: - type: integer - averageRating: - type: object - properties: - student: - type: number - tutor: - type: number - status: - type: object - properties: - student: - type: string - tutor: - type: string - admin: - type: string - videoLink: - type: object - properties: - student: - type: string - professionalBlock: - type: object - properties: - awards: - type: string - scientificActivities: - type: string - workExperience: - type: string - education: - type: string - notificationSettings: - type: object - properties: - isOfferStatusNotification: - type: boolean - isChatNotification: - type: boolean - isSimilarOffersNotification: - type: boolean - isEmailNotification: - type: boolean - _id: - type: string - role: - type: array - items: - type: string - firstName: - type: string - lastName: - type: string - email: + name: type: string - nativeLanguage: - type: string - lastLogin: - type: string - createdAt: - type: string - updatedAt: - type: string - professionalSummary: - type: string - initiatorRole: - type: string - receiver: + category: type: object properties: - address: + appearance: type: object properties: - country: + color: type: string - city: + icon: type: string - mainSubjects: - type: object - properties: - student: - type: array - items: - type: string - tutor: - type: array - items: - type: object - properties: - category: - type: string - subjects: - type: array - items: - type: string - _id: - type: string - totalReviews: - type: object - properties: - student: - type: integer - tutor: - type: integer - averageRating: - type: object - properties: - student: - type: number - tutor: - type: number - status: - type: object - properties: - student: - type: string - tutor: - type: string - admin: - type: string - videoLink: - type: object - properties: - tutor: - type: string - professionalBlock: - type: object - properties: - awards: - type: string - scientificActivities: - type: string - workExperience: - type: string - education: - type: string - notificationSettings: - type: object - properties: - isOfferStatusNotification: - type: boolean - isChatNotification: - type: boolean - isSimilarOffersNotification: - type: boolean - isEmailNotification: - type: boolean - _id: - type: string - role: - type: array - items: - type: string - firstName: - type: string - lastName: - type: string - email: - type: string - nativeLanguage: - type: string - lastLogin: - type: string - createdAt: - type: string - updatedAt: - type: string - photo: - type: string - professionalSummary: - type: string - receiverRole: + description: type: string - title: + languages: + type: array + items: + type: string + receiver: type: string - proficiencyLevel: + receiverRole: type: string price: - type: integer - status: + type: number + title: type: string - needAction: + status: type: string - availableQuizzes: - type: array - items: - type: string - finishedQuizzes: - type: array - items: - type: string + enum: + - pending + - active + - declined + - closed sections: type: array items: @@ -523,7 +328,14 @@ definitions: type: string format: date-time default: null + needAction: + type: string + enum: + - student + - tutor createdAt: type: string + format: date-time updatedAt: type: string + format: date-time diff --git a/docs/cooperation/cooperation.yaml b/docs/cooperation/cooperation.yaml index d2e0e589..e8fe656d 100644 --- a/docs/cooperation/cooperation.yaml +++ b/docs/cooperation/cooperation.yaml @@ -25,8 +25,16 @@ paths: receiverRole: student title: 'Violin lessons' proficiencyLevel: ['Intermediate'] - subject: 6255bc080a75adf9223df101 - category: 6255bc080a75adf9223df102 + subject: { name: 'Music' } + category: { appearance: { color: '#E3B21C', icon: 'ScienceRoundedIcon' } } + user: + { + _id: '66b0aecdadd1fe775238c7d5', + firstName: 'Pavlo', + lastName: 'Dolia', + photo: '1726302778023-pexels-olly-3769706.jpg', + role: 'student' + } description: "I'll teach you how to play violin" languages: ['English'] price: 200 @@ -45,8 +53,16 @@ paths: receiverRole: student title: 'Violin lessons' proficiencyLevel: ['Intermediate'] - subject: 6255bc080a75adf9223df103 - category: 6255bc080a75adf9223df104 + subject: { name: 'Music' } + category: { appearance: { color: '#E3B21C', icon: 'ScienceRoundedIcon' } } + user: + { + _id: '66b0aecdadd1fe775238c7d5', + firstName: 'Pavlo', + lastName: 'Dolia', + photo: '1726302778023-pexels-olly-3769706.jpg', + role: 'student' + } description: "I'll teach you how to play violin" languages: ['English'] price: 300 @@ -189,40 +205,22 @@ paths: _id: 8755bc080a00adr9243df106 description: "I'll teach you how to play violin" languages: ['English'] - subject: '656605be8bebd72f0be55a3d' - category: '6566040f8bebd72f0be55a28' + subject: { name: 'Music' } + category: { appearance: { color: '#E3B21C', icon: 'ScienceRoundedIcon' } } offer: - _id: 63ebc6fbd2f34037d0aba791 - proficiencyLevel: - - Intermediate - - Advanced - - Test Preparation - - Professional - title: 'Understanding Quadratic Equations' - description: "In this lesson, we will delve into the world of quadratic equations. We'll explore the standard form of a quadratic equation, learn how to identify the coefficients, and understand the significance of the discriminant. Students will also practice solving quadratic equations using various methods, including factoring, completing the square, and the quadratic formula. By the end of the lesson, students will be able to solve quadratic equations confidently and understand their graphical representations." - languages: - - English - author: - totalReviews: - student: 0 - tutor: 0 - averageRating: - student: 0 - tutor: 0 - _id: 6658f73f93885febb491e08b - firstName: 'John' - lastName: 'Doe' - photo: '6658f73f93885-test_student.png' - professionalSummary: 'I am student of the University of Cambridge. I am passionate about learning mathematics.' - subject: - _id: 6566133a2bccdd3e18dbe943 - name: 'Algebra' - category: - appearance: - color: '#E3B21C' - icon: 'TagRoundedIcon' - _id: '64884f33fdc2d1a130c24ac2' - name: 'Mathematics' + { + _id: 63ebc6fbd2f34037d0aba791, + author: + { + _id: '66a7abbab3168fa64a8f5af1', + averageRating: { student: 0, tutor: 0 }, + firstName: 'John', + lastName: 'Doe', + photo: '1726302583778-pexels-vanessa-garcia-6326377.jpg', + professionalSummary: 'I have 12 years of experience', + totalReviews: { student: 0, tutor: 0 } + } + } initiator: address: country: 'USA' From 8d001c338d06897519fc65a3988139a690081f4d Mon Sep 17 00:00:00 2001 From: PavloDolia Date: Sat, 9 Nov 2024 23:19:59 +0200 Subject: [PATCH 10/10] fixed cooperation tests --- src/test/integration/controllers/cooperation.spec.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/test/integration/controllers/cooperation.spec.js b/src/test/integration/controllers/cooperation.spec.js index e8a83881..efa18596 100644 --- a/src/test/integration/controllers/cooperation.spec.js +++ b/src/test/integration/controllers/cooperation.spec.js @@ -243,9 +243,7 @@ describe('Cooperation controller', () => { expect(Array.isArray(response.body.items)).toBe(true) expect(response.body.items[0]).toMatchObject({ _id: testCooperation._body._id, - offer: { - _id: testOffer._id - }, + offer: testOffer._id, initiator: testStudentUser.id, receiver: testTutorUser._id, proficiencyLevel: testCooperationData.proficiencyLevel,