diff --git a/app-constants.js b/app-constants.js index bf1a43b7..e9747f85 100644 --- a/app-constants.js +++ b/app-constants.js @@ -3,9 +3,16 @@ */ const UserRoles = { - BookingManager: 'bookingmanager' + BookingManager: 'bookingmanager', + Administrator: 'administrator', + ConnectManager: 'Connect Manager' } +const FullManagePermissionRoles = [ + UserRoles.BookingManager, + UserRoles.Administrator +] + const Scopes = { // job READ_JOB: 'read:taas-jobs', @@ -31,5 +38,6 @@ const Scopes = { module.exports = { UserRoles, + FullManagePermissionRoles, Scopes } diff --git a/app-routes.js b/app-routes.js index 023dda0a..d3987ba2 100644 --- a/app-routes.js +++ b/app-routes.js @@ -49,8 +49,12 @@ module.exports = (app) => { } } else { req.authUser.jwtToken = req.headers.authorization - if (_.includes(req.authUser.roles, constants.UserRoles.BookingManager)) { - req.authUser.isBookingManager = true + // check if user has full manage permission + if (_.intersection(req.authUser.roles, constants.FullManagePermissionRoles).length) { + req.authUser.hasManagePermission = true + } + if (_.includes(req.authUser.roles, constants.UserRoles.ConnectManager)) { + req.authUser.isConnectManager = true } next() } diff --git a/docs/Topcoder-bookings-api.postman_collection.json b/docs/Topcoder-bookings-api.postman_collection.json index 67804f49..9fcdd1f1 100644 --- a/docs/Topcoder-bookings-api.postman_collection.json +++ b/docs/Topcoder-bookings-api.postman_collection.json @@ -7083,6 +7083,2581 @@ } ], "protocolProfileBehavior": {} + }, + { + "name": "Test Permission Rules", + "item": [ + { + "name": "Request with Administrator Role", + "item": [ + { + "name": "Jobs", + "item": [ + { + "name": "✔ create job with administrator", + "event": [ + { + "listen": "test", + "script": { + "id": "30445900-0393-477d-900a-c3b5f43006ed", + "exec": [ + "var data = JSON.parse(responseBody);\r", + "postman.setEnvironmentVariable(\"job_id_created_by_administrator\",data.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token_administrator}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"projectId\": {{projectId}},\r\n \"externalId\": \"1212\",\r\n \"description\": \"Dummy Description\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"numPositions\": 13,\r\n \"resourceType\": \"Dummy Resource Type\",\r\n \"rateType\": \"hourly\",\r\n \"workload\": \"full-time\",\r\n \"skills\": [\r\n \"23e00d92-207a-4b5b-b3c9-4c5662644941\",\r\n \"7d076384-ccf6-4e43-a45d-1b24b1e624aa\",\r\n \"cbac57a3-7180-4316-8769-73af64893158\",\r\n \"a2b4bc11-c641-4a19-9eb7-33980378f82e\"\r\n ]\r\n}\r\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobs", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs" + ] + } + }, + "response": [] + }, + { + "name": "✔ get job with administrator", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "url": { + "raw": "{{URL}}/jobs/{{job_id_created_by_administrator}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs", + "{{job_id_created_by_administrator}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ search jobs with administrator", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "url": { + "raw": "{{URL}}/jobs", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs" + ], + "query": [ + { + "key": "page", + "value": "0", + "disabled": true + }, + { + "key": "perPage", + "value": "3", + "disabled": true + }, + { + "key": "sortBy", + "value": "id", + "disabled": true + }, + { + "key": "sortOrder", + "value": "asc", + "disabled": true + }, + { + "key": "projectId", + "value": "21", + "disabled": true + }, + { + "key": "externalId", + "value": "1212", + "disabled": true + }, + { + "key": "description", + "value": "Dummy", + "disabled": true + }, + { + "key": "startDate", + "value": "2020-09-27T04:17:23.131Z", + "disabled": true + }, + { + "key": "endDate", + "value": "2020-09-27T04:17:23.131Z", + "disabled": true + }, + { + "key": "resourceType", + "value": "Dummy Resource Type", + "disabled": true + }, + { + "key": "skill", + "value": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "disabled": true + }, + { + "key": "rateType", + "value": "hourly", + "disabled": true + }, + { + "key": "status", + "value": "sourcing", + "disabled": true + }, + { + "key": "workload", + "value": "full-time", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "✔ put job with administrator", + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"projectId\": {{projectId}},\r\n \"externalId\": \"1212\",\r\n \"description\": \"Dummy Description\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"numPositions\": 13,\r\n \"resourceType\": \"Dummy Resource Type\",\r\n \"rateType\": \"hourly\",\r\n \"workload\": \"fractional\",\r\n \"skills\": [\r\n \"cbac57a3-7180-4316-8769-73af64893158\",\r\n \"a2b4bc11-c641-4a19-9eb7-33980378f82e\"\r\n ],\r\n \"status\": \"sourcing\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobs/{{job_id_created_by_administrator}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs", + "{{job_id_created_by_administrator}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ patch job with administrator", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"description\": \"Dummy Description\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"numPositions\": 13,\r\n \"resourceType\": \"Dummy Resource Type\",\r\n \"rateType\": \"hourly\",\r\n \"workload\": \"fractional\",\r\n \"skills\": [\r\n \"cbac57a3-7180-4316-8769-73af64893158\"\r\n ],\r\n \"status\": \"sourcing\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobs/{{job_id_created_by_administrator}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs", + "{{job_id_created_by_administrator}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ delete job with administrator", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobs/{{job_id_created_by_administrator}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs", + "{{job_id_created_by_administrator}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Job Candidates", + "item": [ + { + "name": "✔ create job candidate with administrator", + "event": [ + { + "listen": "test", + "script": { + "id": "802acbec-5c9d-4fa5-925f-76ebd2cc3476", + "exec": [ + "var data = JSON.parse(responseBody);\r", + "postman.setEnvironmentVariable(\"job_candidate_id_created_by_administrator\",data.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"jobId\": \"{{job_id_created_by_administrator}}\",\r\n \"userId\": \"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobCandidates", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates" + ] + } + }, + "response": [] + }, + { + "name": "✔ get job candidate with administrator", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "url": { + "raw": "{{URL}}/jobCandidates/{{job_candidate_id_created_by_administrator}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates", + "{{job_candidate_id_created_by_administrator}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ search job candidates with administrator", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "url": { + "raw": "{{URL}}/jobCandidates", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates" + ], + "query": [ + { + "key": "page", + "value": "1", + "disabled": true + }, + { + "key": "perPage", + "value": "1", + "disabled": true + }, + { + "key": "sortBy", + "value": "id", + "disabled": true + }, + { + "key": "sortOrder", + "value": "asc", + "disabled": true + }, + { + "key": "jobId", + "value": "46225f4c-c2a3-4603-a141-0277e96fabfa", + "disabled": true + }, + { + "key": "userId", + "value": "a55fe1bc-1754-45fa-9adc-cf3d6d7c377a", + "disabled": true + }, + { + "key": "status", + "value": "shortlist", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "✔ put job candidate with administrator", + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"jobId\": \"{{job_id_created_by_administrator}}\",\r\n \"userId\": \"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a\",\r\n \"status\": \"selected\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobCandidates/{{job_candidate_id_created_by_administrator}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates", + "{{job_candidate_id_created_by_administrator}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ patch job candidate with administrator", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"status\": \"shortlist\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobCandidates/{{job_candidate_id_created_by_administrator}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates", + "{{job_candidate_id_created_by_administrator}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ delete job candidate with administrator", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobCandidates/{{job_candidate_id_created_by_administrator}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates", + "{{job_candidate_id_created_by_administrator}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Resource Bookings", + "item": [ + { + "name": "✔ create resource booking with administrator", + "event": [ + { + "listen": "test", + "script": { + "id": "75f5abd2-9751-4d24-9252-e0cd70f6157b", + "exec": [ + "var data = JSON.parse(responseBody);\r", + "postman.setEnvironmentVariable(\"resource_bookings_id_created_by_administrator\",data.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"projectId\": {{projectId}},\r\n \"userId\": \"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a\",\r\n \"jobId\": \"{{job_id_created_by_administrator}}\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"memberRate\": 13.23,\r\n \"customerRate\": 13,\r\n \"rateType\": \"hourly\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/resourceBookings", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings" + ] + } + }, + "response": [] + }, + { + "name": "✔ get resource booking with administrator", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "url": { + "raw": "{{URL}}/resourceBookings/{{resource_bookings_id_created_by_administrator}}", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings", + "{{resource_bookings_id_created_by_administrator}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ search resource bookings with administrator", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "url": { + "raw": "{{URL}}/resourceBookings", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings" + ], + "query": [ + { + "key": "page", + "value": "1", + "disabled": true + }, + { + "key": "perPage", + "value": "5", + "disabled": true + }, + { + "key": "sortBy", + "value": "id", + "disabled": true + }, + { + "key": "sortOrder", + "value": "desc", + "disabled": true + }, + { + "key": "startDate", + "value": "2020-09-27T04:17:23.131Z", + "disabled": true + }, + { + "key": "endDate", + "value": "2020-09-27T04:17:23.131Z", + "disabled": true + }, + { + "key": "rateType", + "value": "hourly", + "disabled": true + }, + { + "key": "status", + "value": "sourcing", + "disabled": true + }, + { + "key": "projectIds", + "value": "111, 16705", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "✔ put resource booking with administrator", + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"projectId\": {{projectId}},\r\n \"userId\": \"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a\",\r\n \"jobId\": \"{{job_id_created_by_administrator}}\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"memberRate\": 13.23,\r\n \"customerRate\": 13,\r\n \"rateType\": \"hourly\",\r\n \"status\": \"assigned\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/resourceBookings/{{resource_bookings_id_created_by_administrator}}", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings", + "{{resource_bookings_id_created_by_administrator}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ patch resource booking with administrator", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"status\": \"assigned\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"memberRate\": 13.23,\r\n \"customerRate\": 13,\r\n \"rateType\": \"hourly\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/resourceBookings/{{resource_bookings_id_created_by_administrator}}", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings", + "{{resource_bookings_id_created_by_administrator}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ delete resource booking with administrator", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/resourceBookings/{{resource_bookings_id_created_by_administrator}}", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings", + "{{resource_bookings_id_created_by_administrator}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Request with Topcoder User Role", + "item": [ + { + "name": "Before Test", + "item": [ + { + "name": "[STUB] refresh the jwt token for user tester1234", + "request": { + "method": "LOCK", + "header": [], + "url": { + "raw": "" + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Jobs", + "item": [ + { + "name": "✔ create job with member", + "event": [ + { + "listen": "test", + "script": { + "id": "368f7f55-b18f-4f62-ad6c-a162fdbed90d", + "exec": [ + "var data = JSON.parse(responseBody);\r", + "postman.setEnvironmentVariable(\"job_id_created_by_member\",data.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token_member_tester1234}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"projectId\": {{project_id_16718}},\r\n \"externalId\": \"1212\",\r\n \"description\": \"Dummy Description\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"numPositions\": 13,\r\n \"resourceType\": \"Dummy Resource Type\",\r\n \"rateType\": \"hourly\",\r\n \"workload\": \"full-time\",\r\n \"skills\": [\r\n \"23e00d92-207a-4b5b-b3c9-4c5662644941\",\r\n \"7d076384-ccf6-4e43-a45d-1b24b1e624aa\",\r\n \"cbac57a3-7180-4316-8769-73af64893158\",\r\n \"a2b4bc11-c641-4a19-9eb7-33980378f82e\"\r\n ]\r\n}\r\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobs", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs" + ] + } + }, + "response": [] + }, + { + "name": "✔ get job with member", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "url": { + "raw": "{{URL}}/jobs/{{job_id_created_by_member}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs", + "{{job_id_created_by_member}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ search jobs with member filtering by \"projectId\"", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "url": { + "raw": "{{URL}}/jobs?projectId={{project_id_16718}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs" + ], + "query": [ + { + "key": "page", + "value": "0", + "disabled": true + }, + { + "key": "perPage", + "value": "3", + "disabled": true + }, + { + "key": "sortBy", + "value": "id", + "disabled": true + }, + { + "key": "sortOrder", + "value": "asc", + "disabled": true + }, + { + "key": "projectId", + "value": "{{project_id_16718}}" + }, + { + "key": "externalId", + "value": "1212", + "disabled": true + }, + { + "key": "description", + "value": "Dummy", + "disabled": true + }, + { + "key": "startDate", + "value": "2020-09-27T04:17:23.131Z", + "disabled": true + }, + { + "key": "endDate", + "value": "2020-09-27T04:17:23.131Z", + "disabled": true + }, + { + "key": "resourceType", + "value": "Dummy Resource Type", + "disabled": true + }, + { + "key": "skill", + "value": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "disabled": true + }, + { + "key": "rateType", + "value": "hourly", + "disabled": true + }, + { + "key": "status", + "value": "sourcing", + "disabled": true + }, + { + "key": "workload", + "value": "full-time", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "✔ put job with member", + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"projectId\": {{project_id_16718}},\r\n \"externalId\": \"1212\",\r\n \"description\": \"Dummy Description\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"numPositions\": 13,\r\n \"resourceType\": \"Dummy Resource Type\",\r\n \"rateType\": \"hourly\",\r\n \"workload\": \"fractional\",\r\n \"skills\": [\r\n \"cbac57a3-7180-4316-8769-73af64893158\",\r\n \"a2b4bc11-c641-4a19-9eb7-33980378f82e\"\r\n ],\r\n \"status\": \"sourcing\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobs/{{job_id_created_by_member}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs", + "{{job_id_created_by_member}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ patch job with member", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"description\": \"Dummy Description\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"numPositions\": 13,\r\n \"resourceType\": \"Dummy Resource Type\",\r\n \"rateType\": \"hourly\",\r\n \"workload\": \"fractional\",\r\n \"skills\": [\r\n \"cbac57a3-7180-4316-8769-73af64893158\"\r\n ],\r\n \"status\": \"sourcing\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobs/{{job_id_created_by_member}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs", + "{{job_id_created_by_member}}" + ] + } + }, + "response": [] + }, + { + "name": "✘ delete job with member", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobs/{{job_id_created_by_member}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs", + "{{job_id_created_by_member}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Job Candidates", + "item": [ + { + "name": "Before Test", + "item": [ + { + "name": "create job candidate", + "event": [ + { + "listen": "test", + "script": { + "id": "6f84340d-c8dd-49f9-bee9-660b3e555296", + "exec": [ + "var data = JSON.parse(responseBody);\r", + "postman.setEnvironmentVariable(\"job_candidate_id_created_for_member\",data.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"jobId\": \"{{job_id_created_by_member}}\",\r\n \"userId\": \"fe38eed1-af73-41fd-85a2-ac4da1ff09a3\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobCandidates", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "✘ create job candidate with member", + "event": [ + { + "listen": "test", + "script": { + "id": "f25317af-4933-4c93-b02b-cae7feddac50", + "exec": [ + "var data = JSON.parse(responseBody);\r", + "postman.setEnvironmentVariable(\"job_candidate_id_created_for_member\",data.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"jobId\": \"{{job_id_created_by_member}}\",\r\n \"userId\": \"fe38eed1-af73-41fd-85a2-ac4da1ff09a3\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobCandidates", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates" + ] + } + }, + "response": [] + }, + { + "name": "✔ get job candidate with member", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "url": { + "raw": "{{URL}}/jobCandidates/{{job_candidate_id_created_for_member}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates", + "{{job_candidate_id_created_for_member}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ search job candidates with member filtering by \"jobId\"", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "url": { + "raw": "{{URL}}/jobCandidates?jobId={{job_id_created_by_member}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates" + ], + "query": [ + { + "key": "page", + "value": "1", + "disabled": true + }, + { + "key": "perPage", + "value": "1", + "disabled": true + }, + { + "key": "sortBy", + "value": "id", + "disabled": true + }, + { + "key": "sortOrder", + "value": "asc", + "disabled": true + }, + { + "key": "jobId", + "value": "{{job_id_created_by_member}}" + }, + { + "key": "userId", + "value": "fe38eed1-af73-41fd-85a2-ac4da1ff09a3", + "disabled": true + }, + { + "key": "status", + "value": "shortlist", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "✔ put job candidate with member", + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"jobId\": \"{{job_id_created_by_member}}\",\r\n \"userId\": \"fe38eed1-af73-41fd-85a2-ac4da1ff09a3\",\r\n \"status\": \"selected\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobCandidates/{{job_candidate_id_created_for_member}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates", + "{{job_candidate_id_created_for_member}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ patch job candidate with member", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"status\": \"shortlist\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobCandidates/{{job_candidate_id_created_for_member}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates", + "{{job_candidate_id_created_for_member}}" + ] + } + }, + "response": [] + }, + { + "name": "✘ delete job candidate with member", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobCandidates/{{job_candidate_id_created_for_member}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates", + "{{job_candidate_id_created_for_member}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Resource Bookings", + "item": [ + { + "name": "Before Test", + "item": [ + { + "name": "create resource booking", + "event": [ + { + "listen": "test", + "script": { + "id": "dbf53485-021e-49d5-b877-4b8d970a0331", + "exec": [ + "var data = JSON.parse(responseBody);\r", + "postman.setEnvironmentVariable(\"resource_booking_id_created_for_member\",data.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"projectId\": {{project_id_16718}},\r\n \"userId\": \"fe38eed1-af73-41fd-85a2-ac4da1ff09a3\",\r\n \"jobId\": \"{{job_id_created_by_member}}\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"memberRate\": 13.23,\r\n \"customerRate\": 13,\r\n \"rateType\": \"hourly\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/resourceBookings", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "✘ create resource booking with member", + "event": [ + { + "listen": "test", + "script": { + "id": "9754578e-91dd-437d-b5a2-cdb5668e14e4", + "exec": [ + "var data = JSON.parse(responseBody);\r", + "postman.setEnvironmentVariable(\"resource_booking_id_created_for_member\",data.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"projectId\": {{project_id_16718}},\r\n \"userId\": \"fe38eed1-af73-41fd-85a2-ac4da1ff09a3\",\r\n \"jobId\": \"{{job_id_created_by_member}}\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"memberRate\": 13.23,\r\n \"customerRate\": 13,\r\n \"rateType\": \"hourly\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/resourceBookings", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings" + ] + } + }, + "response": [] + }, + { + "name": "✔ get resource booking with member", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "url": { + "raw": "{{URL}}/resourceBookings/{{resource_booking_id_created_for_member}}", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings", + "{{resource_booking_id_created_for_member}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ search resource bookings with member filtering by \"projectId\"", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "url": { + "raw": "{{URL}}/resourceBookings?projectId={{project_id_16718}}", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings" + ], + "query": [ + { + "key": "page", + "value": "1", + "disabled": true + }, + { + "key": "perPage", + "value": "5", + "disabled": true + }, + { + "key": "sortBy", + "value": "id", + "disabled": true + }, + { + "key": "sortOrder", + "value": "desc", + "disabled": true + }, + { + "key": "startDate", + "value": "2020-09-27T04:17:23.131Z", + "disabled": true + }, + { + "key": "endDate", + "value": "2020-09-27T04:17:23.131Z", + "disabled": true + }, + { + "key": "rateType", + "value": "hourly", + "disabled": true + }, + { + "key": "status", + "value": "sourcing", + "disabled": true + }, + { + "key": "projectIds", + "value": "111, 16705", + "disabled": true + }, + { + "key": "projectId", + "value": "{{project_id_16718}}" + } + ] + } + }, + "response": [] + }, + { + "name": "✘ put resource booking with member", + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"projectId\": {{project_id_16718}},\r\n \"userId\": \"fe38eed1-af73-41fd-85a2-ac4da1ff09a3\",\r\n \"jobId\": \"{{job_id_created_by_member}}\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"memberRate\": 13.23,\r\n \"customerRate\": 13,\r\n \"rateType\": \"hourly\",\r\n \"status\": \"assigned\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/resourceBookings/{{resource_booking_id_created_for_member}}", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings", + "{{resource_booking_id_created_for_member}}" + ] + } + }, + "response": [] + }, + { + "name": "✘ patch resource booking with member", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"status\": \"assigned\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"memberRate\": 13.23,\r\n \"customerRate\": 13,\r\n \"rateType\": \"hourly\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/resourceBookings/{{resource_booking_id_created_for_member}}", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings", + "{{resource_booking_id_created_for_member}}" + ] + } + }, + "response": [] + }, + { + "name": "✘ delete resource booking with member", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_member_tester1234}}" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/resourceBookings/{{resource_booking_id_created_for_member}}", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings", + "{{resource_booking_id_created_for_member}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Request with Connect Manager Role", + "item": [ + { + "name": "Before Test", + "item": [ + { + "name": "[STUB] refresh the jwt token for connect manager", + "request": { + "method": "LOCK", + "header": [], + "url": { + "raw": "" + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Jobs", + "item": [ + { + "name": "Before Test", + "item": [ + { + "name": "create job", + "event": [ + { + "listen": "test", + "script": { + "id": "faaf5dc1-9869-4615-992c-3cace41f65e8", + "exec": [ + "var data = JSON.parse(responseBody);\r", + "postman.setEnvironmentVariable(\"job_id_created_for_connect_manager\",data.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token_administrator}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"projectId\": {{project_id_16718}},\r\n \"externalId\": \"1212\",\r\n \"description\": \"Dummy Description\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"numPositions\": 13,\r\n \"resourceType\": \"Dummy Resource Type\",\r\n \"rateType\": \"hourly\",\r\n \"workload\": \"full-time\",\r\n \"skills\": [\r\n \"23e00d92-207a-4b5b-b3c9-4c5662644941\",\r\n \"7d076384-ccf6-4e43-a45d-1b24b1e624aa\",\r\n \"cbac57a3-7180-4316-8769-73af64893158\",\r\n \"a2b4bc11-c641-4a19-9eb7-33980378f82e\"\r\n ]\r\n}\r\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobs", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "✘ create job with connect manager", + "event": [ + { + "listen": "test", + "script": { + "id": "ab2fa9b2-71fc-4cda-b72d-60cf2d99525d", + "exec": [ + "var data = JSON.parse(responseBody);\r", + "postman.setEnvironmentVariable(\"job_id_created_for_connect_manager\",data.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token_connectUser}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"projectId\": {{project_id_16718}},\r\n \"externalId\": \"1212\",\r\n \"description\": \"Dummy Description\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"numPositions\": 13,\r\n \"resourceType\": \"Dummy Resource Type\",\r\n \"rateType\": \"hourly\",\r\n \"workload\": \"full-time\",\r\n \"skills\": [\r\n \"23e00d92-207a-4b5b-b3c9-4c5662644941\",\r\n \"7d076384-ccf6-4e43-a45d-1b24b1e624aa\",\r\n \"cbac57a3-7180-4316-8769-73af64893158\",\r\n \"a2b4bc11-c641-4a19-9eb7-33980378f82e\"\r\n ]\r\n}\r\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobs", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs" + ] + } + }, + "response": [] + }, + { + "name": "✔ get job with connect manager", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "url": { + "raw": "{{URL}}/jobs/{{job_id_created_for_connect_manager}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs", + "{{job_id_created_for_connect_manager}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ search jobs with connect manager", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "url": { + "raw": "{{URL}}/jobs", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs" + ], + "query": [ + { + "key": "page", + "value": "0", + "disabled": true + }, + { + "key": "perPage", + "value": "3", + "disabled": true + }, + { + "key": "sortBy", + "value": "id", + "disabled": true + }, + { + "key": "sortOrder", + "value": "asc", + "disabled": true + }, + { + "key": "projectId", + "value": "21", + "disabled": true + }, + { + "key": "externalId", + "value": "1212", + "disabled": true + }, + { + "key": "description", + "value": "Dummy", + "disabled": true + }, + { + "key": "startDate", + "value": "2020-09-27T04:17:23.131Z", + "disabled": true + }, + { + "key": "endDate", + "value": "2020-09-27T04:17:23.131Z", + "disabled": true + }, + { + "key": "resourceType", + "value": "Dummy Resource Type", + "disabled": true + }, + { + "key": "skill", + "value": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "disabled": true + }, + { + "key": "rateType", + "value": "hourly", + "disabled": true + }, + { + "key": "status", + "value": "sourcing", + "disabled": true + }, + { + "key": "workload", + "value": "full-time", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "✘ put job with connect manager", + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"projectId\": {{project_id_16718}},\r\n \"externalId\": \"1212\",\r\n \"description\": \"Dummy Description\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"numPositions\": 13,\r\n \"resourceType\": \"Dummy Resource Type\",\r\n \"rateType\": \"hourly\",\r\n \"workload\": \"fractional\",\r\n \"skills\": [\r\n \"cbac57a3-7180-4316-8769-73af64893158\",\r\n \"a2b4bc11-c641-4a19-9eb7-33980378f82e\"\r\n ],\r\n \"status\": \"sourcing\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobs/{{job_id_created_for_connect_manager}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs", + "{{job_id_created_for_connect_manager}}" + ] + } + }, + "response": [] + }, + { + "name": "✘ patch job with connect manager", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"description\": \"Dummy Description\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"numPositions\": 13,\r\n \"resourceType\": \"Dummy Resource Type\",\r\n \"rateType\": \"hourly\",\r\n \"workload\": \"fractional\",\r\n \"skills\": [\r\n \"cbac57a3-7180-4316-8769-73af64893158\"\r\n ],\r\n \"status\": \"sourcing\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobs/{{job_id_created_for_connect_manager}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs", + "{{job_id_created_for_connect_manager}}" + ] + } + }, + "response": [] + }, + { + "name": "✘ delete job with connect manager", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobs/{{job_id_created_for_connect_manager}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobs", + "{{job_id_created_for_connect_manager}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Job Candidates", + "item": [ + { + "name": "Before Test", + "item": [ + { + "name": "create job candidate", + "event": [ + { + "listen": "test", + "script": { + "id": "5ce6dd7a-39aa-4910-aba1-37f02559d293", + "exec": [ + "var data = JSON.parse(responseBody);\r", + "postman.setEnvironmentVariable(\"job_candidate_id_created_for_connect_manager\",data.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"jobId\": \"{{job_id_created_by_member}}\",\r\n \"userId\": \"fe38eed1-af73-41fd-85a2-ac4da1ff09a3\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobCandidates", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "✘ create job candidate with connect manager", + "event": [ + { + "listen": "test", + "script": { + "id": "74e63fe5-8d71-4791-a722-5d7347e28f83", + "exec": [ + "var data = JSON.parse(responseBody);\r", + "postman.setEnvironmentVariable(\"job_candidate_id_created_for_connect_manager\",data.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"jobId\": \"{{job_id_created_for_connect_manager}}\",\r\n \"userId\": \"fe38eed1-af73-41fd-85a2-ac4da1ff09a3\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobCandidates", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates" + ] + } + }, + "response": [] + }, + { + "name": "✔ get job candidate with connect manager", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "url": { + "raw": "{{URL}}/jobCandidates/{{job_candidate_id_created_for_connect_manager}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates", + "{{job_candidate_id_created_for_connect_manager}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ search job candidates with connect manager", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "url": { + "raw": "{{URL}}/jobCandidates", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates" + ], + "query": [ + { + "key": "page", + "value": "1", + "disabled": true + }, + { + "key": "perPage", + "value": "1", + "disabled": true + }, + { + "key": "sortBy", + "value": "id", + "disabled": true + }, + { + "key": "sortOrder", + "value": "asc", + "disabled": true + }, + { + "key": "jobId", + "value": "46225f4c-c2a3-4603-a141-0277e96fabfa", + "disabled": true + }, + { + "key": "userId", + "value": "fe38eed1-af73-41fd-85a2-ac4da1ff09a3", + "disabled": true + }, + { + "key": "status", + "value": "shortlist", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "✘ put job candidate with connect manager", + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"jobId\": \"{{job_id_created_for_connect_manager}}\",\r\n \"userId\": \"fe38eed1-af73-41fd-85a2-ac4da1ff09a3\",\r\n \"status\": \"selected\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobCandidates/{{job_candidate_id_created_for_connect_manager}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates", + "{{job_candidate_id_created_for_connect_manager}}" + ] + } + }, + "response": [] + }, + { + "name": "✘ patch job candidate with connect manager", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"status\": \"shortlist\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobCandidates/{{job_candidate_id_created_for_connect_manager}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates", + "{{job_candidate_id_created_for_connect_manager}}" + ] + } + }, + "response": [] + }, + { + "name": "✘ delete job candidate with connect manager", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/jobCandidates/{{job_candidate_id_created_for_connect_manager}}", + "host": [ + "{{URL}}" + ], + "path": [ + "jobCandidates", + "{{job_candidate_id_created_for_connect_manager}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Resource Bookings", + "item": [ + { + "name": "Before Test", + "item": [ + { + "name": "create resource booking", + "event": [ + { + "listen": "test", + "script": { + "id": "513617f1-b4ba-4041-9aaf-fd99f883939b", + "exec": [ + "var data = JSON.parse(responseBody);\r", + "postman.setEnvironmentVariable(\"resource_booking_id_created_for_connect_manager\",data.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_administrator}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"projectId\": {{project_id_16718}},\r\n \"userId\": \"fe38eed1-af73-41fd-85a2-ac4da1ff09a3\",\r\n \"jobId\": \"{{job_id_created_by_member}}\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"memberRate\": 13.23,\r\n \"customerRate\": 13,\r\n \"rateType\": \"hourly\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/resourceBookings", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "✘ create resource booking with connect manager", + "event": [ + { + "listen": "test", + "script": { + "id": "be4bde84-1e50-4bb4-a99c-4d03ce055023", + "exec": [ + "var data = JSON.parse(responseBody);\r", + "postman.setEnvironmentVariable(\"resource_booking_id_created_for_connect_manager\",data.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"projectId\": {{project_id_16718}},\r\n \"userId\": \"fe38eed1-af73-41fd-85a2-ac4da1ff09a3\",\r\n \"jobId\": \"{{job_id_created_for_connect_manager}}\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"memberRate\": 13.23,\r\n \"customerRate\": 13,\r\n \"rateType\": \"hourly\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/resourceBookings", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings" + ] + } + }, + "response": [] + }, + { + "name": "✔ get resource booking with connect manager", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "url": { + "raw": "{{URL}}/resourceBookings/{{resource_booking_id_created_for_connect_manager}}", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings", + "{{resource_booking_id_created_for_connect_manager}}" + ] + } + }, + "response": [] + }, + { + "name": "✔ search resource bookings with connect manager", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "url": { + "raw": "{{URL}}/resourceBookings", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings" + ], + "query": [ + { + "key": "page", + "value": "1", + "disabled": true + }, + { + "key": "perPage", + "value": "5", + "disabled": true + }, + { + "key": "sortBy", + "value": "id", + "disabled": true + }, + { + "key": "sortOrder", + "value": "desc", + "disabled": true + }, + { + "key": "startDate", + "value": "2020-09-27T04:17:23.131Z", + "disabled": true + }, + { + "key": "endDate", + "value": "2020-09-27T04:17:23.131Z", + "disabled": true + }, + { + "key": "rateType", + "value": "hourly", + "disabled": true + }, + { + "key": "status", + "value": "sourcing", + "disabled": true + }, + { + "key": "projectIds", + "value": "111, 16705", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "✘ put resource booking with connect manager", + "request": { + "method": "PUT", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"projectId\": {{project_id_16718}},\r\n \"userId\": \"fe38eed1-af73-41fd-85a2-ac4da1ff09a3\",\r\n \"jobId\": \"{{job_id_created_for_connect_manager}}\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"memberRate\": 13.23,\r\n \"customerRate\": 13,\r\n \"rateType\": \"hourly\",\r\n \"status\": \"assigned\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/resourceBookings/{{resource_booking_id_created_for_connect_manager}}", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings", + "{{resource_booking_id_created_for_connect_manager}}" + ] + } + }, + "response": [] + }, + { + "name": "✘ patch resource booking with connect manager", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"status\": \"assigned\",\r\n \"startDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"endDate\": \"2020-09-27T04:17:23.131Z\",\r\n \"memberRate\": 13.23,\r\n \"customerRate\": 13,\r\n \"rateType\": \"hourly\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/resourceBookings/{{resource_booking_id_created_for_connect_manager}}", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings", + "{{resource_booking_id_created_for_connect_manager}}" + ] + } + }, + "response": [] + }, + { + "name": "✘ delete resource booking with connect manager", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token_connectUser}}" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/resourceBookings/{{resource_booking_id_created_for_connect_manager}}", + "host": [ + "{{URL}}" + ], + "path": [ + "resourceBookings", + "{{resource_booking_id_created_for_connect_manager}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {} } ], "protocolProfileBehavior": {} diff --git a/docs/topcoder-bookings.postman_environment.json b/docs/topcoder-bookings.postman_environment.json index a97d2383..17491678 100644 --- a/docs/topcoder-bookings.postman_environment.json +++ b/docs/topcoder-bookings.postman_environment.json @@ -22,6 +22,11 @@ "value": "", "enabled": true }, + { + "key": "token_administrator", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiYWRtaW5pc3RyYXRvciJdLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci1kZXYuY29tIiwiaGFuZGxlIjoidGVzdDEiLCJleHAiOjIxNDc0ODM2NDgsInVzZXJJZCI6IjQwMTUyODU2IiwiaWF0IjoxNDYzMDc2MDg5LCJlbWFpbCI6InRlc3RAdG9wY29kZXIuY29tIiwianRpIjoiYjMzYjc3Y2QtYjUyZS00MGZlLTgzN2UtYmViOGUwYWU2YTRhIn0.PKv0QrMCPf0-ZPjv4PGWT7eXne54m7i9YX9eq-fceMU", + "enabled": true + }, { "key": "token_bookingManager", "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik5VSkZORGd4UlRVME5EWTBOVVkzTlRkR05qTXlRamxETmpOQk5UYzVRVUV3UlRFeU56TTJRUSJ9.eyJodHRwczovL3RvcGNvZGVyLWRldi5jb20vcm9sZXMiOlsiVG9wY29kZXIgVXNlciIsImNvcGlsb3QiLCJDb25uZWN0IE1hbmFnZXIiLCJib29raW5nbWFuYWdlciIsInUtYmFobiJdLCJodHRwczovL3RvcGNvZGVyLWRldi5jb20vdXNlcklkIjoiNDAxNTI4NTYiLCJodHRwczovL3RvcGNvZGVyLWRldi5jb20vaGFuZGxlIjoicHNoYWhfbWFuYWdlciIsImh0dHBzOi8vdG9wY29kZXItZGV2LmNvbS91c2VyX2lkIjoiYXV0aDB8NDAxNTI4NTYiLCJodHRwczovL3RvcGNvZGVyLWRldi5jb20vdGNzc28iOiI0MDE1Mjg1Nnw4MTM0ZjQ4ZWJlMTFhODQ4YTM3NTllNWVmOWU5MmYyMTQ2OTJlMjExMzA0MGM4MmI1ZDhmNTgxYzZkZmNjYzg4IiwiaHR0cHM6Ly90b3Bjb2Rlci1kZXYuY29tL2FjdGl2ZSI6dHJ1ZSwibmlja25hbWUiOiJwc2hhaF9tYW5hZ2VyIiwibmFtZSI6InZpa2FzLmFnYXJ3YWwrcHNoYWhfbWFuYWdlckB0b3Bjb2Rlci5jb20iLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvOTJhZmIyZjBlZDUyZmRmYWUxZjM3MTAyMWFlNjUwMTM_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZ2aS5wbmciLCJ1cGRhdGVkX2F0IjoiMjAyMC0xMC0yNFQwODoyODoyNC4xODRaIiwiZW1haWwiOiJ2aWthcy5hZ2Fyd2FsK3BzaGFoX21hbmFnZXJAdG9wY29kZXIuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8vYXV0aC50b3Bjb2Rlci1kZXYuY29tLyIsInN1YiI6ImF1dGgwfDQwMTUyODU2IiwiYXVkIjoiQlhXWFVXbmlsVlVQZE4wMXQyU2UyOVR3MlpZTkdadkgiLCJpYXQiOjE2MDM1NDMzMzgsImV4cCI6MzMxNjA0NTI3MzgsIm5vbmNlIjoiUjFBMmN6WXVWVFptYmpaSFJHOTJWbDlEU1VKNlVsbHZRWGMzUkhoNVMzWldkV1pEY0ROWE1FWjFYdz09In0.2gPsqZTgS1rtiNa1USm3KPA6Xsv3TcHxuDFofgIbeOM", @@ -37,6 +42,11 @@ "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik5VSkZORGd4UlRVME5EWTBOVVkzTlRkR05qTXlRamxETmpOQk5UYzVRVUV3UlRFeU56TTJRUSJ9.eyJodHRwczovL3RvcGNvZGVyLWRldi5jb20vcm9sZXMiOlsiVG9wY29kZXIgVXNlciJdLCJodHRwczovL3RvcGNvZGVyLWRldi5jb20vdXNlcklkIjoiODU0Nzg5OSIsImh0dHBzOi8vdG9wY29kZXItZGV2LmNvbS9oYW5kbGUiOiJwc2hhaF9tYW5hZ2VyIiwiaHR0cHM6Ly90b3Bjb2Rlci1kZXYuY29tL3VzZXJfaWQiOiJhdXRoMHw0MDE1Mjg1NiIsImh0dHBzOi8vdG9wY29kZXItZGV2LmNvbS90Y3NzbyI6IjQwMTUyODU2fDgxMzRmNDhlYmUxMWE4NDhhMzc1OWU1ZWY5ZTkyZjIxNDY5MmUyMTEzMDQwYzgyYjVkOGY1ODFjNmRmY2NjODgiLCJodHRwczovL3RvcGNvZGVyLWRldi5jb20vYWN0aXZlIjp0cnVlLCJuaWNrbmFtZSI6InBzaGFoX21hbmFnZXIiLCJuYW1lIjoidmlrYXMuYWdhcndhbCtwc2hhaF9tYW5hZ2VyQHRvcGNvZGVyLmNvbSIsInBpY3R1cmUiOiJodHRwczovL3MuZ3JhdmF0YXIuY29tL2F2YXRhci85MmFmYjJmMGVkNTJmZGZhZTFmMzcxMDIxYWU2NTAxMz9zPTQ4MCZyPXBnJmQ9aHR0cHMlM0ElMkYlMkZjZG4uYXV0aDAuY29tJTJGYXZhdGFycyUyRnZpLnBuZyIsInVwZGF0ZWRfYXQiOiIyMDIwLTEwLTI0VDA4OjI4OjI0LjE4NFoiLCJlbWFpbCI6InZpa2FzLmFnYXJ3YWwrcHNoYWhfbWFuYWdlckB0b3Bjb2Rlci5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaXNzIjoiaHR0cHM6Ly9hdXRoLnRvcGNvZGVyLWRldi5jb20vIiwic3ViIjoiYXV0aDB8NDAxNTI4NTYiLCJhdWQiOiJCWFdYVVduaWxWVVBkTjAxdDJTZTI5VHcyWllOR1p2SCIsImlhdCI6MTYwMzU0MzMzOCwiZXhwIjozMzE2MDQ1MjczOCwibm9uY2UiOiJSMUEyY3pZdVZUWm1ialpIUkc5MlZsOURTVUo2VWxsdlFYYzNSSGg1UzNaV2RXWkRjRE5YTUVaMVh3PT0ifQ.HbAisH30DLcbFNQeIifSzk1yhDmlGHNpPi9LSZbAowo", "enabled": true }, + { + "key": "token_member_tester1234", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczovL3RvcGNvZGVyLWRldi5jb20vcm9sZXMiOlsiVG9wY29kZXIgVXNlciJdLCJodHRwczovL3RvcGNvZGVyLWRldi5jb20vdXNlcklkIjoiNDAxNTkwOTciLCJodHRwczovL3RvcGNvZGVyLWRldi5jb20vaGFuZGxlIjoidGVzdGVyMTIzNCIsImh0dHBzOi8vdG9wY29kZXItZGV2LmNvbS91c2VyX2lkIjoiYXV0aDB8NDAxNTkwOTciLCJodHRwczovL3RvcGNvZGVyLWRldi5jb20vdGNzc28iOiI0MDE1OTA5N3xhYjZhMTlkYThhZTdjMDY0MjVhMzNmYTMzMTkwNWI2MjIyZTY3ZDQ2MWY5MzEzZjNlZTNjODI2NWZkOWMxNWYiLCJodHRwczovL3RvcGNvZGVyLWRldi5jb20vYWN0aXZlIjp0cnVlLCJuaWNrbmFtZSI6InRlc3RlcjEyMzQiLCJuYW1lIjoic2F0aHlhLmpheWFiYWxAZ21haWwuY29tIiwicGljdHVyZSI6Imh0dHBzOi8vcy5ncmF2YXRhci5jb20vYXZhdGFyLzg1NDNjMGI0OTAyODZmMjViOTgyZTc0ZDlkZjM3MzU4P3M9NDgwJnI9cGcmZD1odHRwcyUzQSUyRiUyRmNkbi5hdXRoMC5jb20lMkZhdmF0YXJzJTJGc2EucG5nIiwidXBkYXRlZF9hdCI6IjIwMjAtMTItMjFUMTE6MDQ6MTkuODg2WiIsImVtYWlsIjoic2F0aHlhLmpheWFiYWxAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8vYXV0aC50b3Bjb2Rlci1kZXYuY29tLyIsInN1YiI6ImF1dGgwfDQwMTU5MDk3IiwiYXVkIjoiQlhXWFVXbmlsVlVQZE4wMXQyU2UyOVR3MlpZTkdadkgiLCJpYXQiOjE2MDg1NDg2NjEsImV4cCI6MjE0NzQ4MzY0OCwibm9uY2UiOiJTVlowTVhwTlRqTkVUSFJQTkZNeU9HdG9SazFLUlhNMlVsTm1iM0ptVTBkRmJrSk9lVlp2TlM1MGVBPT0ifQ.l_SqklfrEJBBdxEw1R0mszRhY8jZMAUOvUh_mpyihkM", + "enabled": true + }, { "key": "token_userId_not_exist", "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik5VSkZORGd4UlRVME5EWTBOVVkzTlRkR05qTXlRamxETmpOQk5UYzVRVUV3UlRFeU56TTJRUSJ9.eyJodHRwczovL3RvcGNvZGVyLWRldi5jb20vcm9sZXMiOlsiVG9wY29kZXIgVXNlciIsImNvcGlsb3QiLCJDb25uZWN0IE1hbmFnZXIiLCJib29raW5nbWFuYWdlciIsInUtYmFobiJdLCJodHRwczovL3RvcGNvZGVyLWRldi5jb20vdXNlcklkIjoibm90X2V4aXN0IiwiaHR0cHM6Ly90b3Bjb2Rlci1kZXYuY29tL2hhbmRsZSI6InBzaGFoX21hbmFnZXIiLCJodHRwczovL3RvcGNvZGVyLWRldi5jb20vdXNlcl9pZCI6ImF1dGgwfDQwMTUyODU2IiwiaHR0cHM6Ly90b3Bjb2Rlci1kZXYuY29tL3Rjc3NvIjoiNDAxNTI4NTZ8ODEzNGY0OGViZTExYTg0OGEzNzU5ZTVlZjllOTJmMjE0NjkyZTIxMTMwNDBjODJiNWQ4ZjU4MWM2ZGZjY2M4OCIsImh0dHBzOi8vdG9wY29kZXItZGV2LmNvbS9hY3RpdmUiOnRydWUsIm5pY2tuYW1lIjoicHNoYWhfbWFuYWdlciIsIm5hbWUiOiJ2aWthcy5hZ2Fyd2FsK3BzaGFoX21hbmFnZXJAdG9wY29kZXIuY29tIiwicGljdHVyZSI6Imh0dHBzOi8vcy5ncmF2YXRhci5jb20vYXZhdGFyLzkyYWZiMmYwZWQ1MmZkZmFlMWYzNzEwMjFhZTY1MDEzP3M9NDgwJnI9cGcmZD1odHRwcyUzQSUyRiUyRmNkbi5hdXRoMC5jb20lMkZhdmF0YXJzJTJGdmkucG5nIiwidXBkYXRlZF9hdCI6IjIwMjAtMTAtMjRUMDg6Mjg6MjQuMTg0WiIsImVtYWlsIjoidmlrYXMuYWdhcndhbCtwc2hhaF9tYW5hZ2VyQHRvcGNvZGVyLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpc3MiOiJodHRwczovL2F1dGgudG9wY29kZXItZGV2LmNvbS8iLCJzdWIiOiJhdXRoMHw0MDE1Mjg1NiIsImF1ZCI6IkJYV1hVV25pbFZVUGROMDF0MlNlMjlUdzJaWU5HWnZIIiwiaWF0IjoxNjAzNTQzMzM4LCJleHAiOjMzMTYwNDUyNzM4LCJub25jZSI6IlIxQTJjell1VlRabWJqWkhSRzkyVmw5RFNVSjZVbGx2UVhjM1JIaDVTM1pXZFdaRGNETlhNRVoxWHc9PSJ9.411P_6dTLAJ8TuwdUeyo9Pggzmmtjv37trsvK7ydWns", @@ -47,6 +57,11 @@ "value": "111", "enabled": true }, + { + "key": "project_id_16718", + "value": "16718", + "enabled": true + }, { "key": "jobIdCreatedByMember", "value": "", @@ -151,4 +166,4 @@ "_postman_variable_scope": "environment", "_postman_exported_at": "2020-12-04T12:09:30.809Z", "_postman_exported_using": "Postman/7.29.0" -} \ No newline at end of file +} diff --git a/src/common/helper.js b/src/common/helper.js index 715d9981..654a10df 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -175,26 +175,6 @@ function clearObject (obj) { } } -/** - * Check whether connect member or not - * @param {Number} projectId the project id - * @param {String} jwtToken the jwt token - * @param {Boolean} - */ -async function isConnectMember (projectId, jwtToken) { - const url = `${config.PROJECT_API_URL}/v5/projects/${projectId}` - try { - await request - .get(url) - .set('Authorization', jwtToken) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json') - } catch (err) { - return false - } - return true -} - /** * Get ES Client * @return {Object} Elastic Host Client Instance @@ -336,7 +316,7 @@ function isDocumentMissingException (err) { */ async function getProjects (currentUser, criteria = {}) { let token - if (currentUser.isBookingManager || currentUser.isMachine) { + if (currentUser.hasManagePermission || currentUser.isMachine) { const m2mToken = await getM2Mtoken() token = `Bearer ${m2mToken}` } else { @@ -467,7 +447,7 @@ async function getMembers (handles) { */ async function getProjectById (currentUser, id) { let token - if (currentUser.isBookingManager || currentUser.isMachine) { + if (currentUser.hasManagePermission || currentUser.isMachine) { const m2mToken = await getM2Mtoken() token = `Bearer ${m2mToken}` } else { @@ -484,7 +464,7 @@ async function getProjectById (currentUser, id) { return _.pick(res.body, ['id', 'name']) } catch (err) { if (err.status === HttpStatus.FORBIDDEN) { - throw new errors.UnauthorizedError(`You are not allowed to access the project with id ${id}`) + throw new errors.ForbiddenError(`You are not allowed to access the project with id ${id}`) } if (err.status === HttpStatus.NOT_FOUND) { throw new errors.NotFoundError(`id: ${id} project not found`) @@ -594,7 +574,6 @@ module.exports = { autoWrapExpress, setResHeaders, clearObject, - isConnectMember, getESClient, getUserId: async (userId) => { // check m2m user id diff --git a/src/controllers/JobCandidateController.js b/src/controllers/JobCandidateController.js index 1b1cf971..4f81c7ec 100644 --- a/src/controllers/JobCandidateController.js +++ b/src/controllers/JobCandidateController.js @@ -11,7 +11,7 @@ const helper = require('../common/helper') * @param res the response */ async function getJobCandidate (req, res) { - res.send(await service.getJobCandidate(req.params.id, req.query.fromDb)) + res.send(await service.getJobCandidate(req.authUser, req.params.id, req.query.fromDb)) } /** @@ -57,7 +57,7 @@ async function deleteJobCandidate (req, res) { * @param res the response */ async function searchJobCandidates (req, res) { - const result = await service.searchJobCandidates(req.query) + const result = await service.searchJobCandidates(req.authUser, req.query) helper.setResHeaders(req, res, result) res.send(result.result) } diff --git a/src/controllers/JobController.js b/src/controllers/JobController.js index f15f0a66..14f5cfc5 100644 --- a/src/controllers/JobController.js +++ b/src/controllers/JobController.js @@ -11,7 +11,7 @@ const helper = require('../common/helper') * @param res the response */ async function getJob (req, res) { - res.send(await service.getJob(req.params.id, req.query.fromDb)) + res.send(await service.getJob(req.authUser, req.params.id, req.query.fromDb)) } /** @@ -57,7 +57,7 @@ async function deleteJob (req, res) { * @param res the response */ async function searchJobs (req, res) { - const result = await service.searchJobs(req.query) + const result = await service.searchJobs(req.authUser, req.query) helper.setResHeaders(req, res, result) res.send(result.result) } diff --git a/src/controllers/ResourceBookingController.js b/src/controllers/ResourceBookingController.js index d222adc5..098fd8e5 100644 --- a/src/controllers/ResourceBookingController.js +++ b/src/controllers/ResourceBookingController.js @@ -57,7 +57,7 @@ async function deleteResourceBooking (req, res) { * @param res the response */ async function searchResourceBookings (req, res) { - const result = await service.searchResourceBookings(req.query) + const result = await service.searchResourceBookings(req.authUser, req.query) helper.setResHeaders(req, res, result) res.send(result.result) } diff --git a/src/models/JobCandidate.js b/src/models/JobCandidate.js index e6761eaa..285d6f36 100644 --- a/src/models/JobCandidate.js +++ b/src/models/JobCandidate.js @@ -33,15 +33,6 @@ module.exports = (sequelize) => { } return jobCandidate } - - static async getProjectId (jobId) { - try { - const job = await JobCandidate._models.Job.findById(jobId) - return job.dataValues.projectId - } catch (error) { - return null - } - } } JobCandidate.init( { diff --git a/src/services/JobCandidateService.js b/src/services/JobCandidateService.js index d48e409f..106ac1c6 100644 --- a/src/services/JobCandidateService.js +++ b/src/services/JobCandidateService.js @@ -5,44 +5,72 @@ const _ = require('lodash') const Joi = require('joi') const config = require('config') +const HttpStatus = require('http-status-codes') const { Op } = require('sequelize') const { v4: uuid } = require('uuid') const helper = require('../common/helper') const logger = require('../common/logger') const errors = require('../common/errors') const models = require('../models') +const JobService = require('./JobService') const JobCandidate = models.JobCandidate const esClient = helper.getESClient() +/** + * Check whether user can access associated job of a candidate. + * + * @param {Object} currentUser the user who perform this operation. + * @param {String} jobId the job id + * @returns {undefined} + */ +async function _checkUserAccessAssociatedJob (currentUser, jobId) { + if (!currentUser.hasManagePermission && !currentUser.isMachine && !currentUser.isConnectManager) { + await JobService.getJob(currentUser, jobId) + } +} + /** * Get jobCandidate by id + * @param {Object} currentUser the user who perform this operation. * @param {String} id the jobCandidate id * @param {Boolean} fromDb flag if query db for data or not * @returns {Object} the jobCandidate */ -async function getJobCandidate (id, fromDb = false) { +async function getJobCandidate (currentUser, id, fromDb = false) { if (!fromDb) { try { const jobCandidate = await esClient.get({ index: config.esConfig.ES_INDEX_JOB_CANDIDATE, id }) + + // check whether user can access the job associated with the jobCandidate + await _checkUserAccessAssociatedJob(currentUser, jobCandidate.body._source.jobId) + const jobCandidateRecord = { id: jobCandidate.body._id, ...jobCandidate.body._source } return jobCandidateRecord } catch (err) { if (helper.isDocumentMissingException(err)) { throw new errors.NotFoundError(`id: ${id} "JobCandidate" not found`) } + if (err.httpStatus === HttpStatus.FORBIDDEN) { + throw err + } logger.logFullError(err, { component: 'JobCandidateService', context: 'getJobCandidate' }) } } logger.info({ component: 'JobCandidateService', context: 'getJobCandidate', message: 'try to query db for data' }) const jobCandidate = await JobCandidate.findById(id) + + // check whether user can access the job associated with the jobCandidate + await _checkUserAccessAssociatedJob(currentUser, jobCandidate.jobId) + return helper.clearObject(jobCandidate.dataValues) } getJobCandidate.schema = Joi.object().keys({ + currentUser: Joi.object().required(), id: Joi.string().guid().required(), fromDb: Joi.boolean() }).required() @@ -54,6 +82,10 @@ getJobCandidate.schema = Joi.object().keys({ * @returns {Object} the created jobCandidate */ async function createJobCandidate (currentUser, jobCandidate) { + if (!currentUser.hasManagePermission && !currentUser.isMachine) { + throw new errors.ForbiddenError('You are not allowed to perform this action!') + } + await helper.ensureJobById(jobCandidate.jobId) // ensure job exists await helper.ensureUserById(jobCandidate.userId) // ensure user exists @@ -84,14 +116,17 @@ createJobCandidate.schema = Joi.object().keys({ */ async function updateJobCandidate (currentUser, id, data) { const jobCandidate = await JobCandidate.findById(id) - const projectId = await JobCandidate.getProjectId(jobCandidate.dataValues.jobId) + const userId = await helper.getUserId(currentUser.userId) - if (projectId && !currentUser.isBookingManager && !currentUser.isMachine) { - const connect = await helper.isConnectMember(projectId, currentUser.jwtToken) - if (!connect) { - if (jobCandidate.dataValues.userId !== userId) { - throw new errors.ForbiddenError('You are not allowed to perform this action!') - } + if (!currentUser.hasManagePermission && !currentUser.isMachine) { + if (currentUser.isConnectManager) { + throw new errors.ForbiddenError('You are not allowed to perform this action!') + } + // check whether user can access the job associated with the jobCandidate + await JobService.getJob(currentUser, jobCandidate.dataValues.jobId) + // check whether user are allowed to update the candidate + if (jobCandidate.dataValues.userId !== userId) { + throw new errors.ForbiddenError('You are not allowed to perform this action!') } } data.updatedAt = new Date() @@ -151,7 +186,7 @@ fullyUpdateJobCandidate.schema = Joi.object().keys({ * @params {String} id the jobCandidate id */ async function deleteJobCandidate (currentUser, id) { - if (!currentUser.isBookingManager && !currentUser.isMachine) { + if (!currentUser.hasManagePermission && !currentUser.isMachine) { throw new errors.ForbiddenError('You are not allowed to perform this action!') } @@ -167,10 +202,20 @@ deleteJobCandidate.schema = Joi.object().keys({ /** * List resourceBookings + * @param {Object} currentUser the user who perform this operation. * @params {Object} criteria the search criteria * @returns {Object} the search result, contain total/page/perPage and result array */ -async function searchJobCandidates (criteria) { +async function searchJobCandidates (currentUser, criteria) { + if (!currentUser.hasManagePermission && !currentUser.isMachine && !currentUser.isConnectManager) { + // regular user can only search with filtering by "jobId" + if (!criteria.jobId) { + throw new errors.ForbiddenError('Not allowed without filtering by "jobId"') + } + // check whether user can access the job associated with the jobCandidate + await JobService.getJob(currentUser, criteria.jobId) + } + const page = criteria.page > 0 ? criteria.page : 1 const perPage = criteria.perPage > 0 ? criteria.perPage : 20 if (!criteria.sortBy) { @@ -248,6 +293,7 @@ async function searchJobCandidates (criteria) { } searchJobCandidates.schema = Joi.object().keys({ + currentUser: Joi.object().required(), criteria: Joi.object().keys({ page: Joi.number().integer(), perPage: Joi.number().integer(), diff --git a/src/services/JobService.js b/src/services/JobService.js index 90c78bf2..f4aa8d26 100644 --- a/src/services/JobService.js +++ b/src/services/JobService.js @@ -74,19 +74,37 @@ async function _validateSkills (skills) { } } +/** + * Check whether user can access associated project of a job. + * + * @param {Object} currentUser the user who perform this operation. + * @param {String} projectId the project id + * @returns {undefined} + */ +async function _checkUserAccessAssociatedProject (currentUser, projectId) { + if (!currentUser.hasManagePermission && !currentUser.isMachine && !currentUser.isConnectManager) { + await helper.getProjectById(currentUser, projectId) + } +} + /** * Get job by id + * @param {Object} currentUser the user who perform this operation. * @param {String} id the job id * @param {Boolean} fromDb flag if query db for data or not * @returns {Object} the job */ -async function getJob (id, fromDb = false) { +async function getJob (currentUser, id, fromDb = false) { if (!fromDb) { try { const job = await esClient.get({ index: config.esConfig.ES_INDEX_JOB, id }) + + // check whether user can access the project associated with the job + await _checkUserAccessAssociatedProject(currentUser, job.body._source.projectId) + const jobId = job.body._id const jobRecord = { id: jobId, ...job.body._source } const candidates = await _getJobCandidates(jobId) @@ -98,16 +116,24 @@ async function getJob (id, fromDb = false) { if (helper.isDocumentMissingException(err)) { throw new errors.NotFoundError(`id: ${id} "Job" not found`) } + if (err.httpStatus === HttpStatus.FORBIDDEN) { + throw err + } logger.logFullError(err, { component: 'JobService', context: 'getJob' }) } } logger.info({ component: 'JobService', context: 'getJob', message: 'try to query db for data' }) const job = await Job.findById(id, true) + + // check whether user can access the project associated with the job + await _checkUserAccessAssociatedProject(currentUser, job.projectId) + job.dataValues.candidates = _.map(job.dataValues.candidates, (c) => helper.clearObject(c.dataValues)) return helper.clearObject(job.dataValues) } getJob.schema = Joi.object().keys({ + currentUser: Joi.object().required(), id: Joi.string().guid().required(), fromDb: Joi.boolean() }).required() @@ -119,6 +145,14 @@ getJob.schema = Joi.object().keys({ * @returns {Object} the created job */ async function createJob (currentUser, job) { + // check if user can access the project + if (!currentUser.hasManagePermission && !currentUser.isMachine) { + if (currentUser.isConnectManager) { + throw new errors.ForbiddenError('You are not allowed to perform this action!') + } + await helper.getProjectById(currentUser, job.projectId) + } + await _validateSkills(job.skills) job.id = uuid() job.createdAt = new Date() @@ -159,12 +193,15 @@ async function updateJob (currentUser, id, data) { } let job = await Job.findById(id) const ubhanUserId = await helper.getUserId(currentUser.userId) - if (!currentUser.isBookingManager && !currentUser.isMachine) { - const connect = await helper.isConnectMember(job.dataValues.projectId, currentUser.jwtToken) - if (!connect) { - if (ubhanUserId !== job.createdBy) { - throw new errors.ForbiddenError('You are not allowed to perform this action!') - } + if (!currentUser.hasManagePermission && !currentUser.isMachine) { + if (currentUser.isConnectManager) { + throw new errors.ForbiddenError('You are not allowed to perform this action!') + } + // Check whether user can update the job. + // Note that there is no need to check if user is member of the project associated with the job here + // because user who created the job must be the member of the project associated with the job + if (ubhanUserId !== job.createdBy) { + throw new errors.ForbiddenError('You are not allowed to perform this action!') } } @@ -235,19 +272,16 @@ fullyUpdateJob.schema = Joi.object().keys({ }).required() /** - * Delete job by id. Normal user can only delete the job he/she created. + * Delete job by id. * @params {Object} currentUser the user who perform this operation * @params {String} id the job id */ async function deleteJob (currentUser, id) { - const job = await Job.findById(id) - if (!currentUser.isBookingManager && !currentUser.isMachine) { - const ubhanUserId = await helper.getUserId(currentUser.userId) - if (ubhanUserId !== job.createdBy) { - throw new errors.ForbiddenError('You are not allowed to perform this action!') - } + if (!currentUser.hasManagePermission && !currentUser.isMachine) { + throw new errors.ForbiddenError('You are not allowed to perform this action!') } + const job = await Job.findById(id) await job.update({ deletedAt: new Date() }) await helper.postEvent(config.TAAS_JOB_DELETE_TOPIC, { id }) } @@ -259,11 +293,21 @@ deleteJob.schema = Joi.object().keys({ /** * List jobs + * @param {Object} currentUser the user who perform this operation. * @params {Object} criteria the search criteria * @params {Object} options the extra options to control the function * @returns {Object} the search result, contain total/page/perPage and result array */ -async function searchJobs (criteria, options = { returnAll: false }) { +async function searchJobs (currentUser, criteria, options = { returnAll: false }) { + if (!currentUser.hasManagePermission && !currentUser.isMachine && !currentUser.isConnectManager) { + // regular user can only search with filtering by "projectId" + if (!criteria.projectId) { + throw new errors.ForbiddenError('Not allowed without filtering by "projectId"') + } + // check if user can access the project + await helper.getProjectById(currentUser, criteria.projectId) + } + const page = criteria.page > 0 ? criteria.page : 1 let perPage if (options.returnAll) { @@ -424,6 +468,7 @@ async function searchJobs (criteria, options = { returnAll: false }) { } searchJobs.schema = Joi.object().keys({ + currentUser: Joi.object().required(), criteria: Joi.object().keys({ page: Joi.number().integer(), perPage: Joi.number().integer(), diff --git a/src/services/ResourceBookingService.js b/src/services/ResourceBookingService.js index 6cc739aa..9fb57ab9 100644 --- a/src/services/ResourceBookingService.js +++ b/src/services/ResourceBookingService.js @@ -5,6 +5,7 @@ const _ = require('lodash') const Joi = require('joi') const config = require('config') +const HttpStatus = require('http-status-codes') const { Op } = require('sequelize') const { v4: uuid } = require('uuid') const helper = require('../common/helper') @@ -23,12 +24,22 @@ const esClient = helper.getESClient() * @returns {Object} the resourceBooking */ async function _getResourceBookingFilteringFields (currentUser, resourceBooking) { - if (currentUser.isBookingManager || currentUser.isMachine) { + if (currentUser.hasManagePermission || currentUser.isMachine) { return helper.clearObject(resourceBooking) - } else if (await helper.isConnectMember(resourceBooking.projectId, currentUser.jwtToken)) { - return _.omit(helper.clearObject(resourceBooking), 'memberRate') - } else { - return _.omit(helper.clearObject(resourceBooking), 'customerRate') + } + return _.omit(helper.clearObject(resourceBooking), 'memberRate') +} + +/** + * Check whether user can access associated project of a job. + * + * @param {Object} currentUser the user who perform this operation. + * @param {String} projectId the project id + * @returns {undefined} + */ +async function _checkUserAccessAssociatedProject (currentUser, projectId) { + if (!currentUser.hasManagePermission && !currentUser.isMachine && !currentUser.isConnectManager) { + await helper.getProjectById(currentUser, projectId) } } @@ -46,17 +57,28 @@ async function getResourceBooking (currentUser, id, fromDb = false) { index: config.esConfig.ES_INDEX_RESOURCE_BOOKING, id }) + + // check if user can access the project associated with the resourceBooking + await _checkUserAccessAssociatedProject(currentUser, resourceBooking.body._source.projectId) + const resourceBookingRecord = { id: resourceBooking.body._id, ...resourceBooking.body._source } return _getResourceBookingFilteringFields(currentUser, resourceBookingRecord) } catch (err) { if (helper.isDocumentMissingException(err)) { throw new errors.NotFoundError(`id: ${id} "ResourceBooking" not found`) } + if (err.httpStatus === HttpStatus.FORBIDDEN) { + throw err + } logger.logFullError(err, { component: 'ResourceBookingService', context: 'getResourceBooking' }) } } logger.info({ component: 'ResourceBookingService', context: 'getResourceBooking', message: 'try to query db for data' }) const resourceBooking = await ResourceBooking.findById(id) + + // check if user can access the project associated with the resourceBooking + await _checkUserAccessAssociatedProject(currentUser, resourceBooking.projectId) + return _getResourceBookingFilteringFields(currentUser, resourceBooking.dataValues) } @@ -73,17 +95,16 @@ getResourceBooking.schema = Joi.object().keys({ * @returns {Object} the created resourceBooking */ async function createResourceBooking (currentUser, resourceBooking) { + // check permission + if (!currentUser.hasManagePermission && !currentUser.isMachine) { + throw new errors.ForbiddenError('You are not allowed to perform this action!') + } + if (resourceBooking.jobId) { await helper.ensureJobById(resourceBooking.jobId) // ensure job exists } await helper.ensureUserById(resourceBooking.userId) // ensure user exists - if (!currentUser.isBookingManager && !currentUser.isMachine) { - const connect = await helper.isConnectMember(resourceBooking.projectId, currentUser.jwtToken) - if (!connect) { - throw new errors.ForbiddenError('You are not allowed to perform this action!') - } - } resourceBooking.id = uuid() resourceBooking.createdAt = new Date() resourceBooking.createdBy = await helper.getUserId(currentUser.userId) @@ -116,14 +137,13 @@ createResourceBooking.schema = Joi.object().keys({ * @returns {Object} the updated resourceBooking */ async function updateResourceBooking (currentUser, id, data) { + // check permission + if (!currentUser.hasManagePermission && !currentUser.isMachine) { + throw new errors.ForbiddenError('You are not allowed to perform this action!') + } + const resourceBooking = await ResourceBooking.findById(id) const isDiffStatus = resourceBooking.status !== data.status - if (!currentUser.isBookingManager && !currentUser.isMachine) { - const connect = await helper.isConnectMember(resourceBooking.dataValues.projectId, currentUser.jwtToken) - if (!connect) { - throw new errors.ForbiddenError('You are not allowed to perform this action!') - } - } data.updatedAt = new Date() data.updatedBy = await helper.getUserId(currentUser.userId) @@ -220,7 +240,8 @@ fullyUpdateResourceBooking.schema = Joi.object().keys({ * @params {String} id the resourceBooking id */ async function deleteResourceBooking (currentUser, id) { - if (!currentUser.isBookingManager && !currentUser.isMachine) { + // check permission + if (!currentUser.hasManagePermission && !currentUser.isMachine) { throw new errors.ForbiddenError('You are not allowed to perform this action!') } @@ -236,11 +257,21 @@ deleteResourceBooking.schema = Joi.object().keys({ /** * List resourceBookings + * @param {Object} currentUser the user who perform this operation. * @params {Object} criteria the search criteria * @params {Object} options the extra options to control the function * @returns {Object} the search result, contain total/page/perPage and result array */ -async function searchResourceBookings (criteria, options = { returnAll: false }) { +async function searchResourceBookings (currentUser, criteria, options = { returnAll: false }) { + if (!currentUser.hasManagePermission && !currentUser.isMachine && !currentUser.isConnectManager) { + // regular user can only search with filtering by "projectId" + if (!criteria.projectId) { + throw new errors.ForbiddenError('Not allowed without filtering by "projectId"') + } + // check if user can access the project + await helper.getProjectById(currentUser, criteria.projectId) + } + // `criteria`.projectIds` could be array of ids, or comma separated string of ids // in case it's comma separated string of ids we have to convert it to an array of ids if ((typeof criteria.projectIds) === 'string') { @@ -351,6 +382,7 @@ async function searchResourceBookings (criteria, options = { returnAll: false }) } searchResourceBookings.schema = Joi.object().keys({ + currentUser: Joi.object().required(), criteria: Joi.object().keys({ page: Joi.number().integer(), perPage: Joi.number().integer(),