From 5f94c2f01b4fdaecdf4db237a548d916816f5660 Mon Sep 17 00:00:00 2001 From: jose-carlos-sousa <2409jmsousa@gmail.com> Date: Thu, 8 Feb 2024 06:02:23 +0000 Subject: [PATCH 1/9] added freelancing to job types --- src/models/constants/JobTypes.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/models/constants/JobTypes.js b/src/models/constants/JobTypes.js index 0400f105..84d3a0ae 100644 --- a/src/models/constants/JobTypes.js +++ b/src/models/constants/JobTypes.js @@ -4,6 +4,7 @@ const JobTypes = Object.freeze([ "SUMMER INTERNSHIP", "CURRICULAR INTERNSHIP", "RESEARCH GRANT", + "Freelancing", "OTHER", ]); From 833242a551192ad9863b29077b8e7d2539dac520 Mon Sep 17 00:00:00 2001 From: jose-carlos-sousa <2409jmsousa@gmail.com> Date: Tue, 13 Feb 2024 22:16:00 +0000 Subject: [PATCH 2/9] make duration optional --- .env | 1 + NODE_ENV=test | 0 eslint | 0 nijobs-be@1.0.0 | 0 npm | 0 package-lock.json | 76 +++++++++++++++++++------- src/api/middleware/validators/offer.js | 2 + src/models/Offer.js | 4 +- src/models/constants/JobTypes.js | 2 +- test/end-to-end/offer.js | 2 - test/offer_schema.js | 22 -------- 11 files changed, 61 insertions(+), 48 deletions(-) create mode 100644 NODE_ENV=test create mode 100644 eslint create mode 100644 nijobs-be@1.0.0 create mode 100644 npm diff --git a/.env b/.env index 0bdfbe73..454dee8a 100644 --- a/.env +++ b/.env @@ -33,6 +33,7 @@ TEST_LOG_REQUESTS=false ADMIN_EMAIL=ni@aefeup.pt ADMIN_PASSWORD=n1j0bs_ftw.12345 + # List of regexes or url's specifying allowed origins. Example: # ACCESS_CONTROL_ALLOW_ORIGINS=["https:\\/\\/deploy-preview-\\d+--nijobs\\.netlify\\.app", "https://nijobs.netlify.app"] ACCESS_CONTROL_ALLOW_ORIGINS= diff --git a/NODE_ENV=test b/NODE_ENV=test new file mode 100644 index 00000000..e69de29b diff --git a/eslint b/eslint new file mode 100644 index 00000000..e69de29b diff --git a/nijobs-be@1.0.0 b/nijobs-be@1.0.0 new file mode 100644 index 00000000..e69de29b diff --git a/npm b/npm new file mode 100644 index 00000000..e69de29b diff --git a/package-lock.json b/package-lock.json index 5d461e75..8b71cae4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5881,10 +5881,22 @@ "version": "2.0.4", "license": "ISC" }, - "node_modules/ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" }, "node_modules/ipaddr.js": { "version": "1.9.1", @@ -6665,6 +6677,11 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + }, "node_modules/jsesc": { "version": "2.5.2", "license": "MIT", @@ -7258,8 +7275,9 @@ "license": "MIT" }, "node_modules/nodemailer": { - "version": "6.8.0", - "license": "MIT", + "version": "6.9.9", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.9.tgz", + "integrity": "sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==", "engines": { "node": ">=6.0.0" } @@ -8181,15 +8199,15 @@ } }, "node_modules/socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.3.tgz", + "integrity": "sha512-vfuYK48HXCTFD03G/1/zkIls3Ebr2YNa4qU9gHDZdblHLiqhJrJGkY3+0Nx0JpN9qBhJbVObc1CNciT1bIZJxw==", "dependencies": { - "ip": "^2.0.0", + "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" }, "engines": { - "node": ">= 10.13.0", + "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, @@ -12849,10 +12867,21 @@ "inherits": { "version": "2.0.4" }, - "ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + "ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "requires": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "dependencies": { + "sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" + } + } }, "ipaddr.js": { "version": "1.9.1" @@ -13380,6 +13409,11 @@ "esprima": "^4.0.0" } }, + "jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + }, "jsesc": { "version": "2.5.2" }, @@ -13760,7 +13794,9 @@ "version": "2.0.7" }, "nodemailer": { - "version": "6.8.0" + "version": "6.9.9", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.9.tgz", + "integrity": "sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==" }, "nodemailer-express-handlebars": { "version": "5.0.0", @@ -14327,11 +14363,11 @@ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" }, "socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.3.tgz", + "integrity": "sha512-vfuYK48HXCTFD03G/1/zkIls3Ebr2YNa4qU9gHDZdblHLiqhJrJGkY3+0Nx0JpN9qBhJbVObc1CNciT1bIZJxw==", "requires": { - "ip": "^2.0.0", + "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" } }, diff --git a/src/api/middleware/validators/offer.js b/src/api/middleware/validators/offer.js index c306f099..678d8d62 100644 --- a/src/api/middleware/validators/offer.js +++ b/src/api/middleware/validators/offer.js @@ -82,10 +82,12 @@ export const create = useExpressValidators([ body("jobMinDuration", ValidationReasons.DEFAULT) + .optional() .exists().withMessage(ValidationReasons.REQUIRED).bail() .isInt().withMessage(ValidationReasons.INT), body("jobMaxDuration", ValidationReasons.DEFAULT) + .optional() .exists().withMessage(ValidationReasons.REQUIRED).bail() .isInt().withMessage(ValidationReasons.INT).bail() .custom(jobMaxDurationGreaterOrEqualThanJobMinDuration), diff --git a/src/models/Offer.js b/src/models/Offer.js index f8b53fd9..054ee8c5 100644 --- a/src/models/Offer.js +++ b/src/models/Offer.js @@ -32,12 +32,10 @@ const OfferSchema = new Schema({ }, jobMinDuration: { - type: Number, - required: true, + type: Number }, jobMaxDuration: { type: Number, - required: true, validate: [ validateJobMaxDuration, "`jobMaxDuration` must be larger than `jobMinDuration`", diff --git a/src/models/constants/JobTypes.js b/src/models/constants/JobTypes.js index 84d3a0ae..ec31f6c0 100644 --- a/src/models/constants/JobTypes.js +++ b/src/models/constants/JobTypes.js @@ -4,7 +4,7 @@ const JobTypes = Object.freeze([ "SUMMER INTERNSHIP", "CURRICULAR INTERNSHIP", "RESEARCH GRANT", - "Freelancing", + "FREELANCE", "OTHER", ]); diff --git a/test/end-to-end/offer.js b/test/end-to-end/offer.js index d79cfe6b..a50d130a 100644 --- a/test/end-to-end/offer.js +++ b/test/end-to-end/offer.js @@ -235,13 +235,11 @@ describe("Offer endpoint tests", () => { describe("jobMinDuration", () => { const FieldValidatorTester = BodyValidatorTester("jobMinDuration"); - FieldValidatorTester.isRequired(); FieldValidatorTester.mustBeNumber(); }); describe("jobMaxDuration", () => { const FieldValidatorTester = BodyValidatorTester("jobMaxDuration"); - FieldValidatorTester.isRequired(); FieldValidatorTester.mustBeNumber(); FieldValidatorTester.mustBeGreaterThanOrEqualToField("jobMinDuration"); }); diff --git a/test/offer_schema.js b/test/offer_schema.js index 30436f88..dbc1949b 100644 --- a/test/offer_schema.js +++ b/test/offer_schema.js @@ -127,28 +127,6 @@ describe("# Offer Schema tests", () => { expect(err.errors.requirements).toHaveProperty("message", "There must be at least one requirement"); } }); - - test("'jobMinDuration' is required", async () => { - const offer = new Offer({}); - try { - await offer.validate(); - } catch (err) { - expect(err.errors.jobMinDuration).toBeDefined(); - expect(err.errors.jobMinDuration).toHaveProperty("kind", "required"); - expect(err.errors.jobMinDuration).toHaveProperty("message", "Path `jobMinDuration` is required."); - } - }); - - test("'jobMaxDuration' is required", async () => { - const offer = new Offer({}); - try { - await offer.validate(); - } catch (err) { - expect(err.errors.jobMaxDuration).toBeDefined(); - expect(err.errors.jobMaxDuration).toHaveProperty("kind", "required"); - expect(err.errors.jobMaxDuration).toHaveProperty("message", "Path `jobMaxDuration` is required."); - } - }); }); describe("required using custom validators (checking for array lengths, etc)", () => { From a789bdc09ef4306b4ebb1f6bd8a9d87ea93c1f58 Mon Sep 17 00:00:00 2001 From: jose-carlos-sousa <2409jmsousa@gmail.com> Date: Thu, 15 Feb 2024 00:32:11 +0000 Subject: [PATCH 3/9] now max and min only optional for freelance --- .env | 2 +- NODE_ENV=test | 0 eslint | 0 nijobs-be@1.0.0 | 0 npm | 0 src/api/middleware/validators/offer.js | 6 ++++-- 6 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 NODE_ENV=test delete mode 100644 eslint delete mode 100644 nijobs-be@1.0.0 delete mode 100644 npm diff --git a/.env b/.env index 454dee8a..281a1752 100644 --- a/.env +++ b/.env @@ -36,7 +36,7 @@ ADMIN_PASSWORD=n1j0bs_ftw.12345 # List of regexes or url's specifying allowed origins. Example: # ACCESS_CONTROL_ALLOW_ORIGINS=["https:\\/\\/deploy-preview-\\d+--nijobs\\.netlify\\.app", "https://nijobs.netlify.app"] -ACCESS_CONTROL_ALLOW_ORIGINS= +ACCESS_CONTROL_ALLOW_ORIGINS=["https:\\/\\/deploy-preview-\\d+--nijobs\\.netlify\\.app", "https://nijobs.netlify.app","https://localhost"] # Mail service information. If you don't provide a MAIL_FROM, no emails will be sent. The app will execute no-ops and won't crash # However, if you want to send emails, you need to fill all of the following 2 fields diff --git a/NODE_ENV=test b/NODE_ENV=test deleted file mode 100644 index e69de29b..00000000 diff --git a/eslint b/eslint deleted file mode 100644 index e69de29b..00000000 diff --git a/nijobs-be@1.0.0 b/nijobs-be@1.0.0 deleted file mode 100644 index e69de29b..00000000 diff --git a/npm b/npm deleted file mode 100644 index e69de29b..00000000 diff --git a/src/api/middleware/validators/offer.js b/src/api/middleware/validators/offer.js index 678d8d62..2e6ee670 100644 --- a/src/api/middleware/validators/offer.js +++ b/src/api/middleware/validators/offer.js @@ -82,15 +82,17 @@ export const create = useExpressValidators([ body("jobMinDuration", ValidationReasons.DEFAULT) - .optional() + .if((value, { req }) => req.body.jobType !== "FREELANCE") .exists().withMessage(ValidationReasons.REQUIRED).bail() .isInt().withMessage(ValidationReasons.INT), + body("jobMaxDuration", ValidationReasons.DEFAULT) - .optional() + .if((value, { req }) => req.body.jobType !== "FREELANCE") .exists().withMessage(ValidationReasons.REQUIRED).bail() .isInt().withMessage(ValidationReasons.INT).bail() .custom(jobMaxDurationGreaterOrEqualThanJobMinDuration), + body("jobStartDate", ValidationReasons.DEFAULT) .optional() From cd3f02b7fbf6846c8e6b89a463739a09d0301124 Mon Sep 17 00:00:00 2001 From: jose-carlos-sousa <2409jmsousa@gmail.com> Date: Thu, 15 Feb 2024 20:56:57 +0000 Subject: [PATCH 4/9] Making tests --- .env | 7 +++--- src/api/middleware/validators/offer.js | 5 ++--- src/models/Offer.js | 13 ++++++++++- test/end-to-end/offer.js | 9 ++++---- test/offer_schema.js | 31 ++++++++++++++++++++++++++ 5 files changed, 53 insertions(+), 12 deletions(-) diff --git a/.env b/.env index 281a1752..c886003a 100644 --- a/.env +++ b/.env @@ -33,10 +33,9 @@ TEST_LOG_REQUESTS=false ADMIN_EMAIL=ni@aefeup.pt ADMIN_PASSWORD=n1j0bs_ftw.12345 - # List of regexes or url's specifying allowed origins. Example: # ACCESS_CONTROL_ALLOW_ORIGINS=["https:\\/\\/deploy-preview-\\d+--nijobs\\.netlify\\.app", "https://nijobs.netlify.app"] -ACCESS_CONTROL_ALLOW_ORIGINS=["https:\\/\\/deploy-preview-\\d+--nijobs\\.netlify\\.app", "https://nijobs.netlify.app","https://localhost"] +ACCESS_CONTROL_ALLOW_ORIGINS= # Mail service information. If you don't provide a MAIL_FROM, no emails will be sent. The app will execute no-ops and won't crash # However, if you want to send emails, you need to fill all of the following 2 fields @@ -46,7 +45,7 @@ ACCESS_CONTROL_ALLOW_ORIGINS=["https:\\/\\/deploy-preview-\\d+--nijobs\\.netlify MAIL_FROM= # Password for email above -MAIL_FROM_PASSWORD= +MAIL_FROM_PASSWORD= # Cloudinary API URL to save images CLOUDINARY_URL= @@ -55,4 +54,4 @@ CLOUDINARY_URL= WEBSERVER_HOST=https://localhost:8087 # Path to save file uploads, the path must be relative to the root of the project - Defaults to static -UPLOAD_FOLDER= +UPLOAD_FOLDER= \ No newline at end of file diff --git a/src/api/middleware/validators/offer.js b/src/api/middleware/validators/offer.js index 2e6ee670..1f0b8141 100644 --- a/src/api/middleware/validators/offer.js +++ b/src/api/middleware/validators/offer.js @@ -85,14 +85,13 @@ export const create = useExpressValidators([ .if((value, { req }) => req.body.jobType !== "FREELANCE") .exists().withMessage(ValidationReasons.REQUIRED).bail() .isInt().withMessage(ValidationReasons.INT), - + body("jobMaxDuration", ValidationReasons.DEFAULT) - .if((value, { req }) => req.body.jobType !== "FREELANCE") .exists().withMessage(ValidationReasons.REQUIRED).bail() .isInt().withMessage(ValidationReasons.INT).bail() .custom(jobMaxDurationGreaterOrEqualThanJobMinDuration), - + body("jobStartDate", ValidationReasons.DEFAULT) .optional() diff --git a/src/models/Offer.js b/src/models/Offer.js index 054ee8c5..fa0cee66 100644 --- a/src/models/Offer.js +++ b/src/models/Offer.js @@ -32,15 +32,20 @@ const OfferSchema = new Schema({ }, jobMinDuration: { - type: Number + type: Number, + required: validateMinDuration, }, + jobMaxDuration: { + required: true, type: Number, validate: [ validateJobMaxDuration, "`jobMaxDuration` must be larger than `jobMinDuration`", + ], }, + jobStartDate: { type: Date }, description: { type: String, @@ -133,9 +138,15 @@ export function validatePublishEndDateLimit(publishDate, publishEndDate) { // jobMaxDuration must be larger than jobMinDuration function validateJobMaxDuration(value) { + if (this.jobType === "FREELANCE" && this.jobMinDuration === undefined) return true; return value >= this.jobMinDuration; } +function validateMinDuration() { + if (this.jobType === "FREELANCE") return false; + return true; +} + function validateOwnerConcurrentOffers(value) { return concurrentOffersNotExceeded(this.constructor)(value, this.publishDate, this.publishEndDate); } diff --git a/test/end-to-end/offer.js b/test/end-to-end/offer.js index a50d130a..373e102d 100644 --- a/test/end-to-end/offer.js +++ b/test/end-to-end/offer.js @@ -232,18 +232,19 @@ describe("Offer endpoint tests", () => { FieldValidatorTester.mustBeFuture(); FieldValidatorTester.mustBeAfter("publishDate"); }); - describe("jobMinDuration", () => { const FieldValidatorTester = BodyValidatorTester("jobMinDuration"); - FieldValidatorTester.mustBeNumber(); + if (BodyValidatorTester("jobType") !== "freelance") { + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeNumber(); + } }); - describe("jobMaxDuration", () => { const FieldValidatorTester = BodyValidatorTester("jobMaxDuration"); + FieldValidatorTester.isRequired(); FieldValidatorTester.mustBeNumber(); FieldValidatorTester.mustBeGreaterThanOrEqualToField("jobMinDuration"); }); - describe("jobStartDate", () => { const FieldValidatorTester = BodyValidatorTester("jobStartDate"); FieldValidatorTester.mustBeDate(); diff --git a/test/offer_schema.js b/test/offer_schema.js index dbc1949b..1694974b 100644 --- a/test/offer_schema.js +++ b/test/offer_schema.js @@ -129,6 +129,37 @@ describe("# Offer Schema tests", () => { }); }); + test("'jobMinDuration' is required if type offer different than freelance", async () => { + const offer = new Offer({}); + try { + await offer.validate(); + } catch (err) { + expect(err.errors.jobMinDuration).toBeDefined(); + expect(err.errors.jobMinDuration).toHaveProperty("kind", "required"); + expect(err.errors.jobMinDuration).toHaveProperty("message", "Path `jobMinDuration` is required."); + } + }); + + test("'jobMinDuration' is not required for 'FREELANCE' job type", async () => { + const offer = new Offer({ jobType: "FREELANCE" }); + try { + await offer.validate(); + } catch (err) { + expect(err.errors.jobMinDuration).toBeFalsy(); + } + }); + + test("'jobMaxDuration' is required", async () => { + const offer = new Offer({}); + try { + await offer.validate(); + } catch (err) { + expect(err.errors.jobMaxDuration).toBeDefined(); + expect(err.errors.jobMaxDuration).toHaveProperty("kind", "required"); + expect(err.errors.jobMaxDuration).toHaveProperty("message", "Path `jobMaxDuration` is required."); + } + }); + describe("required using custom validators (checking for array lengths, etc)", () => { describe(`'fields' must have between ${MIN_FIELDS} and ${MAX_FIELDS} values`, () => { test("Below minimum should throw error", async () => { From 2577513f547a4f4cd62a4e50328c41ff36aabb23 Mon Sep 17 00:00:00 2001 From: jose-carlos-sousa <139002032+jose-carlos-sousa@users.noreply.github.com> Date: Tue, 5 Mar 2024 18:33:51 +0000 Subject: [PATCH 5/9] changed end-to-end tests --- .env | 2 +- test/end-to-end/offer.js | 25 +++++++++++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/.env b/.env index c886003a..59857deb 100644 --- a/.env +++ b/.env @@ -50,7 +50,7 @@ MAIL_FROM_PASSWORD= # Cloudinary API URL to save images CLOUDINARY_URL= -# Hostname of the application (where the webserver will be served) - without the trailing '/' +# Hostname of the application (where the webserver will be served) - without the trailing '/' WEBSERVER_HOST=https://localhost:8087 # Path to save file uploads, the path must be relative to the root of the project - Defaults to static diff --git a/test/end-to-end/offer.js b/test/end-to-end/offer.js index 373e102d..a100ae9a 100644 --- a/test/end-to-end/offer.js +++ b/test/end-to-end/offer.js @@ -234,7 +234,7 @@ describe("Offer endpoint tests", () => { }); describe("jobMinDuration", () => { const FieldValidatorTester = BodyValidatorTester("jobMinDuration"); - if (BodyValidatorTester("jobType") !== "freelance") { + if (BodyValidatorTester("jobType") !== "FREELANCE") { FieldValidatorTester.isRequired(); FieldValidatorTester.mustBeNumber(); } @@ -680,22 +680,31 @@ describe("Offer endpoint tests", () => { }); describe("Job Duration", () => { - test("should fail if jobMinDuration is greater than jobMaxDuration", async () => { + + test("should succeed if jobMinDuration doesn't exist in freelance offer", async () => { const offer_params = generateTestOffer({ - jobMinDuration: 10, + jobType: "FREELANCE", jobMaxDuration: 8, + jobMinDuration: null, owner: test_company._id, }); - const res = await request() .post("/offers/new") .send(withGodToken(offer_params)); + expect(res.status).toBe(HTTPStatus.OK); + }); + + test("should fail if jobMinDuration doesn't exist in any offer besides freelance", async () => { + const offer_params = generateTestOffer({ + jobMaxDuration: 8, + jobMinDuration: null, + owner: test_company._id, + }); + const res = await request() + .post("/offers/new") + .send(withGodToken(offer_params)); expect(res.status).toBe(HTTPStatus.UNPROCESSABLE_ENTITY); - expect(res.body).toHaveProperty("error_code", ErrorTypes.VALIDATION_ERROR); - expect(res.body).toHaveProperty("errors"); - expect(res.body.errors[0]).toHaveProperty("param", "jobMaxDuration"); - expect(res.body.errors[0]).toHaveProperty("msg", ValidationReasons.MUST_BE_GREATER_THAN_OR_EQUAL_TO("jobMinDuration")); }); test("should succeed if jobMaxDuration is greater than jobMinDuration", async () => { From 02c7145b08a46207f14a897fe63c36a5ba18f7c1 Mon Sep 17 00:00:00 2001 From: jose-carlos-sousa <139002032+jose-carlos-sousa@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:21:18 +0000 Subject: [PATCH 6/9] testing numeric value in minduration --- test/end-to-end/offer.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/test/end-to-end/offer.js b/test/end-to-end/offer.js index a100ae9a..32e6fee2 100644 --- a/test/end-to-end/offer.js +++ b/test/end-to-end/offer.js @@ -234,10 +234,8 @@ describe("Offer endpoint tests", () => { }); describe("jobMinDuration", () => { const FieldValidatorTester = BodyValidatorTester("jobMinDuration"); - if (BodyValidatorTester("jobType") !== "FREELANCE") { - FieldValidatorTester.isRequired(); - FieldValidatorTester.mustBeNumber(); - } + FieldValidatorTester.isRequired(); + FieldValidatorTester.mustBeNumber(); }); describe("jobMaxDuration", () => { const FieldValidatorTester = BodyValidatorTester("jobMaxDuration"); @@ -707,6 +705,19 @@ describe("Offer endpoint tests", () => { expect(res.status).toBe(HTTPStatus.UNPROCESSABLE_ENTITY); }); + test("should fail jobMinDuration isn't numeric value", async () => { + const offer_params = generateTestOffer({ + jobMaxDuration: 8, + jobMinDuration: "nonNumeric", + owner: test_company._id, + }); + const res = await request() + .post("/offers/new") + .send(withGodToken(offer_params)); + expect(res.status).toBe(HTTPStatus.UNPROCESSABLE_ENTITY); + }); + + test("should succeed if jobMaxDuration is greater than jobMinDuration", async () => { const offer_params = generateTestOffer({ jobMinDuration: 8, From 0e62eb433401c140149d46761d06957efcd80015 Mon Sep 17 00:00:00 2001 From: jose-carlos-sousa <139002032+jose-carlos-sousa@users.noreply.github.com> Date: Tue, 9 Apr 2024 22:53:11 +0100 Subject: [PATCH 7/9] changed undefined to null as requested --- package-lock.json | 28 ++++++++++++++-------------- src/models/Offer.js | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7c0ae0b5..751c7d04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6979,11 +6979,9 @@ "license": "MIT" }, "node_modules/minipass": { - "version": "4.0.0", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "engines": { "node": ">=8" } @@ -8453,12 +8451,13 @@ } }, "node_modules/tar": { - "version": "6.1.13", - "license": "ISC", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", + "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" @@ -13630,10 +13629,9 @@ "version": "1.2.6" }, "minipass": { - "version": "4.0.0", - "requires": { - "yallist": "^4.0.0" - } + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" }, "minizlib": { "version": "2.1.2", @@ -14548,11 +14546,13 @@ "dev": true }, "tar": { - "version": "6.1.13", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", + "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" diff --git a/src/models/Offer.js b/src/models/Offer.js index fa0cee66..1d1400a9 100644 --- a/src/models/Offer.js +++ b/src/models/Offer.js @@ -138,7 +138,7 @@ export function validatePublishEndDateLimit(publishDate, publishEndDate) { // jobMaxDuration must be larger than jobMinDuration function validateJobMaxDuration(value) { - if (this.jobType === "FREELANCE" && this.jobMinDuration === undefined) return true; + if (this.jobType === "FREELANCE" && this.jobMinDuration === null) return true; return value >= this.jobMinDuration; } From 09eb81e62c5f960c6904137b3dabf55e8caf51e9 Mon Sep 17 00:00:00 2001 From: jose-carlos-sousa <139002032+jose-carlos-sousa@users.noreply.github.com> Date: Sat, 13 Apr 2024 14:04:58 +0100 Subject: [PATCH 8/9] now freelance jobs cant have min duration --- src/models/Offer.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/models/Offer.js b/src/models/Offer.js index 1d1400a9..ac075f3e 100644 --- a/src/models/Offer.js +++ b/src/models/Offer.js @@ -33,7 +33,11 @@ const OfferSchema = new Schema({ jobMinDuration: { type: Number, - required: validateMinDuration, + required: isMinDurationRequired, + validate: [ + validateJobMinDuration, + "`jobMinDuration` is not valid for freelance job", + ], }, jobMaxDuration: { @@ -138,11 +142,20 @@ export function validatePublishEndDateLimit(publishDate, publishEndDate) { // jobMaxDuration must be larger than jobMinDuration function validateJobMaxDuration(value) { - if (this.jobType === "FREELANCE" && this.jobMinDuration === null) return true; + if (this.jobType === "FREELANCE") return true; return value >= this.jobMinDuration; } -function validateMinDuration() { +function validateJobMinDuration() { + if (this.jobType === "FREELANCE") { + if (this.jobMinDuration === null) return true; + return false; + } + if (this.jobMinDuration === null) return false; + return true; +} + +function isMinDurationRequired() { if (this.jobType === "FREELANCE") return false; return true; } From 700be2ad1bac6009ffbd903bc532bb23bb1b7c39 Mon Sep 17 00:00:00 2001 From: jose-carlos-sousa <139002032+jose-carlos-sousa@users.noreply.github.com> Date: Wed, 17 Apr 2024 15:50:07 +0100 Subject: [PATCH 9/9] added validator in middleware --- src/api/middleware/validators/offer.js | 8 ++++++++ src/api/middleware/validators/validationReasons.js | 1 + 2 files changed, 9 insertions(+) diff --git a/src/api/middleware/validators/offer.js b/src/api/middleware/validators/offer.js index 1f0b8141..38371c5e 100644 --- a/src/api/middleware/validators/offer.js +++ b/src/api/middleware/validators/offer.js @@ -18,6 +18,13 @@ import { import * as companyMiddleware from "../company.js"; import config from "../../../config/env.js"; import { validApplyURL } from "../../../models/modelUtils.js"; +const jobMinDurationMustNotExistInFreelance = (jobMinDuration, { req }) => { + if (req.body.jobType !== "FREELANCE") return true; + if (jobMinDuration !== null && jobMinDuration !== undefined) { + throw new Error(ValidationReasons.FREELANCE_OFFER_CANT_HAVE_MIN_DURATION); + } + return true; +}; const jobMaxDurationGreaterOrEqualThanJobMinDuration = (jobMaxDuration, { req }) => { @@ -82,6 +89,7 @@ export const create = useExpressValidators([ body("jobMinDuration", ValidationReasons.DEFAULT) + .custom(jobMinDurationMustNotExistInFreelance) .if((value, { req }) => req.body.jobType !== "FREELANCE") .exists().withMessage(ValidationReasons.REQUIRED).bail() .isInt().withMessage(ValidationReasons.INT), diff --git a/src/api/middleware/validators/validationReasons.js b/src/api/middleware/validators/validationReasons.js index 7a38a793..fcd35d97 100644 --- a/src/api/middleware/validators/validationReasons.js +++ b/src/api/middleware/validators/validationReasons.js @@ -50,6 +50,7 @@ const ValidationReasons = Object.freeze({ OFFER_HIDDEN: "offer-is-hidden", FILE_TOO_LARGE: (max) => `file-cant-be-larger-than-${max}MB`, MUST_BE_GREATER_THAN_OR_EQUAL_TO: (field) => `must-be-greater-than-or-equal-to:${field}`, + FREELANCE_OFFER_CANT_HAVE_MIN_DURATION: "freelance-offer-cant-have-min-duration", }); export default ValidationReasons;