diff --git a/.env b/.env index 0bdfbe73..59857deb 100644 --- a/.env +++ b/.env @@ -45,13 +45,13 @@ ACCESS_CONTROL_ALLOW_ORIGINS= MAIL_FROM= # Password for email above -MAIL_FROM_PASSWORD= +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 -UPLOAD_FOLDER= +UPLOAD_FOLDER= \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 5d461e75..751c7d04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3916,11 +3916,12 @@ } }, "node_modules/body-parser": { - "version": "1.20.1", - "license": "MIT", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -3928,7 +3929,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -3939,18 +3940,21 @@ }, "node_modules/body-parser/node_modules/debug": { "version": "2.6.9", - "license": "MIT", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { "ms": "2.0.0" } }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/body-parser/node_modules/on-finished": { "version": "2.4.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { "ee-first": "1.1.1" }, @@ -4070,7 +4074,8 @@ }, "node_modules/bytes": { "version": "3.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { "node": ">= 0.8" } @@ -4432,8 +4437,9 @@ "license": "MIT" }, "node_modules/content-type": { - "version": "1.0.4", - "license": "MIT", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { "node": ">= 0.6" } @@ -5122,15 +5128,16 @@ } }, "node_modules/express": { - "version": "4.18.2", - "license": "MIT", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -5259,8 +5266,9 @@ } }, "node_modules/express/node_modules/cookie": { - "version": "0.5.0", - "license": "MIT", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } @@ -5780,7 +5788,8 @@ }, "node_modules/iconv-lite": { "version": "0.4.24", - "license": "MIT", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -5881,10 +5890,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 +6686,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", @@ -6953,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" } @@ -7258,8 +7282,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" } @@ -7782,8 +7807,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "license": "MIT", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -8003,7 +8029,8 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/saslprep": { "version": "1.0.3", @@ -8181,15 +8208,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" } }, @@ -8424,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" @@ -11657,10 +11685,12 @@ } }, "body-parser": { - "version": "1.20.1", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "requires": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -11668,22 +11698,28 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, "dependencies": { "debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" } }, "ms": { - "version": "2.0.0" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "on-finished": { "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "requires": { "ee-first": "1.1.1" } @@ -11754,7 +11790,9 @@ } }, "bytes": { - "version": "3.1.2" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, "call-bind": { "version": "1.0.2", @@ -11969,7 +12007,9 @@ } }, "content-type": { - "version": "1.0.4" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, "convert-source-map": { "version": "1.7.0", @@ -12384,14 +12424,16 @@ } }, "express": { - "version": "4.18.2", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -12420,7 +12462,9 @@ }, "dependencies": { "cookie": { - "version": "0.5.0" + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" }, "debug": { "version": "2.6.9", @@ -12796,6 +12840,8 @@ }, "iconv-lite": { "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -12849,10 +12895,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 +13437,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" }, @@ -13567,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", @@ -13760,7 +13821,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", @@ -14064,7 +14127,9 @@ } }, "raw-body": { - "version": "2.5.1", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "requires": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -14199,7 +14264,9 @@ "version": "5.1.2" }, "safer-buffer": { - "version": "2.1.2" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "saslprep": { "version": "1.0.3", @@ -14327,11 +14394,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" } }, @@ -14479,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/api/middleware/validators/offer.js b/src/api/middleware/validators/offer.js index c306f099..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,14 +89,18 @@ 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), + body("jobMaxDuration", ValidationReasons.DEFAULT) .exists().withMessage(ValidationReasons.REQUIRED).bail() .isInt().withMessage(ValidationReasons.INT).bail() .custom(jobMaxDurationGreaterOrEqualThanJobMinDuration), + body("jobStartDate", ValidationReasons.DEFAULT) .optional() .isISO8601({ strict: true }).withMessage(ValidationReasons.DATE).bail() 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; diff --git a/src/models/Offer.js b/src/models/Offer.js index f8b53fd9..ac075f3e 100644 --- a/src/models/Offer.js +++ b/src/models/Offer.js @@ -33,16 +33,23 @@ const OfferSchema = new Schema({ jobMinDuration: { type: Number, - required: true, + required: isMinDurationRequired, + validate: [ + validateJobMinDuration, + "`jobMinDuration` is not valid for freelance job", + ], }, + jobMaxDuration: { - type: Number, required: true, + type: Number, validate: [ validateJobMaxDuration, "`jobMaxDuration` must be larger than `jobMinDuration`", + ], }, + jobStartDate: { type: Date }, description: { type: String, @@ -135,9 +142,24 @@ export function validatePublishEndDateLimit(publishDate, publishEndDate) { // jobMaxDuration must be larger than jobMinDuration function validateJobMaxDuration(value) { + if (this.jobType === "FREELANCE") return true; return value >= this.jobMinDuration; } +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; +} + function validateOwnerConcurrentOffers(value) { return concurrentOffersNotExceeded(this.constructor)(value, this.publishDate, this.publishEndDate); } diff --git a/src/models/constants/JobTypes.js b/src/models/constants/JobTypes.js index 0400f105..ec31f6c0 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", + "FREELANCE", "OTHER", ]); diff --git a/test/end-to-end/offer.js b/test/end-to-end/offer.js index d79cfe6b..32e6fee2 100644 --- a/test/end-to-end/offer.js +++ b/test/end-to-end/offer.js @@ -232,20 +232,17 @@ describe("Offer endpoint tests", () => { FieldValidatorTester.mustBeFuture(); FieldValidatorTester.mustBeAfter("publishDate"); }); - describe("jobMinDuration", () => { const FieldValidatorTester = BodyValidatorTester("jobMinDuration"); 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(); @@ -681,24 +678,46 @@ 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({ + 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({ - jobMinDuration: 10, 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); + }); + 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); - 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 () => { const offer_params = generateTestOffer({ jobMinDuration: 8, diff --git a/test/offer_schema.js b/test/offer_schema.js index 30436f88..1694974b 100644 --- a/test/offer_schema.js +++ b/test/offer_schema.js @@ -127,28 +127,37 @@ 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("'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("'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."); - } - }); + 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)", () => {