From 2be7ca5bac7c8ed724f0b0a6a3dff2df53fae7e8 Mon Sep 17 00:00:00 2001 From: Rakib Ansary Date: Mon, 26 Jul 2021 17:38:58 +0600 Subject: [PATCH] feature: add support for fetching groups by uuids --- README.md | 1 + app-bootstrap.js | 2 +- app-routes.js | 4 +- config/default.js | 37 ++-- docs/member-api.postman_collection.json | 252 +++--------------------- src/common/eshelper.js | 16 +- src/common/helper.js | 198 ++++++++++++------- src/init-db.js | 12 +- src/init-es.js | 27 +++ src/models/Member.js | 8 +- src/models/MemberStats.js | 4 +- src/models/MemberStatsPrivate.js | 4 +- src/scripts/seed-data.js | 223 +++++++++++++++++++-- src/services/MemberService.js | 18 +- src/services/MemberTraitService.js | 12 +- src/services/SearchService.js | 32 +-- src/services/StatisticsService.js | 46 ++--- 17 files changed, 505 insertions(+), 391 deletions(-) diff --git a/README.md b/README.md index 86f4379..18e1867 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ The following parameters can be set in config files or in env variables: - AUTH0_CLIENT_SECRET: AUTH0 client secret, used to get M2M token - BUSAPI_URL: Bus API URL - KAFKA_ERROR_TOPIC: Kafka error topic used by bus API wrapper +- GROUPS_API_URL: Groups API URL - AMAZON.AWS_ACCESS_KEY_ID: The Amazon certificate key to use when connecting. Use local dynamodb you can set fake value - AMAZON.AWS_SECRET_ACCESS_KEY: The Amazon certificate access key to use when connecting. Use local dynamodb you can set fake value - AMAZON.AWS_REGION: The Amazon certificate region to use when connecting. Use local dynamodb you can set fake value diff --git a/app-bootstrap.js b/app-bootstrap.js index 664c76c..6eb4458 100644 --- a/app-bootstrap.js +++ b/app-bootstrap.js @@ -7,4 +7,4 @@ const Joi = require('joi') Joi.page = () => Joi.number().integer().min(1).default(1) Joi.perPage = () => Joi.number().integer().min(1).max(100).default(50) Joi.size = () => Joi.number().integer().min(1).max(1000).default(500) -Joi.sort = () => Joi.string().default("asc") \ No newline at end of file +Joi.sort = () => Joi.string().default('asc') diff --git a/app-routes.js b/app-routes.js index 9167695..b8af818 100644 --- a/app-routes.js +++ b/app-routes.js @@ -41,7 +41,7 @@ module.exports = (app) => { // add Authenticator/Authorization check if route has auth actions.push((req, res, next) => { // When authorization token is not provided and allow no token is enabled then bypass - if(!_.get(req, 'headers.authorization') && def.allowNoToken) { + if (!_.get(req, 'headers.authorization') && def.allowNoToken) { next() } else { authenticator(_.pick(config, ['AUTH_SECRET', 'VALID_ISSUERS']))(req, res, next) @@ -50,7 +50,7 @@ module.exports = (app) => { actions.push((req, res, next) => { // When authorization token is not provided and allow no token is enabled then bypass - if(!_.get(req, 'headers.authorization') && def.allowNoToken) { + if (!_.get(req, 'headers.authorization') && def.allowNoToken) { next() } else { if (req.authUser.isMachine) { diff --git a/config/default.js b/config/default.js index b945355..ca301e5 100644 --- a/config/default.js +++ b/config/default.js @@ -25,9 +25,9 @@ module.exports = { TAGS: { TAGS_BASE_URL: process.env.TAGS_BASE_URL || 'https://api.topcoder-dev.com', TAGS_API_VERSION: process.env.TAGS_API_VERSION || '/v3', - TAGS_FILTER: process.env.TAGS_FILTER || '/tags/?filter=domain%3DSKILLS%26status%3DAPPROVED&limit=1000', + TAGS_FILTER: process.env.TAGS_FILTER || '/tags/?filter=domain%3DSKILLS%26status%3DAPPROVED&limit=1000' }, - + GROUPS_API_URL: process.env.GROUPS_API_URL || 'https://api.topcoder-dev.com/v5/groups', // aws config params AMAZON: { AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID, @@ -48,7 +48,7 @@ module.exports = { MEMBER_PROFILE_ES_INDEX: process.env.MEMBER_PROFILE_ES_INDEX || 'members-2020-01', // member type, ES 6.x accepts only 1 Type per index and it's mandatory to define it MEMBER_PROFILE_ES_TYPE: process.env.MEMBER_PROFILE_ES_TYPE || 'profiles', - MEMBER_TRAIT_ES_INDEX: process.env.MEMBER_TRAIT_ES_INDEX || 'members-2020-01', + MEMBER_TRAIT_ES_INDEX: process.env.MEMBER_TRAIT_ES_INDEX || 'membertraits-2020-01', MEMBER_TRAIT_ES_TYPE: process.env.MEMBER_TRAIT_ES_TYPE || 'profiletraits', MEMBER_STATS_ES_INDEX: process.env.MEMBER_STATS_ES_INDEX || 'memberstats-2020-01', MEMBER_STATS_ES_TYPE: process.env.MEMBER_STATS_ES_TYPE || 'stats', @@ -62,12 +62,12 @@ module.exports = { // file upload max size in bytes FILE_UPLOAD_SIZE_LIMIT: process.env.FILE_UPLOAD_SIZE_LIMIT ? Number(process.env.FILE_UPLOAD_SIZE_LIMIT) : 10 * 1024 * 1024, // 10M - + // photo URL template, its will be replaced with S3 object key, // the URL is specific to AWS region and bucket, you may go to AWS console S3 service to // see bucket object URL to get the URL structure PHOTO_URL_TEMPLATE: process.env.PHOTO_URL_TEMPLATE || 'https://topcoder-dev-media.s3.us-east-1.amazonaws.com/member/profile/', - + // verify token expiration in minutes VERIFY_TOKEN_EXPIRATION: process.env.VERIFY_TOKEN_EXPIRATION || 60, @@ -84,33 +84,32 @@ module.exports = { READ: process.env.SCOPE_MEMBERS_READ || 'read:user_profiles', UPDATE: process.env.SCOPE_MEMBERS_UPDATE || 'update:user_profiles', DELETE: process.env.SCOPE_MEMBERS_DELETE || 'delete:user_profiles', - ALL: process.env.SCOPE_MEMBERS_ALL || 'all:user_profiles', + ALL: process.env.SCOPE_MEMBERS_ALL || 'all:user_profiles' } }, - + // Member identifiable info fields, only admin, M2M, or member himself can get these fields - MEMBER_SECURE_FIELDS: process.env.MEMBER_SECURE_FIELDS - ? process.env.MEMBER_SECURE_FIELDS.split(',') + MEMBER_SECURE_FIELDS: process.env.MEMBER_SECURE_FIELDS + ? process.env.MEMBER_SECURE_FIELDS.split(',') : ['firstName', 'lastName', 'email', 'addresses', 'createdBy', 'updatedBy'], // Member traits identifiable info fields, only admin, M2M, or member himself can fetch these fields - MEMBER_TRAIT_SECURE_FIELDS: process.env.MEMBER_TRAIT_SECURE_FIELDS - ? process.env.MEMBER_TRAIT_SECURE_FIELDS.split(',') + MEMBER_TRAIT_SECURE_FIELDS: process.env.MEMBER_TRAIT_SECURE_FIELDS + ? process.env.MEMBER_TRAIT_SECURE_FIELDS.split(',') : ['createdBy', 'updatedBy'], // Misc identifiable info fields, only admin, M2M, or member himself can fetch these fields - MISC_SECURE_FIELDS: process.env.MISC_SECURE_FIELDS - ? process.env.MISC_SECURE_FIELDS.split(',') + MISC_SECURE_FIELDS: process.env.MISC_SECURE_FIELDS + ? process.env.MISC_SECURE_FIELDS.split(',') : ['createdBy', 'updatedBy'], - + // Member Search identifiable info fields, only admin, M2M, or member himself can fetch these fields - SEARCH_SECURE_FIELDS: process.env.SEARCH_SECURE_FIELDS - ? process.env.SEARCH_SECURE_FIELDS.split(',') - : ['firstName', 'lastName', 'email', 'addresses', 'createdBy', 'updatedBy'], - + SEARCH_SECURE_FIELDS: process.env.SEARCH_SECURE_FIELDS + ? process.env.SEARCH_SECURE_FIELDS.split(',') + : ['firstName', 'lastName', 'email', 'addresses', 'createdBy', 'updatedBy'], + // Member Statistics identifiable info fields, only admin, M2M, or member himself can fetch these fields STATISTICS_SECURE_FIELDS: process.env.STATISTICS_SECURE_FIELDS ? process.env.STATISTICS_SECURE_FIELDS.split(',') : ['createdBy', 'updatedBy'] - } diff --git a/docs/member-api.postman_collection.json b/docs/member-api.postman_collection.json index 5b41c93..4c34818 100644 --- a/docs/member-api.postman_collection.json +++ b/docs/member-api.postman_collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "a4dc2dfd-296e-45ed-a57f-22a302ee2503", + "_postman_id": "03129edd-111f-4875-924d-2ffd2c0c18eb", "name": "member-api", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, @@ -14,7 +14,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -56,8 +55,7 @@ }, "response": [] } - ], - "protocolProfileBehavior": {} + ] }, { "name": "Basic", @@ -71,7 +69,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -119,7 +116,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -166,7 +162,6 @@ { "listen": "prerequest", "script": { - "id": "87c6c041-981a-489f-9947-34632701ee1a", "type": "text/javascript", "exec": [ "" @@ -176,16 +171,13 @@ { "listen": "test", "script": { - "id": "ad2b566f-7a46-44b9-ae42-af20e539e19d", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true + ] }, { "name": "verify email", @@ -196,7 +188,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -260,7 +251,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -315,7 +305,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -370,7 +359,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -425,7 +413,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 403\", function () {", " pm.response.to.have.status(403);", @@ -480,7 +467,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 401\", function () {", " pm.response.to.have.status(401);", @@ -535,7 +521,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 401\", function () {", " pm.response.to.have.status(401);", @@ -590,7 +575,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -639,7 +623,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -693,7 +676,6 @@ { "listen": "prerequest", "script": { - "id": "87c6c041-981a-489f-9947-34632701ee1a", "type": "text/javascript", "exec": [ "" @@ -703,16 +685,13 @@ { "listen": "test", "script": { - "id": "ad2b566f-7a46-44b9-ae42-af20e539e19d", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true + ] }, { "name": "update member", @@ -723,7 +702,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -787,7 +765,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -851,7 +828,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -921,7 +897,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -991,7 +966,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -1061,7 +1035,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -1131,7 +1104,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 403\", function () {", " pm.response.to.have.status(403);", @@ -1188,7 +1160,6 @@ { "listen": "prerequest", "script": { - "id": "87c6c041-981a-489f-9947-34632701ee1a", "type": "text/javascript", "exec": [ "" @@ -1198,16 +1169,13 @@ { "listen": "test", "script": { - "id": "ad2b566f-7a46-44b9-ae42-af20e539e19d", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true + ] }, { "name": "upload photo", @@ -1218,7 +1186,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1283,7 +1250,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -1342,7 +1308,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 401\", function () {", " pm.response.to.have.status(401);", @@ -1402,7 +1367,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -1467,7 +1431,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 403\", function () {", " pm.response.to.have.status(403);", @@ -1531,7 +1494,6 @@ { "listen": "prerequest", "script": { - "id": "87c6c041-981a-489f-9947-34632701ee1a", "type": "text/javascript", "exec": [ "" @@ -1541,23 +1503,19 @@ { "listen": "test", "script": { - "id": "ad2b566f-7a46-44b9-ae42-af20e539e19d", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true + ] } ], "event": [ { "listen": "prerequest", "script": { - "id": "76111abb-b426-4486-b1ec-2a7b7153a0d1", "type": "text/javascript", "exec": [ "" @@ -1567,15 +1525,13 @@ { "listen": "test", "script": { - "id": "1e60e05b-96d6-4bbe-bc03-68db6eb53784", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {} + ] }, { "name": "Traits", @@ -1589,7 +1545,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1648,7 +1603,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -1707,7 +1661,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -1766,7 +1719,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 403\", function () {", " pm.response.to.have.status(403);", @@ -1825,7 +1777,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -1883,7 +1834,6 @@ { "listen": "prerequest", "script": { - "id": "87c6c041-981a-489f-9947-34632701ee1a", "type": "text/javascript", "exec": [ "" @@ -1893,16 +1843,13 @@ { "listen": "test", "script": { - "id": "ad2b566f-7a46-44b9-ae42-af20e539e19d", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true + ] }, { "name": "create member traits", @@ -1913,7 +1860,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1978,7 +1924,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -2043,7 +1988,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -2108,7 +2052,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -2173,7 +2116,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 401\", function () {", " pm.response.to.have.status(401);", @@ -2232,7 +2174,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 403\", function () {", " pm.response.to.have.status(403);", @@ -2290,7 +2231,6 @@ { "listen": "prerequest", "script": { - "id": "87c6c041-981a-489f-9947-34632701ee1a", "type": "text/javascript", "exec": [ "" @@ -2300,16 +2240,13 @@ { "listen": "test", "script": { - "id": "ad2b566f-7a46-44b9-ae42-af20e539e19d", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true + ] }, { "name": "update member traits", @@ -2320,7 +2257,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -2385,7 +2321,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -2450,7 +2385,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -2515,7 +2449,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -2580,7 +2513,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -2645,7 +2577,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -2710,7 +2641,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 403\", function () {", " pm.response.to.have.status(403);", @@ -2768,7 +2698,6 @@ { "listen": "prerequest", "script": { - "id": "87c6c041-981a-489f-9947-34632701ee1a", "type": "text/javascript", "exec": [ "" @@ -2778,16 +2707,13 @@ { "listen": "test", "script": { - "id": "ad2b566f-7a46-44b9-ae42-af20e539e19d", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true + ] }, { "name": "remove member traits", @@ -2798,7 +2724,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -2857,7 +2782,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -2928,7 +2852,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -2999,7 +2922,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -3070,7 +2992,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -3141,7 +3062,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -3212,7 +3132,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 403\", function () {", " pm.response.to.have.status(403);", @@ -3270,7 +3189,6 @@ { "listen": "prerequest", "script": { - "id": "87c6c041-981a-489f-9947-34632701ee1a", "type": "text/javascript", "exec": [ "" @@ -3280,23 +3198,19 @@ { "listen": "test", "script": { - "id": "ad2b566f-7a46-44b9-ae42-af20e539e19d", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true + ] } ], "event": [ { "listen": "prerequest", "script": { - "id": "76111abb-b426-4486-b1ec-2a7b7153a0d1", "type": "text/javascript", "exec": [ "" @@ -3306,15 +3220,13 @@ { "listen": "test", "script": { - "id": "1e60e05b-96d6-4bbe-bc03-68db6eb53784", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {} + ] }, { "name": "Statistics", @@ -3328,7 +3240,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3372,7 +3283,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3430,7 +3340,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -3488,7 +3397,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -3546,7 +3454,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -3604,7 +3511,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -3661,7 +3567,6 @@ { "listen": "prerequest", "script": { - "id": "87c6c041-981a-489f-9947-34632701ee1a", "type": "text/javascript", "exec": [ "" @@ -3671,16 +3576,13 @@ { "listen": "test", "script": { - "id": "ad2b566f-7a46-44b9-ae42-af20e539e19d", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true + ] }, { "name": "get member history statistics", @@ -3691,7 +3593,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3742,7 +3643,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3797,7 +3697,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3852,7 +3751,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -3903,7 +3801,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -3954,7 +3851,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -4005,7 +3901,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -4055,7 +3950,6 @@ { "listen": "prerequest", "script": { - "id": "87c6c041-981a-489f-9947-34632701ee1a", "type": "text/javascript", "exec": [ "" @@ -4065,16 +3959,13 @@ { "listen": "test", "script": { - "id": "ad2b566f-7a46-44b9-ae42-af20e539e19d", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true + ] }, { "name": "get member statistics", @@ -4085,7 +3976,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4110,13 +4000,13 @@ } ], "url": { - "raw": "{{URL}}/members/upbeat/stats", + "raw": "{{URL}}/members/denis/stats", "host": [ "{{URL}}" ], "path": [ "members", - "upbeat", + "denis", "stats" ] } @@ -4129,7 +4019,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4154,19 +4043,19 @@ } ], "url": { - "raw": "{{URL}}/members/tonyj/stats?groupIds=20000000,20000010", + "raw": "{{URL}}/members/denis/stats?groupIds=10,d6bf6bb6-457c-461c-a4d6-0a6b1a87fde9", "host": [ "{{URL}}" ], "path": [ "members", - "tonyj", + "denis", "stats" ], "query": [ { "key": "groupIds", - "value": "20000000,20000010" + "value": "10,d6bf6bb6-457c-461c-a4d6-0a6b1a87fde9" } ] } @@ -4179,7 +4068,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4204,19 +4092,19 @@ } ], "url": { - "raw": "{{URL}}/members/tonyj/stats?groupIds=10,20000000,20000010&fields=userId,handle,wins,groupId,challenges", + "raw": "{{URL}}/members/denis/stats?groupIds=10,d6bf6bb6-457c-461c-a4d6-0a6b1a87fde9&fields=userId,handle,wins,groupId,challenges", "host": [ "{{URL}}" ], "path": [ "members", - "tonyj", + "denis", "stats" ], "query": [ { "key": "groupIds", - "value": "10,20000000,20000010" + "value": "10,d6bf6bb6-457c-461c-a4d6-0a6b1a87fde9" }, { "key": "fields", @@ -4233,7 +4121,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4283,7 +4170,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -4333,7 +4219,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -4383,7 +4268,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -4433,7 +4317,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -4482,7 +4365,6 @@ { "listen": "prerequest", "script": { - "id": "87c6c041-981a-489f-9947-34632701ee1a", "type": "text/javascript", "exec": [ "" @@ -4492,16 +4374,13 @@ { "listen": "test", "script": { - "id": "ad2b566f-7a46-44b9-ae42-af20e539e19d", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true + ] }, { "name": "get member skills", @@ -4512,7 +4391,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4562,7 +4440,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -4612,7 +4489,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -4662,7 +4538,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -4712,7 +4587,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -4762,7 +4636,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -4812,7 +4685,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -4861,7 +4733,6 @@ { "listen": "prerequest", "script": { - "id": "87c6c041-981a-489f-9947-34632701ee1a", "type": "text/javascript", "exec": [ "" @@ -4871,16 +4742,13 @@ { "listen": "test", "script": { - "id": "ad2b566f-7a46-44b9-ae42-af20e539e19d", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true + ] }, { "name": "partially update member skills", @@ -4891,7 +4759,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4950,7 +4817,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5009,7 +4875,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5068,7 +4933,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -5127,7 +4991,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -5186,7 +5049,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 403\", function () {", " pm.response.to.have.status(403);", @@ -5239,7 +5101,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -5297,7 +5158,6 @@ { "listen": "prerequest", "script": { - "id": "87c6c041-981a-489f-9947-34632701ee1a", "type": "text/javascript", "exec": [ "" @@ -5307,23 +5167,19 @@ { "listen": "test", "script": { - "id": "ad2b566f-7a46-44b9-ae42-af20e539e19d", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true + ] } ], "event": [ { "listen": "prerequest", "script": { - "id": "76111abb-b426-4486-b1ec-2a7b7153a0d1", "type": "text/javascript", "exec": [ "" @@ -5333,15 +5189,13 @@ { "listen": "test", "script": { - "id": "1e60e05b-96d6-4bbe-bc03-68db6eb53784", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {} + ] }, { "name": "Misc", @@ -5355,7 +5209,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5410,7 +5263,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5465,7 +5317,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -5520,7 +5371,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -5575,7 +5425,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -5630,7 +5479,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -5685,7 +5533,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 403\", function () {", " pm.response.to.have.status(403);", @@ -5740,7 +5587,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 401\", function () {", " pm.response.to.have.status(401);", @@ -5789,7 +5635,6 @@ { "listen": "prerequest", "script": { - "id": "87c6c041-981a-489f-9947-34632701ee1a", "type": "text/javascript", "exec": [ "" @@ -5799,23 +5644,19 @@ { "listen": "test", "script": { - "id": "ad2b566f-7a46-44b9-ae42-af20e539e19d", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true + ] } ], "event": [ { "listen": "prerequest", "script": { - "id": "76111abb-b426-4486-b1ec-2a7b7153a0d1", "type": "text/javascript", "exec": [ "" @@ -5825,15 +5666,13 @@ { "listen": "test", "script": { - "id": "1e60e05b-96d6-4bbe-bc03-68db6eb53784", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {} + ] }, { "name": "Search", @@ -5847,7 +5686,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5889,7 +5727,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5958,7 +5795,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6027,7 +5863,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6096,7 +5931,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6165,7 +5999,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6234,7 +6067,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6303,7 +6135,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6372,7 +6203,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6437,7 +6267,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6503,7 +6332,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6569,7 +6397,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6630,7 +6457,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6706,7 +6532,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -6775,7 +6600,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -6844,7 +6668,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -6913,7 +6736,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -6970,7 +6792,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -7027,7 +6848,6 @@ { "listen": "test", "script": { - "id": "ca548603-f08a-47d7-a4a8-bb68fdb62b31", "exec": [ "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400);", @@ -7095,7 +6915,6 @@ { "listen": "prerequest", "script": { - "id": "87c6c041-981a-489f-9947-34632701ee1a", "type": "text/javascript", "exec": [ "" @@ -7105,23 +6924,19 @@ { "listen": "test", "script": { - "id": "ad2b566f-7a46-44b9-ae42-af20e539e19d", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true + ] } ], "event": [ { "listen": "prerequest", "script": { - "id": "76111abb-b426-4486-b1ec-2a7b7153a0d1", "type": "text/javascript", "exec": [ "" @@ -7131,22 +6946,19 @@ { "listen": "test", "script": { - "id": "1e60e05b-96d6-4bbe-bc03-68db6eb53784", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {} + ] } ], "event": [ { "listen": "prerequest", "script": { - "id": "053175cf-60bc-4840-a318-b5a395c451c9", "type": "text/javascript", "exec": [ "" @@ -7156,13 +6968,11 @@ { "listen": "test", "script": { - "id": "b9784964-418a-4359-b81d-690cc3fcdaa4", "type": "text/javascript", "exec": [ "" ] } } - ], - "protocolProfileBehavior": {} + ] } \ No newline at end of file diff --git a/src/common/eshelper.js b/src/common/eshelper.js index 53e9fff..12de156 100644 --- a/src/common/eshelper.js +++ b/src/common/eshelper.js @@ -11,7 +11,7 @@ const { response } = require('express') * @param {Object} query the HTTP request query * @returns {Object} members and total */ -async function getMembers(query, esClient, currentUser) { +async function getMembers (query, esClient, currentUser) { const handles = _.isArray(query.handles) ? query.handles : [] const handlesLower = _.isArray(query.handlesLower) ? query.handlesLower : [] var userIds = _.isArray(query.userIds) ? query.userIds : [] @@ -44,7 +44,7 @@ async function getMembers(query, esClient, currentUser) { if (handles.length > 0) { boolQueryMembers.push({ query: { terms: { handle: handles } } }) } - boolQueryMembers.push({ match_phrase: { status: "ACTIVE" } }) + boolQueryMembers.push({ match_phrase: { status: 'ACTIVE' } }) if (boolQueryMembers.length > 0) { esQueryMembers.body.query = { bool: { @@ -62,7 +62,7 @@ async function getMembers(query, esClient, currentUser) { * @param {Object} query the HTTP request query * @returns {Object} members skills */ -async function getMembersSkills(query, esClient) { +async function getMembersSkills (query, esClient) { // construct ES query for skills const esQuerySkills = { index: config.get('ES.MEMBER_SKILLS_ES_INDEX'), @@ -91,7 +91,7 @@ async function getMembersSkills(query, esClient) { * @param {Object} query the HTTP request query * @returns {Object} members stats */ -async function getMembersStats(query, esClient) { +async function getMembersStats (query, esClient) { // construct ES query for stats const esQueryStats = { index: config.get('ES.MEMBER_STATS_ES_INDEX'), @@ -120,7 +120,7 @@ async function getMembersStats(query, esClient) { * @param {Object} query the HTTP request query * @returns {Object} suggestion */ -async function getSuggestion(query, esClient, currentUser) { +async function getSuggestion (query, esClient, currentUser) { // construct ES query for members profile suggestion let esSuggestionMembers = { index: config.get('ES.MEMBER_PROFILE_ES_INDEX'), @@ -131,11 +131,11 @@ async function getSuggestion(query, esClient, currentUser) { } if (query.term) { esSuggestionMembers.body.suggest = { - "handle-suggestion": { + 'handle-suggestion': { text: query.term, completion: { size: query.size, - field: "handleSuggest" + field: 'handleSuggest' } } } @@ -150,7 +150,7 @@ async function getSuggestion(query, esClient, currentUser) { * @param {Object} docs the HTTP request query * @returns {Object} total */ -function getTotal(docs) { +function getTotal (docs) { const total = docs.hits.total if (_.isObject(total)) { total = total.value || 0 diff --git a/src/common/helper.js b/src/common/helper.js index ef644d1..d39620e 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -11,25 +11,25 @@ const busApi = require('topcoder-bus-api-wrapper') const elasticsearch = require('elasticsearch') const uuid = require('uuid/v4') const querystring = require('querystring') -const request = require('request'); +const request = require('request') // Color schema for Ratings const RATING_COLORS = [{ color: '#9D9FA0' /* Grey */, - limit: 900, + limit: 900 }, { color: '#69C329' /* Green */, - limit: 1200, + limit: 1200 }, { color: '#616BD5' /* Blue */, - limit: 1500, + limit: 1500 }, { color: '#FCD617' /* Yellow */, - limit: 2200, + limit: 2200 }, { color: '#EF3A3A' /* Red */, - limit: Infinity, -}]; + limit: Infinity +}] // Bus API Client let busApiClient @@ -49,6 +49,18 @@ AWS.config.update(awsConfig) const s3 = new AWS.S3() +const m2mAuth = require('tc-core-library-js').auth.m2m + +const m2m = m2mAuth( + _.pick(config, [ + 'AUTH0_URL', + 'AUTH0_AUDIENCE', + 'AUTH0_CLIENT_ID', + 'AUTH0_CLIENT_SECRET', + 'AUTH0_PROXY_SERVER_URL' + ]) +) + /** * Wrap async function to standard express function * @param {Function} fn the async function @@ -168,9 +180,9 @@ async function getEntityByHashKey (handle, modelName, hashKeyName, value, throwE */ async function getEntityByHashRangeKey (handle, modelName, hashKeyName, hashKeyValue, rangeKeyName, rangeKeyValue, throwError) { return new Promise((resolve, reject) => { - var param = {}; - param[hashKeyName] = hashKeyValue; - param[rangeKeyName] = rangeKeyValue; + var param = {} + param[hashKeyName] = hashKeyValue + param[rangeKeyName] = rangeKeyValue models[modelName].get(param, function (err, result) { if (err) { @@ -184,7 +196,7 @@ async function getEntityByHashRangeKey (handle, modelName, hashKeyName, hashKeyV return resolve({}) } } - ); + ) }) } @@ -216,16 +228,20 @@ async function getMemberByHandle (handle) { */ async function create (modelName, data) { const dbItem = new models[modelName](data) - if (dbItem.hasOwnProperty("traits")) { - if (typeof dbItem.traits == "object") { - dbItem.traits = JSON.stringify(dbItem.traits) + _.each(['traits', 'addresses', 'skills', 'DEVELOP', 'DESIGN', 'DATA_SCIENCE'], property => { + if (dbItem.hasOwnProperty(property)) { + if (typeof dbItem[property] === 'object') { + dbItem[property] = JSON.stringify(dbItem[property]) + } } - } + }) var result = await itemSave(dbItem) - if (result.hasOwnProperty("traits")) { - result.traits = JSON.parse(result.traits) - result.originalItem().traits = JSON.parse(result.originalItem().traits) - } + _.each(['traits', 'addresses', 'skills', 'DEVELOP', 'DESIGN', 'DATA_SCIENCE'], property => { + if (result.hasOwnProperty(property)) { + result[property] = JSON.parse(result[property]) + result.originalItem()[property] = JSON.parse(result.originalItem()[property]) + } + }) return result } @@ -239,31 +255,31 @@ async function update (dbItem, data) { Object.keys(data).forEach((key) => { dbItem[key] = data[key] }) - if (dbItem.hasOwnProperty("addresses")) { - if (typeof dbItem.addresses == "object") { + if (dbItem.hasOwnProperty('addresses')) { + if (typeof dbItem.addresses === 'object') { dbItem.addresses = JSON.stringify(dbItem.addresses) } } - if (dbItem.hasOwnProperty("traits")) { - if (typeof dbItem.traits == "object") { + if (dbItem.hasOwnProperty('traits')) { + if (typeof dbItem.traits === 'object') { dbItem.traits = JSON.stringify(dbItem.traits) } } - if (dbItem.hasOwnProperty("skills")) { - if (typeof dbItem.skills == "object") { + if (dbItem.hasOwnProperty('skills')) { + if (typeof dbItem.skills === 'object') { dbItem.skills = JSON.stringify(dbItem.skills) } } var result = await itemSave(dbItem) - if (result.hasOwnProperty("addresses")) { + if (result.hasOwnProperty('addresses')) { result.addresses = JSON.parse(result.addresses) result.originalItem().addresses = JSON.parse(result.originalItem().addresses) } - if (result.hasOwnProperty("traits")) { + if (result.hasOwnProperty('traits')) { result.traits = JSON.parse(result.traits) result.originalItem().traits = JSON.parse(result.originalItem().traits) } - if (result.hasOwnProperty("skills")) { + if (result.hasOwnProperty('skills')) { result.skills = JSON.parse(result.skills) result.originalItem().skills = JSON.parse(result.originalItem().skills) } @@ -491,26 +507,26 @@ function canManageMember (currentUser, member) { function cleanUpStatistics (stats, fields) { // cleanup - convert string to object - for (count = 0; count < stats.length; count++) { - if (stats[count].hasOwnProperty("maxRating")) { - if (typeof stats[count].maxRating == "string") { + for (let count = 0; count < stats.length; count++) { + if (stats[count].hasOwnProperty('maxRating')) { + if (typeof stats[count].maxRating === 'string') { stats[count].maxRating = JSON.parse(stats[count].maxRating) } // set the rating color stats[count].maxRating.ratingColor = this.getRatingColor(stats[count].maxRating.rating) } - if (stats[count].hasOwnProperty("DATA_SCIENCE")) { - if (typeof stats[count].DATA_SCIENCE == "string") { + if (stats[count].hasOwnProperty('DATA_SCIENCE')) { + if (typeof stats[count].DATA_SCIENCE === 'string') { stats[count].DATA_SCIENCE = JSON.parse(stats[count].DATA_SCIENCE) } } - if (stats[count].hasOwnProperty("DESIGN")) { - if (typeof stats[count].DESIGN == "string") { + if (stats[count].hasOwnProperty('DESIGN')) { + if (typeof stats[count].DESIGN === 'string') { stats[count].DESIGN = JSON.parse(stats[count].DESIGN) } } - if (stats[count].hasOwnProperty("DEVELOP")) { - if (typeof stats[count].DEVELOP == "string") { + if (stats[count].hasOwnProperty('DEVELOP')) { + if (typeof stats[count].DEVELOP === 'string') { stats[count].DEVELOP = JSON.parse(stats[count].DEVELOP) } } @@ -523,8 +539,8 @@ function cleanUpStatistics (stats, fields) { } function convertToObjectSkills (skill) { - if (skill.hasOwnProperty("skills")) { - if (typeof skill.skills == "string") { + if (skill.hasOwnProperty('skills')) { + if (typeof skill.skills === 'string') { skill.skills = JSON.parse(skill.skills) } } @@ -532,16 +548,16 @@ function convertToObjectSkills (skill) { } function cleanupSkills (memberEnteredSkill, member) { - if (memberEnteredSkill.hasOwnProperty("userHandle")) { + if (memberEnteredSkill.hasOwnProperty('userHandle')) { memberEnteredSkill.handle = memberEnteredSkill.userHandle } - if (!memberEnteredSkill.hasOwnProperty("userId")) { + if (!memberEnteredSkill.hasOwnProperty('userId')) { memberEnteredSkill.userId = member.userId } - if (!memberEnteredSkill.hasOwnProperty("handle")) { + if (!memberEnteredSkill.hasOwnProperty('handle')) { memberEnteredSkill.handle = member.handle } - if (!memberEnteredSkill.hasOwnProperty("handleLower")) { + if (!memberEnteredSkill.hasOwnProperty('handleLower')) { memberEnteredSkill.handleLower = member.handleLower } return memberEnteredSkill @@ -549,17 +565,17 @@ function cleanupSkills (memberEnteredSkill, member) { function mergeSkills (memberEnteredSkill, memberAggregatedSkill, allTags) { // process skills in member entered skill - if (memberEnteredSkill.hasOwnProperty("skills")) { - var tempSkill = {} + if (memberEnteredSkill.hasOwnProperty('skills')) { + let tempSkill = {} _.forIn(memberEnteredSkill.skills, (value, key) => { if (!value.hidden) { var tag = this.findTagById(allTags, Number(key)) - if(tag) { + if (tag) { value.tagName = tag.name - if (!value.hasOwnProperty("sources")) { + if (!value.hasOwnProperty('sources')) { value.sources = [ 'USER_ENTERED' ] } - if (!value.hasOwnProperty("score")) { + if (!value.hasOwnProperty('score')) { value.score = 0 } tempSkill[key] = value @@ -573,8 +589,8 @@ function mergeSkills (memberEnteredSkill, memberAggregatedSkill, allTags) { memberEnteredSkill.skills = tempSkill } else { // process skills in member aggregated skill - if (memberAggregatedSkill.hasOwnProperty("skills")) { - var tempSkill = {} + if (memberAggregatedSkill.hasOwnProperty('skills')) { + let tempSkill = {} memberEnteredSkill.skills = mergeAggregatedSkill(memberAggregatedSkill, allTags, tempSkill) } else { memberEnteredSkill.skills = {} @@ -588,12 +604,12 @@ function mergeAggregatedSkill (memberAggregatedSkill, allTags, tempSkill) { var value = memberAggregatedSkill.skills[key] if (!value.hidden) { var tag = findTagById(allTags, Number(key)) - if(tag) { - if (value.hasOwnProperty("sources")) { - if(value.sources.includes("CHALLENGE")) { + if (tag) { + if (value.hasOwnProperty('sources')) { + if (value.sources.includes('CHALLENGE')) { if (tempSkill[key]) { value.tagName = tag.name - if (!value.hasOwnProperty("score")) { + if (!value.hasOwnProperty('score')) { value.score = tempSkill[key].score } else { if (value.score <= tempSkill[key].score) { @@ -603,7 +619,7 @@ function mergeAggregatedSkill (memberAggregatedSkill, allTags, tempSkill) { value.sources.push(tempSkill[key].sources[0]) } else { value.tagName = tag.name - if (!value.hasOwnProperty("score")) { + if (!value.hasOwnProperty('score')) { value.score = 0 } } @@ -616,7 +632,7 @@ function mergeAggregatedSkill (memberAggregatedSkill, allTags, tempSkill) { return tempSkill } -async function getAllTags(url) { +async function getAllTags (url) { return new Promise(function (resolve, reject) { request({ url: url }, function (error, response, body) { @@ -624,24 +640,71 @@ async function getAllTags(url) { reject(new errors.NotFoundError(`Tags not found. ` + error)) } var allTags = JSON.parse(body) - resolve(allTags.result.content); + resolve(allTags.result.content) } - ); + ) }) } function findTagById (data, id) { - return _.find(data, { 'id': id }); + return _.find(data, { 'id': id }) } -function getRatingColor(rating) { - let i = 0; const r = Number(rating); - while (RATING_COLORS[i].limit <= r) i += 1; - return RATING_COLORS[i].color || 'black'; +function getRatingColor (rating) { + let i = 0; const r = Number(rating) + while (RATING_COLORS[i].limit <= r) i += 1 + return RATING_COLORS[i].color || 'black' } -function paginate(array, page_size, page_number) { - return array.slice(page_number * page_size, page_number * page_size + page_size); +function paginate (array, page_size, page_number) { + return array.slice(page_number * page_size, page_number * page_size + page_size) +} + +async function parseGroupIds (groupIds) { + const idArray = _.split(groupIds, ',') + const newIdArray = [] + for (const id of idArray) { + if (_.isInteger(_.toNumber(id))) { + newIdArray.push(id) + } else { + try { + const { oldId } = await getGroupId(id) + if (oldId != null && oldId.trim() != '') { + newIdArray.push(oldId) + } + } catch (err) { } + } + } + return newIdArray +} + +async function getGroupId (id) { + const token = await getM2MToken() + return new Promise(function (resolve, reject) { + request({ url: `${config.GROUPS_API_URL}/${id}`, + headers: { + Authorization: `Bearer ${token}` + } }, + function (error, response, body) { + if (response.statusCode === 200) { + resolve(JSON.parse(body)) + } else { + reject(error) + } + } + ) + }) +} + +/* + * Function to get M2M token + * @returns {Promise} + */ +const getM2MToken = () => { + return m2m.getMachineToken( + config.AUTH0_CLIENT_ID, + config.AUTH0_CLIENT_SECRET + ) } module.exports = { @@ -670,5 +733,8 @@ module.exports = { getAllTags, findTagById, getRatingColor, - paginate + paginate, + parseGroupIds, + getGroupId, + getM2MToken } diff --git a/src/init-db.js b/src/init-db.js index 9af0204..447edd8 100644 --- a/src/init-db.js +++ b/src/init-db.js @@ -16,6 +16,10 @@ const initDB = async () => { for (const item of memberStats) { await item.delete() } + const memberStatsPrivate = await helper.scan('MemberStatsPrivate') + for (const item of memberStatsPrivate) { + await item.delete() + } const memberHistoryStats = await helper.scan('MemberHistoryStats') for (const item of memberHistoryStats) { await item.delete() @@ -28,8 +32,12 @@ const initDB = async () => { for (const item of memberFinancials) { await item.delete() } - const memberSkills = await helper.scan('MemberSkill') - for (const item of memberSkills) { + const memberAggregatedSkills = await helper.scan('MemberAggregatedSkills') + for (const item of memberAggregatedSkills) { + await item.delete() + } + const memberEnteredSkills = await helper.scan('MemberEnteredSkills') + for (const item of memberEnteredSkills) { await item.delete() } const traits = await helper.scan('MemberTrait') diff --git a/src/init-es.js b/src/init-es.js index ab15f35..7b37aee 100644 --- a/src/init-es.js +++ b/src/init-es.js @@ -26,6 +26,12 @@ const initES = async () => { } catch (err) { // ignore } + logger.info(`Delete index ${config.ES.MEMBER_STATS_ES_INDEX} if any.`) + try { + await client.indices.delete({ index: config.ES.MEMBER_STATS_ES_INDEX }) + } catch (err) { + // ignore + } } let exists = await client.indices.exists({ index: config.ES.MEMBER_PROFILE_ES_INDEX }) @@ -68,6 +74,27 @@ const initES = async () => { body }) } + exists = await client.indices.exists({ index: config.ES.MEMBER_STATS_ES_INDEX }) + if (exists) { + logger.info(`The index ${config.ES.MEMBER_STATS_ES_INDEX} exists.`) + } else { + logger.info(`The index ${config.ES.MEMBER_STATS_ES_INDEX} will be created.`) + + const body = { mappings: {} } + body.mappings[config.get('ES.MEMBER_STATS_ES_TYPE')] = { + properties: { + handleLower: { type: 'keyword' }, + handle: { type: 'keyword' }, + groupId: { type: 'keyword' }, + userId: { type: 'keyword' } + } + } + + await client.indices.create({ + index: config.ES.MEMBER_STATS_ES_INDEX, + body + }) + } } initES().then(() => { diff --git a/src/models/Member.js b/src/models/Member.js index 2099f3d..2f6435e 100644 --- a/src/models/Member.js +++ b/src/models/Member.js @@ -25,10 +25,10 @@ const schema = new Schema({ type: String, required: false, index: [ - { - global: true, - name: 'email-index' - }] + { + global: true, + name: 'email-index' + }] }, maxRating: { type: Object, diff --git a/src/models/MemberStats.js b/src/models/MemberStats.js index 2596d9e..ad21ae5 100644 --- a/src/models/MemberStats.js +++ b/src/models/MemberStats.js @@ -21,7 +21,7 @@ const schema = new Schema({ required: true }, maxRating: { - type: String, + type: Object, required: false }, challenges: { @@ -69,4 +69,4 @@ const schema = new Schema({ throughput: { read: 4, write: 2 } }) -module.exports = schema \ No newline at end of file +module.exports = schema diff --git a/src/models/MemberStatsPrivate.js b/src/models/MemberStatsPrivate.js index 002def2..6395084 100644 --- a/src/models/MemberStatsPrivate.js +++ b/src/models/MemberStatsPrivate.js @@ -26,7 +26,7 @@ const schema = new Schema({ required: true }, maxRating: { - type: String, + type: Object, required: false }, challenges: { @@ -74,4 +74,4 @@ const schema = new Schema({ throughput: { read: 4, write: 2 } }) -module.exports = schema \ No newline at end of file +module.exports = schema diff --git a/src/scripts/seed-data.js b/src/scripts/seed-data.js index ff2ce5c..4727e10 100644 --- a/src/scripts/seed-data.js +++ b/src/scripts/seed-data.js @@ -24,7 +24,7 @@ const members = [{ otherLangName: 'en', handle: 'denis', handleLower: 'denis', - status: 'active', + status: 'ACTIVE', email: 'denis@topcoder.com', newEmail: 'denis2@topcoder.com', emailVerifyToken: 'abcdefg', @@ -66,7 +66,7 @@ const members = [{ otherLangName: 'en', handle: 'testing', handleLower: 'testing', - status: 'active', + status: 'ACTIVE', email: 'testing@topcoder.com', newEmail: 'testing2@topcoder.com', emailVerifyToken: 'abcdefg', @@ -186,7 +186,181 @@ const memberStats = { }, challenges: 10, wins: 8, - develop: { + DEVELOP: { + challenges: 3, + wins: 2, + subTracks: [ + { + id: 11111, + name: 'test1', + challenges: 20, + wins: 3, + rank: { + rating: 1212, + activePercentile: 80, + activeRank: 1, + activeCountryRank: 2, + activeSchoolRank: 1, + overallPercentile: 10, + overallRank: 2, + overallCountryRank: 1, + overallSchoolRank: 1, + volatility: 60, + reliability: 80, + maxRating: 1999, + minRating: 1200 + }, + submissions: { + numInquiries: 1, + submissions: 2, + submissionRate: 3, + passedScreening: 1, + screeningSuccessRate: 2, + passedReview: 3, + reviewSuccessRate: 1, + appeals: 2, + appealSuccessRate: 3, + maxScore: 1, + minScore: 2, + avgScore: 3, + avgPlacement: 1, + winPercent: 2 + }, + mostRecentEventDate: '2020-02-15T14:05:16.275Z', + mostRecentSubmission: '2020-02-15T14:05:16.275Z' + } + ], + mostRecentEventDate: '2020-02-15T14:05:16.275Z', + mostRecentSubmission: '2020-02-15T14:05:16.275Z' + }, + DESIGN: { + challenges: 1, + wins: 2, + subTracks: [ + { + id: 1, + name: 'test', + numInquiries: 1, + challenges: 2, + wins: 3, + winPercent: 1, + avgPlacement: 2, + submissions: 3, + submissionRate: 1, + passedScreening: 2, + screeningSuccessRate: 3, + mostRecentEventDate: '2020-02-15T14:05:16.275Z', + mostRecentSubmission: '2020-02-15T14:05:16.275Z' + } + ], + mostRecentEventDate: '2020-02-15T14:05:16.275Z', + mostRecentSubmission: '2020-02-15T14:05:16.275Z' + }, + DATA_SCIENCE: { + challenges: 10, + wins: 0, + srm: { + challenges: 1, + wins: 2, + rank: { + rating: 3, + percentile: 0, + rank: 1, + countryRank: 2, + schoolRank: 1, + volatility: 20, + maximumRating: 10, + minimumRating: 20, + defaultLanguage: 'EN', + competitions: 1, + mostRecentEventName: 'test', + mostRecentEventDate: '2020-02-15T14:05:16.276Z' + }, + challengeDetails: [ + { + levelName: 'test', + challenges: 10, + failedChallenges: 20 + } + ], + division1: [ + { + levelName: 'level 1', + problemsSubmitted: 1, + problemsFailed: 2, + problemsSysByTest: 0 + } + ], + division2: [ + { + levelName: 'level 2', + problemsSubmitted: 1, + problemsFailed: 2, + problemsSysByTest: 0 + } + ], + mostRecentEventName: 'test', + mostRecentEventDate: '2020-02-15T14:05:16.276Z', + mostRecentSubmission: '2020-02-15T14:05:16.276Z' + }, + marathonMatch: { + challenges: 1, + wins: 2, + rank: { + rating: 1, + competitions: 2, + avgRank: 1, + avgNumSubmissions: 0, + bestRank: 0, + topFiveFinishes: 0, + topTenFinishes: 0, + rank: 10, + percentile: 20, + volatility: 10, + minimumRating: 20, + maximumRating: 10, + countryRank: 20, + schoolRank: 10, + defaultLanguage: 'test', + mostRecentEventName: 'test', + mostRecentEventDate: '2020-02-15T14:05:16.276Z' + }, + mostRecentEventName: 'test', + mostRecentEventDate: '2020-02-15T14:05:16.276Z', + mostRecentSubmission: '2020-02-15T14:05:16.276Z' + }, + mostRecentEventName: 'test', + mostRecentEventDate: '2020-02-15T14:05:16.276Z', + mostRecentSubmission: '2020-02-15T14:05:16.276Z' + }, + COPILOT: { + contests: 10, + projects: 20, + failures: 10, + reposts: 20, + activeContests: 10, + activeProjects: 30, + fulfillment: 40 + }, + updatedAt: '2020-02-08T07:38:50.088Z', + createdAt: '2020-02-09T07:38:50.088Z', + createdBy: 'test1', + updatedBy: 'test2' +} + +const memberPrivateStats = { + userId: 123, + groupId: 20000001, + handle: 'denis', + handleLower: 'denis', + maxRating: { + rating: 1565, + track: 'develop', + subTrack: 'code' + }, + challenges: 10, + wins: 8, + DEVELOP: { challenges: 3, wins: 2, subTracks: [ @@ -233,7 +407,7 @@ const memberStats = { mostRecentEventDate: '2020-02-15T14:05:16.275Z', mostRecentSubmission: '2020-02-15T14:05:16.275Z' }, - design: { + DESIGN: { challenges: 1, wins: 2, subTracks: [ @@ -256,7 +430,7 @@ const memberStats = { mostRecentEventDate: '2020-02-15T14:05:16.275Z', mostRecentSubmission: '2020-02-15T14:05:16.275Z' }, - dataScience: { + DATA_SCIENCE: { challenges: 10, wins: 0, srm: { @@ -333,7 +507,7 @@ const memberStats = { mostRecentEventDate: '2020-02-15T14:05:16.276Z', mostRecentSubmission: '2020-02-15T14:05:16.276Z' }, - copilot: { + COPILOT: { contests: 10, projects: 20, failures: 10, @@ -358,7 +532,7 @@ const memberFinancial = { updatedBy: 'test2' } -const memberSkills = { +const memberAggregatedSkills = { userId: 123, handle: 'denis', handleLower: 'denis', @@ -376,8 +550,33 @@ const memberSkills = { sources: ['source3'] } }, - updatedAt: '2020-02-08T07:38:50.088Z', - createdAt: '2020-02-09T07:38:50.088Z', + updatedAt: 1621895619502, + createdAt: 1621895619502, + createdBy: 'test1', + updatedBy: 'test2' +} + +const memberEnteredSkills = { + userId: 123, + userHandle: 'POSTMANE2E-denis', + handleLower: 'postmane2e-denis', + skills: { + 286: { + hidden: false, + score: 1888, + sources: ['source1', 'source2'] + }, + 380: { + hidden: true, + score: 1555, + sources: ['source3'] + }, + 311: { + hidden: false + } + }, + updatedAt: 1621895619502, + createdAt: 1621895619502, createdBy: 'test1', updatedBy: 'test2' } @@ -401,7 +600,7 @@ async function seedData () { await esClient.create({ index: config.ES.MEMBER_TRAIT_ES_INDEX, type: config.ES.MEMBER_TRAIT_ES_TYPE, - id: '123basic_id', + id: '123_basic_id', body: { userId: 123, traitId: 'basic_id', @@ -421,8 +620,10 @@ async function seedData () { await helper.create('MemberDistributionStats', distribution2) await helper.create('MemberHistoryStats', historyStats) await helper.create('MemberStats', memberStats) + await helper.create('MemberStatsPrivate', memberPrivateStats) await helper.create('MemberFinancial', memberFinancial) - await helper.create('MemberSkill', memberSkills) + await helper.create('MemberAggregatedSkills', memberAggregatedSkills) + await helper.create('MemberEnteredSkills', memberEnteredSkills) } seedData() diff --git a/src/services/MemberService.js b/src/services/MemberService.js index e13a948..a23f7a7 100644 --- a/src/services/MemberService.js +++ b/src/services/MemberService.js @@ -17,7 +17,7 @@ const esClient = helper.getESClient() const MEMBER_FIELDS = ['userId', 'handle', 'handleLower', 'firstName', 'lastName', 'tracks', 'status', 'addresses', 'description', 'email', 'homeCountryCode', 'competitionCountryCode', 'photoURL', 'maxRating', - 'createdAt', 'createdBy','updatedAt','updatedBy'] + 'createdAt', 'createdBy', 'updatedAt', 'updatedBy'] const INTERNAL_MEMBER_FIELDS = ['newEmail', 'emailVerifyToken', 'emailVerifyTokenDate', 'newEmailVerifyToken', 'newEmailVerifyTokenDate', 'handleSuggest'] @@ -32,10 +32,10 @@ function cleanMember (currentUser, members, selectFields) { var response if (Array.isArray(members)) { const mb = members[0].originalItem ? members[0].originalItem() : members[0] - response = omitMemberAttributes (currentUser, mb) + response = omitMemberAttributes(currentUser, mb) } else { const mb = members.originalItem ? members.originalItem() : members - response = omitMemberAttributes (currentUser, mb) + response = omitMemberAttributes(currentUser, mb) } // select fields if (selectFields) { @@ -88,11 +88,11 @@ async function getMember (currentUser, handle, query) { // get the 'maxRating' from stats if (_.includes(selectFields, 'maxRating')) { for (let i = 0; i < members.length; i += 1) { - const memberStatsFields = { "fields": "userId,groupId,handleLower,maxRating" } - const memberStats = await statisticsService.getMemberStats(currentUser, members[i].handleLower, + const memberStatsFields = { 'fields': 'userId,groupId,handleLower,maxRating' } + const memberStats = await statisticsService.getMemberStats(currentUser, members[i].handleLower, memberStatsFields, false) - if(memberStats[0]) { - if (memberStats[0].hasOwnProperty("maxRating")) { + if (memberStats[0]) { + if (memberStats[0].hasOwnProperty('maxRating')) { members[i].maxRating = memberStats[0].maxRating } else { members[i].maxRating = {} @@ -141,7 +141,7 @@ async function updateMember (currentUser, handle, query, data) { query: { bool: { filter: [ { - match_phrase: { email : data.email } + match_phrase: { email: data.email } } ] } } @@ -302,7 +302,7 @@ async function uploadPhoto (currentUser, handle, files) { } MB.`) } var fileExt = file.name.substr(file.name.lastIndexOf('.')) - var fileName = handle + "-" + new Date().getTime() + fileExt + var fileName = handle + '-' + new Date().getTime() + fileExt // upload photo to S3 // const photoURL = await helper.uploadPhotoToS3(file.data, file.mimetype, file.name) const photoURL = await helper.uploadPhotoToS3(file.data, file.mimetype, fileName) diff --git a/src/services/MemberTraitService.js b/src/services/MemberTraitService.js index d39ed6f..27041d4 100644 --- a/src/services/MemberTraitService.js +++ b/src/services/MemberTraitService.js @@ -5,7 +5,7 @@ const _ = require('lodash') const Joi = require('joi') const config = require('config') -const moment = require('moment'); +const moment = require('moment') const helper = require('../common/helper') const logger = require('../common/logger') const errors = require('../common/errors') @@ -56,7 +56,7 @@ async function getTraits (currentUser, handle, query) { result = _.filter(result, (item) => _.includes(traitIds, item.traitId)) } // convert date time for traits data - _.filter(result, (item) => _.forEach(item.traits.data, function(value) { + _.filter(result, (item) => _.forEach(item.traits.data, function (value) { if (value.hasOwnProperty('birthDate')) { if (value.birthDate) { value.birthDate = moment(value.birthDate).toDate().toISOString() @@ -125,9 +125,9 @@ async function createTraits (currentUser, handle, data) { trait.createdAt = new Date().toISOString() trait.createdBy = Number(currentUser.userId || currentUser.sub) if (trait.traits) { - trait.traits = { "traitId": trait.traitId, "data": trait.traits.data } + trait.traits = { 'traitId': trait.traitId, 'data': trait.traits.data } } else { - trait.traits = { "traitId": trait.traitId, "data": [] } + trait.traits = { 'traitId': trait.traitId, 'data': [] } } // update db await helper.create('MemberTrait', trait) @@ -186,9 +186,9 @@ async function updateTraits (currentUser, handle, data) { existing.updatedAt = new Date().toISOString() existing.updatedBy = Number(currentUser.userId || currentUser.sub) if (trait.traits) { - existing.traits = { "traitId": trait.traitId, "data": trait.traits.data } + existing.traits = { 'traitId': trait.traitId, 'data': trait.traits.data } } else { - existing.traits = { "traitId": trait.traitId, "data": [] } + existing.traits = { 'traitId': trait.traitId, 'data': [] } } // update db var updateDb = await helper.update(existing, {}) diff --git a/src/services/SearchService.js b/src/services/SearchService.js index 4cbdab1..9d87d99 100644 --- a/src/services/SearchService.js +++ b/src/services/SearchService.js @@ -28,7 +28,7 @@ const esClient = helper.getESClient() * @param {Object} query the query parameters * @returns {Object} the search result */ -async function searchMembers(currentUser, query) { +async function searchMembers (currentUser, query) { // validate and parse fields param let fields = helper.parseCommaSeparatedString(query.fields, MEMBER_FIELDS) || MEMBER_FIELDS // if current user is not admin and not M2M, then exclude the admin/M2M only fields @@ -67,7 +67,7 @@ async function searchMembers(currentUser, query) { if (!item.skills) { item.skills = {} } - return item; + return item }) // merge overall members and stats @@ -79,7 +79,7 @@ async function searchMembers(currentUser, query) { // add the maxrating item.maxRating = mbrsSkillsStatsKeys[item.userId].maxRating // set the rating color - if (item.maxRating.hasOwnProperty("rating")) { + if (item.maxRating.hasOwnProperty('rating')) { item.maxRating.ratingColor = helper.getRatingColor(item.maxRating.rating) } } @@ -88,11 +88,11 @@ async function searchMembers(currentUser, query) { } else { item.stats = [] } - return item; + return item }) // sort the data - results = _.orderBy(resultMbrsSkillsStats, ['handleLower'],[query.sort]) - // filter member based on fields + results = _.orderBy(resultMbrsSkillsStats, ['handleLower'], [query.sort]) + // filter member based on fields results = _.map(results, (item) => _.pick(item, fields)) } return { total: total, page: query.page, perPage: query.perPage, result: results } @@ -111,7 +111,7 @@ searchMembers.schema = { fields: Joi.string(), page: Joi.page(), perPage: Joi.perPage(), - sort: Joi.sort(), + sort: Joi.sort() }) } @@ -121,7 +121,7 @@ searchMembers.schema = { * @param {Object} query the query parameters * @returns {Object} the autocomplete result */ -async function autocomplete(currentUser, query) { +async function autocomplete (currentUser, query) { // validate and parse fields param let fields = helper.parseCommaSeparatedString(query.fields, MEMBER_AUTOCOMPLETE_FIELDS) || MEMBER_AUTOCOMPLETE_FIELDS // // if current user is not admin and not M2M, then exclude the admin/M2M only fields @@ -131,15 +131,15 @@ async function autocomplete(currentUser, query) { // } // get suggestion based on querys term const docsSuggestions = await eshelper.getSuggestion(query, esClient, currentUser) - if (docsSuggestions.hasOwnProperty("suggest")) { - const totalSuggest = docsSuggestions.suggest["handle-suggestion"][0].options.length - var results = docsSuggestions.suggest["handle-suggestion"][0].options + if (docsSuggestions.hasOwnProperty('suggest')) { + const totalSuggest = docsSuggestions.suggest['handle-suggestion'][0].options.length + var results = docsSuggestions.suggest['handle-suggestion'][0].options // custom filter & sort - let regex = new RegExp(`^${query.term}`, `i`); + let regex = new RegExp(`^${query.term}`, `i`) results = results - .filter(x => regex.test(x.payload.handle)) - .sort((a, b) => a.payload.handle.localeCompare(b.payload.handle)); - // filter member based on fields + .filter(x => regex.test(x.payload.handle)) + .sort((a, b) => a.payload.handle.localeCompare(b.payload.handle)) + // filter member based on fields results = _.map(results, (item) => _.pick(item.payload, fields)) // custom pagination results = helper.paginate(results, query.perPage, query.page - 1) @@ -156,7 +156,7 @@ autocomplete.schema = { page: Joi.page(), perPage: Joi.perPage(), size: Joi.size(), - sort: Joi.sort(), + sort: Joi.sort() }) } diff --git a/src/services/StatisticsService.js b/src/services/StatisticsService.js index 9a271f2..498d2cc 100644 --- a/src/services/StatisticsService.js +++ b/src/services/StatisticsService.js @@ -17,7 +17,7 @@ const HISTORY_STATS_FIELDS = ['userId', 'groupId', 'handle', 'handleLower', 'DEV 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'] const MEMBER_STATS_FIELDS = ['userId', 'groupId', 'handle', 'handleLower', 'maxRating', - 'challenges', 'wins','DEVELOP', 'DESIGN', 'DATA_SCIENCE', 'copilot', 'createdAt', + 'challenges', 'wins', 'DEVELOP', 'DESIGN', 'DATA_SCIENCE', 'copilot', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'] const MEMBER_SKILL_FIELDS = ['userId', 'handle', 'handleLower', 'skills', @@ -106,7 +106,7 @@ async function getHistoryStats (currentUser, handle, query) { if (!groupIds) { // get statistics by member user id from dynamodb let statsDb = await helper.getEntityByHashKey(handle, 'MemberHistoryStats', 'userId', member.userId, true) - if(!_.isEmpty(statsDb)) { + if (!_.isEmpty(statsDb)) { statsDb.originalItem().groupId = 10 overallStat.push(statsDb.originalItem()) } @@ -114,17 +114,17 @@ async function getHistoryStats (currentUser, handle, query) { if (groupIds) { for (const groupId of groupIds.split(',')) { let statsDb - if(groupId == "10") { + if (groupId === '10') { // get statistics by member user id from dynamodb statsDb = await helper.getEntityByHashKey(handle, 'MemberHistoryStats', 'userId', member.userId, false) - if(!_.isEmpty(statsDb)) { + if (!_.isEmpty(statsDb)) { statsDb.originalItem().groupId = 10 } } else { // get statistics private by member user id from dynamodb statsDb = await helper.getEntityByHashRangeKey(handle, 'MemberHistoryStatsPrivate', 'userId', member.userId, 'groupId', groupId, false) } - if(!_.isEmpty(statsDb)) { + if (!_.isEmpty(statsDb)) { overallStat.push(statsDb.originalItem()) } } @@ -166,18 +166,18 @@ async function getMemberStats (currentUser, handle, query, throwError) { stat = await esClient.get({ index: config.ES.MEMBER_STATS_ES_INDEX, type: config.ES.MEMBER_STATS_ES_TYPE, - id: member.userId + "_10" - }); - if (stat.hasOwnProperty("_source")) { + id: member.userId + '_10' + }) + if (stat.hasOwnProperty('_source')) { stat = stat._source } } catch (error) { - if (error.displayName == "NotFound") { + if (error.displayName === 'NotFound') { // get statistics by member user id from dynamodb stat = await helper.getEntityByHashKey(handle, 'MemberStats', 'userId', member.userId, throwError) if (!_.isEmpty(stat, true)) { stat.originalItem().groupId = 10 - stat = stat.originalItem + stat = stat.originalItem() } } } @@ -186,26 +186,28 @@ async function getMemberStats (currentUser, handle, query, throwError) { } } if (groupIds) { - for (const groupId of groupIds.split(',')) { + groupIds = await helper.parseGroupIds(groupIds) + groupIds = _.uniq(groupIds) + for (const groupId of groupIds) { let stat try { // get statistics private by member user id from Elasticsearch stat = await esClient.get({ index: config.ES.MEMBER_STATS_ES_INDEX, type: config.ES.MEMBER_STATS_ES_TYPE, - id: member.userId + "_" + groupId - }); - if (stat.hasOwnProperty("_source")) { + id: member.userId + '_' + groupId + }) + if (stat.hasOwnProperty('_source')) { stat = stat._source } } catch (error) { - if (error.displayName == "NotFound") { - if(groupId == "10") { + if (error.displayName === 'NotFound') { + if (groupId === '10') { // get statistics by member user id from dynamodb stat = await helper.getEntityByHashKey(handle, 'MemberStats', 'userId', member.userId, false) if (!_.isEmpty(stat, true)) { stat.originalItem().groupId = 10 - stat = stat.originalItem + stat = stat.originalItem() } } else { // get statistics private by member user id from dynamodb @@ -248,7 +250,7 @@ async function getMemberSkills (currentUser, handle, query, throwError) { // get member by handle const member = await helper.getMemberByHandle(handle) // fetch tags data - if(!this.allTags) { + if (!this.allTags) { this.allTags = await helper.getAllTags(config.TAGS.TAGS_BASE_URL + config.TAGS.TAGS_API_VERSION + config.TAGS.TAGS_FILTER) } // get member entered skill by member user id @@ -297,7 +299,7 @@ async function partiallyUpdateMemberSkills (currentUser, handle, data) { throw new errors.ForbiddenError('You are not allowed to update the member skills.') } // fetch tags data - if(!this.allTags) { + if (!this.allTags) { this.allTags = await helper.getAllTags(config.TAGS.TAGS_BASE_URL + config.TAGS.TAGS_API_VERSION + config.TAGS.TAGS_FILTER) } // get member entered skill by member user id @@ -312,12 +314,12 @@ async function partiallyUpdateMemberSkills (currentUser, handle, data) { var tempSkill = {} _.forIn(data, (value, key) => { var tag = helper.findTagById(this.allTags, Number(key)) - if(tag) { + if (tag) { value.tagName = tag.name - if (!value.hasOwnProperty("hidden")) { + if (!value.hasOwnProperty('hidden')) { value.hidden = false } - if (!value.hasOwnProperty("score")) { + if (!value.hasOwnProperty('score')) { value.score = 1 } value.sources = [ 'USER_ENTERED' ]