diff --git a/.circleci/config.yml b/.circleci/config.yml
index 101a1bc599..7985d4ba23 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -340,7 +340,7 @@ workflows:
filters:
branches:
only:
- - develop
+ - recruit-proxy-api-aggapi
# This is alternate dev env for parallel testing
- "build-test":
context : org-global
@@ -368,7 +368,7 @@ workflows:
filters: &filters-staging
branches:
only:
- - develop
+ - recruit-proxy-api-aggapi
# Production builds are exectuted
# when PR is merged to the master
# Don't change anything in this configuration
diff --git a/Dockerfile b/Dockerfile
index 902b754e09..72ffa4d939 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -64,6 +64,7 @@ ARG TC_M2M_GRANT_TYPE
ARG TC_M2M_AUTH0_PROXY_SERVER_URL
ARG TC_M2M_AUTH0_URL
ARG AUTH_SECRET
+ARG VALID_ISSUERS
ARG COMMUNITY_APP_URL
ARG GSHEETS_API_KEY
@@ -124,6 +125,7 @@ ENV TC_M2M_GRANT_TYPE=$TC_M2M_GRANT_TYPE
ENV TC_M2M_AUTH0_PROXY_SERVER_URL=$TC_M2M_AUTH0_PROXY_SERVER_URL
ENV TC_M2M_AUTH0_URL=$TC_M2M_AUTH0_URL
ENV AUTH_SECRET=$AUTH_SECRET
+ENV VALID_ISSUERS=$VALID_ISSUERS
ENV CONTENTFUL_MANAGEMENT_TOKEN=$CONTENTFUL_MANAGEMENT_TOKEN
ENV CONTENTFUL_EDU_SPACE_ID=$CONTENTFUL_EDU_SPACE_ID
diff --git a/build.sh b/build.sh
index 725701d465..23730d8013 100755
--- a/build.sh
+++ b/build.sh
@@ -51,7 +51,8 @@ docker build -t $TAG \
--build-arg GROWSURF_CAMPAIGN_ID=$GROWSURF_CAMPAIGN_ID \
--build-arg GSHEETS_API_KEY=$GSHEETS_API_KEY \
--build-arg OPTIMIZELY_SDK_KEY=$OPTIMIZELY_SDK_KEY \
- --build-arg COMMUNITY_APP_URL=$COMMUNITY_APP_URL .
+ --build-arg COMMUNITY_APP_URL=$COMMUNITY_APP_URL \
+ --build-arg VALID_ISSUERS=$VALID_ISSUERS .
# Copies "node_modules" from the created image, if necessary for caching.
docker create --name app $TAG
diff --git a/config/custom-environment-variables.js b/config/custom-environment-variables.js
index 75e275d959..75169457dd 100644
--- a/config/custom-environment-variables.js
+++ b/config/custom-environment-variables.js
@@ -83,6 +83,7 @@ module.exports = {
JWT_AUTH: {
SECRET: 'AUTH_SECRET',
+ AUTH_SECRET: 'AUTH_SECRET',
VALID_ISSUERS: 'VALID_ISSUERS',
},
diff --git a/config/default.js b/config/default.js
index e2946ddf77..bc183806e0 100644
--- a/config/default.js
+++ b/config/default.js
@@ -429,5 +429,5 @@ module.exports = {
OPTIMIZELY: {
SDK_KEY: '7V4CJhurXT3Y3bnzv1hv1',
},
- PLATFORM_SITE_URL: 'https://platform.topcoder.com',
+ PLATFORM_SITE_URL: 'https://platform.topcoder-dev.com',
};
diff --git a/config/production.js b/config/production.js
index f83da0fbc1..002b8340d8 100644
--- a/config/production.js
+++ b/config/production.js
@@ -214,4 +214,5 @@ module.exports = {
TC_EDU_SEARCH_PATH: '/search',
TC_EDU_SEARCH_BAR_MAX_RESULTS_EACH_GROUP: 3,
ENABLE_RECOMMENDER: true,
+ PLATFORM_SITE_URL: 'https://platform.topcoder.com',
};
diff --git a/docs/postman/Community APP.postman_collection.json b/docs/postman/Community APP.postman_collection.json
new file mode 100644
index 0000000000..d0b3586dcb
--- /dev/null
+++ b/docs/postman/Community APP.postman_collection.json
@@ -0,0 +1,549 @@
+{
+ "info": {
+ "_postman_id": "95d8a75d-2520-494b-becd-f9f2e8537707",
+ "name": "Community APP",
+ "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json"
+ },
+ "item": [
+ {
+ "name": "Recruit Api",
+ "item": [
+ {
+ "name": "Profile",
+ "item": [
+ {
+ "name": "get profile successfully",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Status code is 200', function () {\r",
+ " pm.response.to.have.status(200);\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{token}}",
+ "type": "text"
+ }
+ ],
+ "url": "{{URL}}/recruit/profile"
+ },
+ "response": []
+ },
+ {
+ "name": "get profile with invalid token 1",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Status code is 403', function () {\r",
+ " pm.response.to.have.status(403);\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{invalid_token_1}}",
+ "type": "text"
+ }
+ ],
+ "url": "{{URL}}/recruit/profile"
+ },
+ "response": []
+ },
+ {
+ "name": "get profile with invalid token 2",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Status code is 403', function () {\r",
+ " pm.response.to.have.status(403);\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{invalid_token_2}}",
+ "type": "text"
+ }
+ ],
+ "url": "{{URL}}/recruit/profile"
+ },
+ "response": []
+ },
+ {
+ "name": "get profile with invalid token 3",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Status code is 403', function () {\r",
+ " pm.response.to.have.status(403);\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{invalid_token_3}}",
+ "type": "text"
+ }
+ ],
+ "url": "{{URL}}/recruit/profile"
+ },
+ "response": []
+ },
+ {
+ "name": "get profile with invalid token 4",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Status code is 403', function () {\r",
+ " pm.response.to.have.status(403);\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{invalid_token_4}}",
+ "type": "text"
+ }
+ ],
+ "url": "{{URL}}/recruit/profile"
+ },
+ "response": []
+ },
+ {
+ "name": "update profile successfully",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Status code is 204', function () {\r",
+ " pm.response.to.have.status(204);\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{token}}",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "resume",
+ "type": "file",
+ "src": []
+ },
+ {
+ "key": "phone",
+ "value": "+1123226666",
+ "type": "text"
+ },
+ {
+ "key": "availability",
+ "value": "false",
+ "type": "text"
+ },
+ {
+ "key": "city",
+ "value": "ABC",
+ "type": "text"
+ },
+ {
+ "key": "countryName",
+ "value": "India",
+ "type": "text"
+ }
+ ]
+ },
+ "url": "{{URL}}/recruit/profile"
+ },
+ "response": []
+ },
+ {
+ "name": "update profile with invalid token 1",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Status code is 403', function () {\r",
+ " pm.response.to.have.status(403);\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{invalid_token_1}}",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "resume",
+ "type": "file",
+ "src": "./docs/postman/resume.txt"
+ },
+ {
+ "key": "phone",
+ "value": "+1123226666",
+ "type": "text"
+ },
+ {
+ "key": "availability",
+ "value": "true",
+ "type": "text"
+ }
+ ]
+ },
+ "url": "{{URL}}/recruit/profile"
+ },
+ "response": []
+ },
+ {
+ "name": "update profile with invalid token 2",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Status code is 403', function () {\r",
+ " pm.response.to.have.status(403);\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{invalid_token_2}}",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "resume",
+ "type": "file",
+ "src": "./docs/postman/resume.txt"
+ },
+ {
+ "key": "phone",
+ "value": "+1123226666",
+ "type": "text"
+ },
+ {
+ "key": "availability",
+ "value": "true",
+ "type": "text"
+ }
+ ]
+ },
+ "url": "{{URL}}/recruit/profile"
+ },
+ "response": []
+ },
+ {
+ "name": "update profile with invalid token 3",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Status code is 403', function () {\r",
+ " pm.response.to.have.status(403);\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{invalid_token_3}}",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "resume",
+ "type": "file",
+ "src": "./docs/postman/resume.txt"
+ },
+ {
+ "key": "phone",
+ "value": "+1123226666",
+ "type": "text"
+ },
+ {
+ "key": "availability",
+ "value": "true",
+ "type": "text"
+ }
+ ]
+ },
+ "url": "{{URL}}/recruit/profile"
+ },
+ "response": []
+ },
+ {
+ "name": "update profile with invalid token 4",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Status code is 403', function () {\r",
+ " pm.response.to.have.status(403);\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{invalid_token_4}}",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "resume",
+ "type": "file",
+ "src": "./docs/postman/resume.txt"
+ },
+ {
+ "key": "phone",
+ "value": "+1123226666",
+ "type": "text"
+ },
+ {
+ "key": "availability",
+ "value": "true",
+ "type": "text"
+ }
+ ]
+ },
+ "url": "{{URL}}/recruit/profile"
+ },
+ "response": []
+ },
+ {
+ "name": "update profile with invalid field",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Status code is 400', function () {\r",
+ " pm.response.to.have.status(400);\r",
+ " const response = pm.response.json()\r",
+ " pm.expect(response.message).to.eq(\"\\\"availability\\\" must be a boolean\")\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{token}}",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "resume",
+ "type": "file",
+ "src": "./docs/postman/resume.txt"
+ },
+ {
+ "key": "phone",
+ "value": "+1123226666",
+ "type": "text"
+ },
+ {
+ "key": "availability",
+ "value": "asdd",
+ "type": "text"
+ }
+ ]
+ },
+ "url": "{{URL}}/recruit/profile"
+ },
+ "response": []
+ },
+ {
+ "name": "update profile with missing field 1",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Status code is 400', function () {\r",
+ " pm.response.to.have.status(400);\r",
+ " const response = pm.response.json()\r",
+ " pm.expect(response.message).to.eq(\"\\\"phone\\\" is required\")\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{token}}",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "resume",
+ "type": "file",
+ "src": "./docs/postman/resume.txt"
+ },
+ {
+ "key": "availability",
+ "value": "asdd",
+ "type": "text"
+ }
+ ]
+ },
+ "url": "{{URL}}/recruit/profile"
+ },
+ "response": []
+ },
+ {
+ "name": "update profile with missing field 2",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Status code is 400', function () {\r",
+ " pm.response.to.have.status(400);\r",
+ " const response = pm.response.json()\r",
+ " pm.expect(response.message).to.eq(\"\\\"availability\\\" is required\")\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{token}}",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "resume",
+ "type": "file",
+ "src": "./docs/postman/resume.txt"
+ },
+ {
+ "key": "phone",
+ "value": "+1123226666",
+ "type": "text"
+ }
+ ]
+ },
+ "url": "{{URL}}/recruit/profile"
+ },
+ "response": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/postman/Community APP.postman_environment.json b/docs/postman/Community APP.postman_environment.json
new file mode 100644
index 0000000000..5e5fc77710
--- /dev/null
+++ b/docs/postman/Community APP.postman_environment.json
@@ -0,0 +1,39 @@
+{
+ "id": "c92b4b6b-ed64-4879-91de-e293128256b0",
+ "name": "Community APP",
+ "values": [
+ {
+ "key": "URL",
+ "value": "localhost:3000/api",
+ "enabled": true
+ },
+ {
+ "key": "token",
+ "value": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik5VSkZORGd4UlRVME5EWTBOVVkzTlRkR05qTXlRamxETmpOQk5UYzVRVUV3UlRFeU56TTJRUSJ9.eyJodHRwczovL3RvcGNvZGVyLWRldi5jb20vcm9sZXMiOlsiVG9wY29kZXIgVXNlciJdLCJodHRwczovL3RvcGNvZGVyLWRldi5jb20vdXNlcklkIjoiODg3NzQ2MzQiLCJodHRwczovL3RvcGNvZGVyLWRldi5jb20vaGFuZGxlIjoiaXNiaWxpciIsImh0dHBzOi8vdG9wY29kZXItZGV2LmNvbS91c2VyX2lkIjoiYXV0aDB8ODg3NzQ2MzQiLCJodHRwczovL3RvcGNvZGVyLWRldi5jb20vdGNzc28iOiI4ODc3NDYzNHw0ZDczMWRjODNiZTk5OThkOWE1MmUyOTA2OThmNGIwNGZiMmVjNjE1OTliODIxZmYxNjRjYWEzYzhhNmU3IiwiaHR0cHM6Ly90b3Bjb2Rlci1kZXYuY29tL2FjdGl2ZSI6dHJ1ZSwibmlja25hbWUiOiJpc2JpbGlyIiwibmFtZSI6ImVtcmUuaXNiaWxpckBnbWFpbC5jb20iLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvODE3NjNjMzE0ZGU0Y2ZiNGUxNDRhYzU3M2U1NmMxZjY_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZlbS5wbmciLCJ1cGRhdGVkX2F0IjoiMjAyMS0wNi0xOVQxNzowNjoyMi4yMzVaIiwiZW1haWwiOiJlbXJlLmlzYmlsaXJAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8vYXV0aC50b3Bjb2Rlci1kZXYuY29tLyIsInN1YiI6ImF1dGgwfDg4Nzc0NjM0IiwiYXVkIjoiQlhXWFVXbmlsVlVQZE4wMXQyU2UyOVR3MlpZTkdadkgiLCJpYXQiOjE2MjQxMjUzMzYsImV4cCI6MTYyNDEyODMzNiwibm9uY2UiOiJjVE5DTFVwRlpXMVJhMm94WkhSdE5WaGpVbk5IVkZGVFdWTjRiRzFCYVVaQ1dHZGpSVWhZVTB0RVZnPT0ifQ.SfKTYecx7A2qbqM3v8lJaC1GSdctNhkTx6laAhe2BJYVX5LdVL82ZM3_9yWqqZDPWHS9ku408Jt1DUMo5vRev34Av0iwJpYIT_4BbmMLeWQGQ3aRB78zp_emkZ5TE1vOnfSJvfsPoQoiLIwohyOUaDzz9MGdjrgYuSEAA1RGdjhVcamFlNNbR-lNMffnWYs0oWi89N5kgvv37JZjpDPAvQbotNT80Vs0JrLr1p71JLFcLFTQJ-bthFmt0922dj47-1MSjUQgOzFMN4JNjZKGtExyLGR7s_ipCQxg9lxHjMD-JNwg0liI20YGfTXZv9QQQ_K2jPkXiatHBQq_v2_Qjg",
+ "enabled": true
+ },
+ {
+ "key": "invalid_token_1",
+ "value": "eyJ",
+ "enabled": true
+ },
+ {
+ "key": "invalid_token_2",
+ "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJjb3BpbG90IiwiQ29ubmVjdCBTdXBwb3J0Il0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJHaG9zdGFyIiwiZXhwIjoxNTQ5ODAwMDc3LCJ1c2VySWQiOiIxNTE3NDMiLCJpYXQiOjE1NDk3OTk0NzcsImVtYWlsIjoiZW1haWxAZG9tYWluLmNvbS56IiwianRpIjoiMTJjMWMxMGItOTNlZi00NTMxLTgzMDUtYmE2NjVmYzRlMWI0In0.2n8k9pb16sE7LOLF_7mjAvEVKgggzS-wS3_8n2-R4RU",
+ "enabled": true
+ },
+ {
+ "key": "invalid_token_3",
+ "value": "",
+ "enabled": true
+ },
+ {
+ "key": "invalid_token_4",
+ "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJjb3BpbG90IiwiQ29ubmVjdCBTdXBwb3J0Il0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJHaG9zdGFyIiwiZXhwIjoxNTQ5ODAwMDc3LCJ1c2VySWQiOiIxNTE3NDMiLCJpYXQiOjE1NDk3OTk0NzcsImVtYWlsIjoiZW1haWxAZG9tYWluLmNvbS56IiwianRpIjoiMTJjMWMxMGItOTNlZi00NTMxLTgzMDUtYmE2NjVmYzRlMWI0In0.2n8k9pb16sE7LOLF_7mjAvEVKgggzS-wS3_8n2-R4RU",
+ "enabled": true
+ }
+ ],
+ "_postman_variable_scope": "environment",
+ "_postman_exported_at": "2021-06-19T18:52:31.778Z",
+ "_postman_exported_using": "Postman/8.6.2"
+}
\ No newline at end of file
diff --git a/docs/postman/resume.txt b/docs/postman/resume.txt
new file mode 100644
index 0000000000..a0d9880d43
--- /dev/null
+++ b/docs/postman/resume.txt
@@ -0,0 +1 @@
+My Resume
\ No newline at end of file
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
new file mode 100644
index 0000000000..abd8729743
--- /dev/null
+++ b/docs/swagger.yaml
@@ -0,0 +1,189 @@
+openapi: 3.0.0
+info:
+ title: Community APP
+ description: Community APP
+ version: 1.0.0
+servers:
+ - url: http://local.topcoder-dev.com:3000
+tags:
+ - name: Profile
+paths:
+ /api/recruit/profile:
+ get:
+ tags:
+ - Profile
+ description: |
+ Get Profile of current user
+
+ **Authorization** All topcoder members are allowed.
+ security:
+ - bearerAuth: []
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/Profile"
+ "401":
+ description: Not authenticated
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/AuthError"
+ "403":
+ description: Forbidden
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/AuthError"
+ "404":
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Profile"
+ "500":
+ description: Internal Server Error
+ content:
+ text/plain::
+ schema:
+ type: string
+ post:
+ tags:
+ - Profile
+ description: |
+ Update Profile details of current user
+ **Authorization** All topcoder members are allowed.
+ security:
+ - bearerAuth: []
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ $ref: "#/components/schemas/ProfileUpdate"
+ responses:
+ "204":
+ description: OK
+ "400":
+ description: Bad request
+ content:
+ application/json:
+ schema:
+ oneOf:
+ - $ref: "#/components/schemas/JoiError"
+ - $ref: "#/components/schemas/Error"
+ "401":
+ description: Not authenticated
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/AuthError"
+ "403":
+ description: Forbidden
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/AuthError"
+ "404":
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ "500":
+ description: Internal Server Error
+ content:
+ text/plain::
+ schema:
+ type: string
+components:
+ securitySchemes:
+ bearerAuth:
+ type: http
+ scheme: bearer
+ bearerFormat: JWT
+ schemas:
+ Profile:
+ required:
+ - availability
+ properties:
+ phone:
+ type: string
+ description: "The phone number of the user"
+ example: "+1123226666"
+ resume:
+ type: string
+ description: "The resume of the user"
+ availability:
+ type: boolean
+ description: "The availability of the user"
+ default: true
+ example: true
+ hasProfile:
+ type: boolean
+ description: "Whether has profile for the user"
+ ProfileUpdate:
+ required:
+ - phone
+ - availability
+ - city
+ - countryName
+ properties:
+ phone:
+ type: string
+ description: "The phone number of the user"
+ example: "(123) 456-7890"
+ city:
+ type: string
+ description: "The member's city"
+ countryName:
+ type: string
+ description: "The member's country"
+ resume:
+ type: string
+ format: binary
+ description: "The resume file of the user"
+ availability:
+ type: boolean
+ description: "The availability of the user"
+ example: true
+ Error:
+ properties:
+ error:
+ type: boolean
+ example: true
+ status:
+ type: integer
+ example: 404
+ url:
+ type: string
+ format: uri
+ errObj:
+ type: object
+ JoiError:
+ required:
+ - message
+ properties:
+ message:
+ type: string
+ AuthError:
+ properties:
+ version:
+ type: string
+ result:
+ type: object
+ properties:
+ success:
+ type: boolean
+ example: false
+ status:
+ type: integer
+ example: 403
+ content:
+ type: object
+ properties:
+ message:
+ type: string
diff --git a/package-lock.json b/package-lock.json
index d0f67f50af..d69bbb5673 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -729,8 +729,8 @@
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"requires": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
+ "buffer-from": "1.1.1",
+ "source-map": "0.6.1"
}
}
}
@@ -1333,6 +1333,144 @@
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==",
"dev": true
},
+ "@optimizely/js-sdk-datafile-manager": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@optimizely/js-sdk-datafile-manager/-/js-sdk-datafile-manager-0.8.1.tgz",
+ "integrity": "sha512-zMfyXQUqJlPoFGTNvreGSneGRnr5hn4jp03ofipIpA/RONNsf7DEi/H/uC4pAZxlYm1r5eHZRwKU6gwZTB31LQ==",
+ "requires": {
+ "@optimizely/js-sdk-logging": "0.1.0",
+ "@optimizely/js-sdk-utils": "0.4.0",
+ "decompress-response": "4.2.1"
+ },
+ "dependencies": {
+ "@optimizely/js-sdk-utils": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@optimizely/js-sdk-utils/-/js-sdk-utils-0.4.0.tgz",
+ "integrity": "sha512-QG2oytnITW+VKTJK+l0RxjaS5VrA6W+AZMzpeg4LCB4Rn4BEKtF+EcW/5S1fBDLAviGq/0TLpkjM3DlFkJ9/Gw==",
+ "requires": {
+ "uuid": "3.4.0"
+ }
+ },
+ "decompress-response": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
+ "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
+ "requires": {
+ "mimic-response": "2.1.0"
+ }
+ },
+ "mimic-response": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
+ "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA=="
+ }
+ }
+ },
+ "@optimizely/js-sdk-event-processor": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/@optimizely/js-sdk-event-processor/-/js-sdk-event-processor-0.8.2.tgz",
+ "integrity": "sha512-5sVcQFqgKF0R+vJbBXy6ykKTlEfll0Ti0xGeKU3TLILRNvPDxTpVAlyrLfBC/yfF/hopjRPusGp3z9lZnVej0w==",
+ "requires": {
+ "@optimizely/js-sdk-logging": "0.1.0",
+ "@optimizely/js-sdk-utils": "0.4.0"
+ },
+ "dependencies": {
+ "@optimizely/js-sdk-utils": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@optimizely/js-sdk-utils/-/js-sdk-utils-0.4.0.tgz",
+ "integrity": "sha512-QG2oytnITW+VKTJK+l0RxjaS5VrA6W+AZMzpeg4LCB4Rn4BEKtF+EcW/5S1fBDLAviGq/0TLpkjM3DlFkJ9/Gw==",
+ "requires": {
+ "uuid": "3.4.0"
+ }
+ }
+ }
+ },
+ "@optimizely/js-sdk-logging": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@optimizely/js-sdk-logging/-/js-sdk-logging-0.1.0.tgz",
+ "integrity": "sha512-Bs2zHvsdNIk2QSg05P6mKIlROHoBIRNStbrVwlePm603CucojKRPlFJG4rt7sFZQOo8xS8I7z1BmE4QI3/ZE9A==",
+ "requires": {
+ "@optimizely/js-sdk-utils": "0.1.0"
+ }
+ },
+ "@optimizely/js-sdk-utils": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@optimizely/js-sdk-utils/-/js-sdk-utils-0.1.0.tgz",
+ "integrity": "sha512-p7499GgVaX94YmkrwOiEtLgxgjXTPbUQsvETaAil5J7zg1TOA4Wl8ClalLSvCh+AKWkxGdkL4/uM/zfbxPSNNw==",
+ "requires": {
+ "uuid": "3.4.0"
+ }
+ },
+ "@optimizely/optimizely-sdk": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/@optimizely/optimizely-sdk/-/optimizely-sdk-4.6.0.tgz",
+ "integrity": "sha512-xhTWe3Jg4YFCy8VG0c0depur0wumhgD3AnlSnHrTnaDNjuCvmBVrYV3cV3RMt8OJoZZxwQvJ2SJKjj73LMplBQ==",
+ "requires": {
+ "@optimizely/js-sdk-datafile-manager": "0.8.1",
+ "@optimizely/js-sdk-event-processor": "0.8.2",
+ "@optimizely/js-sdk-logging": "0.1.0",
+ "@optimizely/js-sdk-utils": "0.4.0",
+ "json-schema": "0.2.3",
+ "murmurhash": "0.0.2"
+ },
+ "dependencies": {
+ "@optimizely/js-sdk-utils": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@optimizely/js-sdk-utils/-/js-sdk-utils-0.4.0.tgz",
+ "integrity": "sha512-QG2oytnITW+VKTJK+l0RxjaS5VrA6W+AZMzpeg4LCB4Rn4BEKtF+EcW/5S1fBDLAviGq/0TLpkjM3DlFkJ9/Gw==",
+ "requires": {
+ "uuid": "3.4.0"
+ }
+ }
+ }
+ },
+ "@optimizely/react-sdk": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@optimizely/react-sdk/-/react-sdk-2.6.0.tgz",
+ "integrity": "sha512-+f7RCbZ0VmtvbAAA4APq5fglj9gSiP+ibaAEKsBa5rTaWaN5I/SH2+4ywpfjpRNvYXobjAN7YxEXSvXPPTGeig==",
+ "requires": {
+ "@optimizely/js-sdk-logging": "0.1.0",
+ "@optimizely/optimizely-sdk": "4.6.0",
+ "hoist-non-react-statics": "3.3.2",
+ "prop-types": "15.7.2",
+ "utility-types": "3.10.0"
+ },
+ "dependencies": {
+ "hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "requires": {
+ "react-is": "16.13.1"
+ }
+ }
+ }
+ },
+ "@sideway/address": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.2.tgz",
+ "integrity": "sha512-idTz8ibqWFrPU8kMirL0CoPH/A29XOzzAzpyN3zQ4kAWnzmNfFmRaoMNN6VI8ske5M73HZyhIaW4OuSFIdM4oA==",
+ "requires": {
+ "@hapi/hoek": "9.2.0"
+ },
+ "dependencies": {
+ "@hapi/hoek": {
+ "version": "9.2.0",
+ "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.0.tgz",
+ "integrity": "sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug=="
+ }
+ }
+ },
+ "@sideway/formula": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz",
+ "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg=="
+ },
+ "@sideway/pinpoint": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
+ "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="
+ },
"@tanem/svg-injector": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@tanem/svg-injector/-/svg-injector-1.2.1.tgz",
@@ -13163,19 +13301,29 @@
}
},
"joi": {
- "version": "13.7.0",
- "resolved": "https://registry.npmjs.org/joi/-/joi-13.7.0.tgz",
- "integrity": "sha512-xuY5VkHfeOYK3Hdi91ulocfuFopwgbSORmIwzcwHKESQhC7w1kD5jaVSPnqDxS2I8t3RZ9omCKAxNwXN5zG1/Q==",
+ "version": "17.4.0",
+ "resolved": "https://registry.npmjs.org/joi/-/joi-17.4.0.tgz",
+ "integrity": "sha512-F4WiW2xaV6wc1jxete70Rw4V/VuMd6IN+a5ilZsxG4uYtUXWu2kq9W5P2dz30e7Gmw8RCbY/u/uk+dMPma9tAg==",
"requires": {
- "hoek": "5.0.4",
- "isemail": "3.2.0",
- "topo": "3.0.3"
+ "@hapi/hoek": "9.2.0",
+ "@hapi/topo": "5.0.0",
+ "@sideway/address": "4.1.2",
+ "@sideway/formula": "3.0.0",
+ "@sideway/pinpoint": "2.0.0"
},
"dependencies": {
- "hoek": {
- "version": "5.0.4",
- "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz",
- "integrity": "sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w=="
+ "@hapi/hoek": {
+ "version": "9.2.0",
+ "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.0.tgz",
+ "integrity": "sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug=="
+ },
+ "@hapi/topo": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz",
+ "integrity": "sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==",
+ "requires": {
+ "@hapi/hoek": "9.2.0"
+ }
}
}
},
@@ -14256,8 +14404,8 @@
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz",
"integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=",
"requires": {
- "pseudomap": "^1.0.1",
- "yallist": "^2.0.0"
+ "pseudomap": "1.0.2",
+ "yallist": "2.1.2"
}
}
}
@@ -14965,6 +15113,11 @@
"xtend": "4.0.2"
}
},
+ "murmurhash": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/murmurhash/-/murmurhash-0.0.2.tgz",
+ "integrity": "sha1-bwe9ihEF5wnCb8iUIMtZMMJFhf4="
+ },
"mute-stream": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
@@ -15056,7 +15209,7 @@
"dev": true
},
"navigation-component": {
- "version": "github:topcoder-platform/navigation-component#ad2d6101c4fe10cc98b6ac1389c28b8b80941de7",
+ "version": "github:topcoder-platform/navigation-component#a8855ad57a4e6dcb569a244bc5ad4aff083c7a3c",
"requires": {
"classnames": "2.2.6",
"lodash": "4.17.15",
@@ -15067,9 +15220,9 @@
},
"dependencies": {
"config": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/config/-/config-3.3.3.tgz",
- "integrity": "sha512-T3RmZQEAji5KYqUQpziWtyGJFli6Khz7h0rpxDwYNjSkr5ynyTWwO7WpfjHzTXclNCDfSWQRcwMb+NwxJesCKw==",
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/config/-/config-3.3.6.tgz",
+ "integrity": "sha512-Hj5916C5HFawjYJat1epbyY2PlAgLpBtDUlr0MxGLgo3p5+7kylyvnRY18PqJHgnNWXcdd0eWDemT7eYWuFgwg==",
"requires": {
"json5": "2.2.0"
}
@@ -15104,12 +15257,11 @@
}
},
"redux": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz",
- "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.0.tgz",
+ "integrity": "sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g==",
"requires": {
- "loose-envify": "1.4.0",
- "symbol-observable": "1.2.0"
+ "@babel/runtime": "7.9.6"
}
},
"serialize-javascript": {
@@ -15128,7 +15280,7 @@
"command-line-args": "5.1.1",
"command-line-usage": "5.0.5",
"compression": "1.7.4",
- "config": "3.3.3",
+ "config": "3.3.6",
"cookie-parser": "1.4.5",
"cross-env": "5.2.1",
"express": "4.17.1",
@@ -15145,7 +15297,7 @@
"react-helmet": "5.2.1",
"react-redux": "6.0.1",
"react-router-dom": "4.3.1",
- "redux": "4.0.5",
+ "redux": "4.1.0",
"redux-actions": "2.6.5",
"redux-devtools": "3.5.0",
"redux-devtools-dock-monitor": "1.1.3",
@@ -23614,13 +23766,13 @@
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"topcoder-react-lib": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/topcoder-react-lib/-/topcoder-react-lib-1.1.6.tgz",
- "integrity": "sha512-i9Btnbd9k6dq569avQA4vz0fIxz/nIUxJ9hMldN+n71JY3Qv9rNL6nVCk3ou/j7mMLwTY3zNajJsUAogjD4uIA==",
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/topcoder-react-lib/-/topcoder-react-lib-1.1.8.tgz",
+ "integrity": "sha512-+Op873W6dVze1IpgevXgVN/ikw0IQm00D5GJTI19cD8drx8dZgMgZLk/LP6ydpcL2OQVxCDEYiwOf+VdIigfrg==",
"requires": {
"@topcoder-platform/tc-auth-lib": "git+https://github.com/topcoder-platform/tc-auth-lib.git#68fdc22464810c51b703a33e529cdbd6d09437de",
"auth0-js": "6.8.4",
- "config": "3.3.3",
+ "config": "3.3.6",
"isomorphic-fetch": "2.2.1",
"le_node": "1.8.0",
"lodash": "4.17.15",
@@ -23641,13 +23793,13 @@
"@topcoder-platform/tc-auth-lib": {
"version": "git+https://github.com/topcoder-platform/tc-auth-lib.git#68fdc22464810c51b703a33e529cdbd6d09437de",
"requires": {
- "lodash": "4.17.20"
+ "lodash": "4.17.21"
},
"dependencies": {
"lodash": {
- "version": "4.17.20",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
- "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
}
}
},
@@ -23656,19 +23808,18 @@
"resolved": "https://registry.npmjs.org/auth0-js/-/auth0-js-6.8.4.tgz",
"integrity": "sha1-Qw3Uystk2NFdabHmIRhPmipkCmE=",
"requires": {
- "Base64": "~0.1.3",
+ "Base64": "0.1.4",
"json-fallback": "0.0.1",
- "jsonp": "~0.0.4",
- "qs": "git+https://github.com/jfromaniello/node-querystring.git#fix_ie7_bug_with_arrays",
- "reqwest": "^1.1.4",
- "trim": "~0.0.1",
- "winchan": "^0.1.1",
- "xtend": "~2.1.1"
+ "jsonp": "0.0.4",
+ "qs": "git+https://github.com/jfromaniello/node-querystring.git#5d96513991635e3e22d7aa54a8584d6ce97cace8",
+ "reqwest": "1.1.6",
+ "trim": "0.0.1",
+ "winchan": "0.1.4",
+ "xtend": "2.1.2"
},
"dependencies": {
"qs": {
- "version": "git+https://github.com/jfromaniello/node-querystring.git#5d96513991635e3e22d7aa54a8584d6ce97cace8",
- "from": "git+https://github.com/jfromaniello/node-querystring.git#fix_ie7_bug_with_arrays"
+ "version": "git+https://github.com/jfromaniello/node-querystring.git#5d96513991635e3e22d7aa54a8584d6ce97cace8"
}
}
},
@@ -23681,9 +23832,9 @@
}
},
"config": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/config/-/config-3.3.3.tgz",
- "integrity": "sha512-T3RmZQEAji5KYqUQpziWtyGJFli6Khz7h0rpxDwYNjSkr5ynyTWwO7WpfjHzTXclNCDfSWQRcwMb+NwxJesCKw==",
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/config/-/config-3.3.6.tgz",
+ "integrity": "sha512-Hj5916C5HFawjYJat1epbyY2PlAgLpBtDUlr0MxGLgo3p5+7kylyvnRY18PqJHgnNWXcdd0eWDemT7eYWuFgwg==",
"requires": {
"json5": "2.2.0"
}
@@ -23701,8 +23852,8 @@
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-0.0.7.tgz",
"integrity": "sha1-NLkLqyqRGqNHVx2pDyK9NuzYqRk=",
"requires": {
- "debug": "^2.2.0",
- "stream-consume": "^0.1.0"
+ "debug": "2.6.9",
+ "stream-consume": "0.1.1"
},
"dependencies": {
"debug": {
@@ -23725,7 +23876,20 @@
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"requires": {
- "react-is": "^16.7.0"
+ "react-is": "16.13.1"
+ }
+ },
+ "idtoken-verifier": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/idtoken-verifier/-/idtoken-verifier-2.2.0.tgz",
+ "integrity": "sha512-D+KZkWx+4fDsLXrWIfwWOUAdEQczkqzs0REN0qpJ+9axG4kOeFFXXAFEmSfDErGh8dvM4vY8dQRROw9g8ZnNbw==",
+ "requires": {
+ "base64-js": "1.3.1",
+ "crypto-js": "3.3.0",
+ "es6-promise": "4.2.8",
+ "jsbn": "1.1.0",
+ "unfetch": "4.2.0",
+ "url-join": "4.0.1"
}
},
"json5": {
@@ -23745,9 +23909,9 @@
}
},
"mime": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.0.tgz",
- "integrity": "sha512-ft3WayFSFUVBuJj7BMLKAQcSlItKtfjsKDDsii3rqFDAZ7t11zRe8ASw/GlmivGwVUYtwkQrxiGGpL6gFvB0ag=="
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz",
+ "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg=="
},
"ms": {
"version": "2.1.2",
@@ -23764,12 +23928,12 @@
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-6.0.1.tgz",
"integrity": "sha512-T52I52Kxhbqy/6TEfBv85rQSDz6+Y28V/pf52vDWs1YRXG19mcFOGfHnY2HsNFHyhP+ST34Aih98fvt6tqwVcQ==",
"requires": {
- "@babel/runtime": "^7.3.1",
- "hoist-non-react-statics": "^3.3.0",
- "invariant": "^2.2.4",
- "loose-envify": "^1.4.0",
- "prop-types": "^15.7.2",
- "react-is": "^16.8.2"
+ "@babel/runtime": "7.9.6",
+ "hoist-non-react-statics": "3.3.2",
+ "invariant": "2.2.4",
+ "loose-envify": "1.4.0",
+ "prop-types": "15.7.2",
+ "react-is": "16.13.1"
}
},
"readable-stream": {
@@ -23783,9 +23947,9 @@
}
},
"semver": {
- "version": "7.3.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
- "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
+ "version": "7.3.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+ "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"requires": {
"lru-cache": "6.0.0"
}
@@ -23800,23 +23964,23 @@
"resolved": "https://registry.npmjs.org/superagent/-/superagent-5.3.1.tgz",
"integrity": "sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==",
"requires": {
- "component-emitter": "^1.3.0",
- "cookiejar": "^2.1.2",
- "debug": "^4.1.1",
- "fast-safe-stringify": "^2.0.7",
- "form-data": "^3.0.0",
- "formidable": "^1.2.2",
- "methods": "^1.1.2",
- "mime": "^2.4.6",
- "qs": "^6.9.4",
- "readable-stream": "^3.6.0",
- "semver": "^7.3.2"
+ "component-emitter": "1.3.0",
+ "cookiejar": "2.1.2",
+ "debug": "4.3.1",
+ "fast-safe-stringify": "2.0.7",
+ "form-data": "3.0.0",
+ "formidable": "1.2.2",
+ "methods": "1.1.2",
+ "mime": "2.5.2",
+ "qs": "6.9.4",
+ "readable-stream": "3.6.0",
+ "semver": "7.3.5"
}
},
"tc-core-library-js": {
"version": "github:appirio-tech/tc-core-library-js#d16413db30b1eed21c0cf426e185bedb2329ddab",
"requires": {
- "auth0-js": "9.14.3",
+ "auth0-js": "9.16.2",
"axios": "0.12.0",
"bunyan": "1.8.15",
"jsonwebtoken": "8.5.1",
@@ -23828,17 +23992,17 @@
},
"dependencies": {
"auth0-js": {
- "version": "9.14.3",
- "resolved": "https://registry.npmjs.org/auth0-js/-/auth0-js-9.14.3.tgz",
- "integrity": "sha512-UO/fGv9641PUpYjz2nkPaUHzzrhNaJKupJOqt8blj1pD6wBgpZtxUSXBox6Y8md3eTBzpxeWxV+6RKzzERvr1g==",
+ "version": "9.16.2",
+ "resolved": "https://registry.npmjs.org/auth0-js/-/auth0-js-9.16.2.tgz",
+ "integrity": "sha512-cF1nRjmMDezmhJ+ZwwYp23F0gPqU0zNmF/VvTpcwvCrEMl9lAvkCd4iburN1I7G8SYaaIYEfcGedCphpDZw6OQ==",
"requires": {
- "base64-js": "^1.3.0",
- "idtoken-verifier": "^2.0.3",
- "js-cookie": "^2.2.0",
- "qs": "^6.7.0",
- "superagent": "^5.3.1",
- "url-join": "^4.0.1",
- "winchan": "^0.2.2"
+ "base64-js": "1.3.1",
+ "idtoken-verifier": "2.2.0",
+ "js-cookie": "2.2.1",
+ "qs": "6.9.4",
+ "superagent": "5.3.1",
+ "url-join": "4.0.1",
+ "winchan": "0.2.2"
}
},
"winchan": {
@@ -23853,38 +24017,38 @@
"resolved": "https://registry.npmjs.org/topcoder-react-utils/-/topcoder-react-utils-0.7.5.tgz",
"integrity": "sha512-/jolO/UUCC/FL/MniBMFi9d7Wc1KbzwvgT5STGs4T+7u7R26bQugGPpGVISEPuglsmW0Xybh6iRi+pT/muOkbg==",
"requires": {
- "babel-runtime": "^6.26.0",
- "body-parser": "^1.18.3",
- "command-line-args": "^5.0.2",
- "command-line-usage": "^5.0.5",
- "compression": "^1.7.2",
- "config": "^1.30.0",
- "cookie-parser": "^1.4.3",
- "express": "^4.16.3",
- "helmet": "^3.12.1",
- "lodash": "^4.17.10",
- "moment": "^2.22.2",
- "morgan": "^1.9.0",
- "node-forge": "^0.7.5",
- "prop-types": "^15.6.2",
- "raf": "^3.4.0",
- "react": "^16.4.1",
- "react-css-super-themr": "^2.2.0",
- "react-dom": "^16.4.1",
- "react-helmet": "^5.2.0",
- "react-redux": "^5.0.7",
- "react-router-dom": "^4.3.1",
- "redux": "^3.7.2",
- "redux-actions": "^2.4.0",
- "redux-devtools": "^3.4.1",
- "redux-devtools-dock-monitor": "^1.1.3",
- "redux-devtools-log-monitor": "^1.4.0",
- "redux-promise": "^0.6.0",
- "request-ip": "^2.0.2",
- "serialize-javascript": "^1.5.0",
- "serve-favicon": "^2.5.0",
- "shortid": "^2.2.8",
- "url-parse": "^1.4.1"
+ "babel-runtime": "6.26.0",
+ "body-parser": "1.19.0",
+ "command-line-args": "5.1.1",
+ "command-line-usage": "5.0.5",
+ "compression": "1.7.4",
+ "config": "1.31.0",
+ "cookie-parser": "1.4.5",
+ "express": "4.17.1",
+ "helmet": "3.22.0",
+ "lodash": "4.17.15",
+ "moment": "2.25.3",
+ "morgan": "1.10.0",
+ "node-forge": "0.7.6",
+ "prop-types": "15.7.2",
+ "raf": "3.4.1",
+ "react": "16.13.1",
+ "react-css-super-themr": "2.3.0",
+ "react-dom": "16.13.1",
+ "react-helmet": "5.2.1",
+ "react-redux": "5.1.2",
+ "react-router-dom": "4.3.1",
+ "redux": "3.7.2",
+ "redux-actions": "2.6.5",
+ "redux-devtools": "3.5.0",
+ "redux-devtools-dock-monitor": "1.1.3",
+ "redux-devtools-log-monitor": "1.4.0",
+ "redux-promise": "0.6.0",
+ "request-ip": "2.1.3",
+ "serialize-javascript": "1.9.1",
+ "serve-favicon": "2.5.0",
+ "shortid": "2.2.15",
+ "url-parse": "1.4.7"
},
"dependencies": {
"config": {
@@ -23892,7 +24056,7 @@
"resolved": "https://registry.npmjs.org/config/-/config-1.31.0.tgz",
"integrity": "sha512-Ep/l9Rd1J9IPueztJfpbOqVzuKHQh4ZODMNt9xqTYdBBNRXbV4oTu34kCkkfdRVcDq0ohtpaeXGgb+c0LQxFRA==",
"requires": {
- "json5": "^1.0.1"
+ "json5": "1.0.1"
}
},
"json5": {
@@ -23900,7 +24064,7 @@
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"requires": {
- "minimist": "^1.2.0"
+ "minimist": "1.2.5"
}
},
"react-redux": {
@@ -23908,13 +24072,13 @@
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.1.2.tgz",
"integrity": "sha512-Ns1G0XXc8hDyH/OcBHOxNgQx9ayH3SPxBnFCOidGKSle8pKihysQw2rG/PmciUQRoclhVBO8HMhiRmGXnDja9Q==",
"requires": {
- "@babel/runtime": "^7.1.2",
- "hoist-non-react-statics": "^3.3.0",
- "invariant": "^2.2.4",
- "loose-envify": "^1.1.0",
- "prop-types": "^15.6.1",
- "react-is": "^16.6.0",
- "react-lifecycles-compat": "^3.0.0"
+ "@babel/runtime": "7.9.6",
+ "hoist-non-react-statics": "3.3.2",
+ "invariant": "2.2.4",
+ "loose-envify": "1.4.0",
+ "prop-types": "15.7.2",
+ "react-is": "16.13.1",
+ "react-lifecycles-compat": "3.0.4"
}
}
}
@@ -23929,7 +24093,7 @@
"resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz",
"integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=",
"requires": {
- "object-keys": "~0.4.0"
+ "object-keys": "0.4.0"
}
},
"yallist": {
@@ -24480,6 +24644,11 @@
"object.getownpropertydescriptors": "2.1.0"
}
},
+ "utility-types": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz",
+ "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg=="
+ },
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@@ -26205,6 +26374,25 @@
"hoek": "4.2.1",
"joi": "13.7.0",
"node-expat": "2.3.18"
+ },
+ "dependencies": {
+ "joi": {
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/joi/-/joi-13.7.0.tgz",
+ "integrity": "sha512-xuY5VkHfeOYK3Hdi91ulocfuFopwgbSORmIwzcwHKESQhC7w1kD5jaVSPnqDxS2I8t3RZ9omCKAxNwXN5zG1/Q==",
+ "requires": {
+ "hoek": "5.0.4",
+ "isemail": "3.2.0",
+ "topo": "3.0.3"
+ },
+ "dependencies": {
+ "hoek": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz",
+ "integrity": "sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w=="
+ }
+ }
+ }
}
},
"xmlbuilder": {
diff --git a/package.json b/package.json
index 8f0d6fee5a..7c188001fd 100644
--- a/package.json
+++ b/package.json
@@ -74,6 +74,7 @@
"i18n-iso-countries": "^3.7.1",
"immutable": "^3.8.2",
"isomorphic-fetch": "^2.2.1",
+ "joi": "^17.4.0",
"js-beautify": "^1.10.3",
"le_node": "^1.7.0",
"localStorage": "^1.0.4",
diff --git a/src/server/routes/recruitCRM.js b/src/server/routes/recruitCRM.js
index 34c1831539..5e9f70a658 100644
--- a/src/server/routes/recruitCRM.js
+++ b/src/server/routes/recruitCRM.js
@@ -3,8 +3,13 @@
*/
import express from 'express';
+import { middleware } from 'tc-core-library-js';
+import config from 'config';
+import _ from 'lodash';
import RecruitCRMService from '../services/recruitCRM';
+const authenticator = middleware.jwtAuthenticator;
+const authenticatorOptions = _.pick(config.SECRET.JWT_AUTH, ['AUTH_SECRET', 'VALID_ISSUERS']);
const cors = require('cors');
const multer = require('multer');
@@ -27,5 +32,7 @@ routes.get('/jobs/search', (req, res, next) => new RecruitCRMService().getJobs(r
routes.get('/jobs/:id', (req, res, next) => new RecruitCRMService().getJob(req, res, next));
routes.post('/jobs/:id/apply', upload.single('resume'), (req, res, next) => new RecruitCRMService().applyForJob(req, res, next));
routes.get('/candidates/search', (req, res, next) => new RecruitCRMService().searchCandidates(req, res, next));
-
+// new router added
+routes.get('/profile', (req, res, next) => authenticator(authenticatorOptions)(req, res, next), (req, res, next) => new RecruitCRMService().getProfile(req, res, next));
+routes.post('/profile', (req, res, next) => authenticator(authenticatorOptions)(req, res, next), upload.single('resume'), (req, res, next) => new RecruitCRMService().updateProfile(req, res, next));
export default routes;
diff --git a/src/server/services/recruitCRM.js b/src/server/services/recruitCRM.js
index c352dd0dc5..8eb1b0fc51 100644
--- a/src/server/services/recruitCRM.js
+++ b/src/server/services/recruitCRM.js
@@ -6,6 +6,7 @@ import config from 'config';
import qs from 'qs';
import _ from 'lodash';
import { logger } from 'topcoder-react-lib';
+import Joi from 'joi';
import GrowsurfService from './growsurf';
import { sendEmailDirect } from './sendGrid';
@@ -62,6 +63,13 @@ function notifyKirilAndNick(error) {
});
}
+const updateProfileSchema = Joi.object().keys({
+ phone: Joi.string().required(),
+ availability: Joi.boolean().required(),
+ city: Joi.string().required(),
+ countryName: Joi.string().required(),
+}).required();
+
/**
* Auxiliary class that handles communication with recruitCRM
*/
@@ -449,4 +457,164 @@ export default class RecruitCRMService {
return next(err);
}
}
+
+ /**
+ * Get user profile endpoint.
+ * @return {Promise}
+ * @param {Object} the request.
+ */
+ async getProfile(req, res, next) {
+ try {
+ // get candidate by email
+ const candidate = await this.getCandidateByEmail(req.authUser.email);
+ // return error if getCandidateByEmail operation failed
+ if (candidate.error) {
+ const error = candidate;
+ logger.error(error);
+ const responseNoProfileMapping = {
+ hasProfile: false,
+ };
+ return res.send(responseNoProfileMapping);
+ }
+ // apply desired response format
+ const responseMapping = {
+ hasProfile: true,
+ phone: candidate.contact_number,
+ resume: candidate.resume,
+ availability: _.isNil(candidate.available_from) ? true
+ : new Date(candidate.available_from) <= new Date(),
+ };
+ return res.send(responseMapping);
+ } catch (err) {
+ return next(err);
+ }
+ }
+
+ /**
+ * Update user profile endpoint.
+ * @return {Promise}
+ * @param {Object} the request.
+ */
+ async updateProfile(req, res, next) {
+ const { body, file } = req;
+ // validate provided data
+ const validationResult = updateProfileSchema.validate(body);
+ if (validationResult.error) {
+ return res.status(400).send({ message: validationResult.error.message });
+ }
+ const fileData = new FormData();
+ if (file) {
+ fileData.append('resume', file.buffer, file.originalname);
+ }
+ try {
+ // get candidate by email
+ const candidate = await this.getCandidateByEmail(req.authUser.email);
+ // return error if getCandidateByEmail operation failed
+ if (candidate.error) {
+ const error = candidate;
+ logger.error(error);
+ return res.status(error.status).send(error);
+ }
+ const candidateSlug = candidate.slug;
+ const form = {
+ city: body.city,
+ locality: body.countryName,
+ contact_number: body.phone,
+ available_from: body.availability === 'true' ? new Date().toISOString() : new Date('2100-01-01').toISOString(),
+ };
+ // update candidate profile
+ const response = await fetch(`${this.private.baseUrl}/v1/candidates/${candidateSlug}`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: this.private.authorization,
+ },
+ body: JSON.stringify(form),
+ });
+ if (response.status >= 300) {
+ const error = {
+ error: true,
+ status: response.status,
+ url: `${this.private.baseUrl}/v1/candidates${candidateSlug}`,
+ form,
+ errorObj: await response.json(),
+ };
+ logger.error(error);
+ return res.status(error.status).send(error);
+ }
+ // Attach resume to candidate if uploaded
+ if (file) {
+ const formHeaders = fileData.getHeaders();
+ const fileResponse = await fetch(`${this.private.baseUrl}/v1/candidates/${candidateSlug}`, {
+ method: 'POST',
+ headers: {
+ Authorization: this.private.authorization,
+ ...formHeaders,
+ },
+ body: fileData,
+ });
+ if (fileResponse.status >= 300) {
+ const error = {
+ error: true,
+ status: fileResponse.status,
+ url: `${this.private.baseUrl}/v1/candidates/${candidateSlug}`,
+ file,
+ formHeaders,
+ errorObj: await fileResponse.json(),
+ };
+ logger.error(error);
+ return res.status(error.status).send(error);
+ }
+ }
+ return res.status(204).end();
+ } catch (err) {
+ return next(err);
+ }
+ }
+
+ /**
+ * Get candidate by email
+ * @return {object} result of the search operation
+ * @param {string} email email address of the user.
+ */
+ async getCandidateByEmail(email) {
+ const query = {
+ email,
+ };
+ const url = `${this.private.baseUrl}/v1/candidates/search?${qs.stringify(query)}`;
+ const response = await fetch(url, {
+ method: 'GET',
+ headers: {
+ Authorization: this.private.authorization,
+ },
+ });
+ if (response.status === 429) {
+ await new Promise(resolve => setTimeout(resolve, 30000)); // wait 30sec
+ return this.getCandidateByEmail(email);
+ }
+ if (response.status >= 300) {
+ const error = {
+ error: true,
+ status: response.status,
+ url,
+ errObj: await response.json(),
+ };
+ return error;
+ }
+ const data = await response.json();
+ // return error object if candidate with provided email not found
+ if ((_.isArray(data) && data.length === 0) || data.data.length === 0) {
+ const error = {
+ error: true,
+ status: 404,
+ url,
+ errObj: {
+ message: `No candidate was found with email: ${email}`,
+ },
+ };
+ return error;
+ }
+ // return first candidate
+ return data.data[0];
+ }
}
diff --git a/src/shared/actions/recruitCRM.js b/src/shared/actions/recruitCRM.js
index cdcb6d2260..2d6bf63d24 100644
--- a/src/shared/actions/recruitCRM.js
+++ b/src/shared/actions/recruitCRM.js
@@ -22,6 +22,18 @@ async function getJobsDone(query) {
};
}
+function getJobApplicationsInit() {
+ return {};
+}
+
+async function getJobApplicationsDone(tokenV3) {
+ const ss = new Service();
+ const res = await ss.getJobApplications(tokenV3);
+ return {
+ data: res,
+ };
+}
+
/**
* Job fetch init
*/
@@ -163,5 +175,7 @@ export default redux.createActions({
APPLY_FOR_JOB_DONE: applyForJobDone,
SEARCH_CANDIDATES_INIT: searchCandidatesInit,
SEARCH_CANDIDATES_DONE: searchCandidatesDone,
+ GET_JOB_APPLICATIONS_INIT: getJobApplicationsInit,
+ GET_JOB_APPLICATIONS_DONE: getJobApplicationsDone,
},
});
diff --git a/src/shared/components/Gigs/GigApply/index.jsx b/src/shared/components/Gigs/GigApply/index.jsx
index c7ef512a77..3c801ae2d2 100644
--- a/src/shared/components/Gigs/GigApply/index.jsx
+++ b/src/shared/components/Gigs/GigApply/index.jsx
@@ -108,7 +108,10 @@ export default function GigApply(props) {
VIEW OTHER GIGS
) : (
- GO TO GIGS LIST
+