diff --git a/config/default.js b/config/default.js index 72decef..b6f57d3 100644 --- a/config/default.js +++ b/config/default.js @@ -7,7 +7,7 @@ module.exports = { PORT: process.env.PORT || 3000, API_VERSION: process.env.API_VERSION || 'v5', AUTH_SECRET: process.env.AUTH_SECRET || 'mysecret', - VALID_ISSUERS: process.env.VALID_ISSUERS || '["https://api.topcoder-dev.com", "https://api.topcoder.com", "https://topcoder-dev.auth0.com/"]', + VALID_ISSUERS: process.env.VALID_ISSUERS || '["https://api.topcoder-dev.com", "https://api.topcoder.com", "https://topcoder-dev.auth0.com/", "https://auth.topcoder-dev.com/"]', // used to get M2M token AUTH0_URL: process.env.AUTH0_URL, @@ -27,7 +27,7 @@ module.exports = { TAGS_API_VERSION: process.env.TAGS_API_VERSION || '/v3', 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', + 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, @@ -111,5 +111,10 @@ module.exports = { // 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'] + : ['createdBy', 'updatedBy'], + + // Public group id + PUBLIC_GROUP_ID: process.env.PUBLIC_GROUP_ID || '10', + // Private group ids will be excluded from results for non-admin users. + PRIVATE_GROUP_IDS: JSON.parse(process.env.PRIVATE_GROUP_IDS || '["20000000"]') } diff --git a/docs/member-api.postman_collection.json b/docs/member-api.postman_collection.json index 4c34818..a6b2912 100644 --- a/docs/member-api.postman_collection.json +++ b/docs/member-api.postman_collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "03129edd-111f-4875-924d-2ffd2c0c18eb", + "_postman_id": "2e554101-d99e-4522-8ce5-39ff8e6cca0e", "name": "member-api", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, @@ -3617,13 +3617,13 @@ } ], "url": { - "raw": "{{URL}}/members/standlove/stats/history?fields=userId,handle,DATA_SCIENCE,DEVELOP,createdBy", + "raw": "{{URL}}/members/denis/stats/history?fields=userId,handle,DATA_SCIENCE,DEVELOP,createdBy", "host": [ "{{URL}}" ], "path": [ "members", - "standlove", + "denis", "stats", "history" ], @@ -3667,20 +3667,138 @@ } ], "url": { - "raw": "{{URL}}/members/standlove/stats/history?groupIds=10,20000000&fields=userId,handle,groupId", + "raw": "{{URL}}/members/denis/stats/history?groupIds=10,20000001&fields=userId,handle,groupId", "host": [ "{{URL}}" ], "path": [ "members", - "standlove", + "denis", + "stats", + "history" + ], + "query": [ + { + "key": "groupIds", + "value": "10,20000001" + }, + { + "key": "fields", + "value": "userId,handle,groupId" + } + ] + } + }, + "response": [] + }, + { + "name": "get member history private statistics - groupIds by admin", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{admin_token}}", + "type": "text" + } + ], + "url": { + "raw": "{{URL}}/members/denis/stats/history?groupIds=10,20000001&fields=userId,handle,groupId", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "denis", + "stats", + "history" + ], + "query": [ + { + "key": "groupIds", + "value": "10,20000001" + }, + { + "key": "fields", + "value": "userId,handle,groupId" + } + ] + } + }, + "response": [] + }, + { + "name": "get member history private statistics - groupIds by member himself", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{user_token}}", + "type": "text" + } + ], + "url": { + "raw": "{{URL}}/members/denis/stats/history?groupIds=10,20000001&fields=userId,handle,groupId", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "denis", "stats", "history" ], "query": [ { "key": "groupIds", - "value": "10,20000000" + "value": "10,20000001" }, { "key": "fields", @@ -3721,13 +3839,13 @@ } ], "url": { - "raw": "{{URL}}/members/standlove/stats/history?groupIds=10,20000000,20000010&fields=userId,handle,groupId", + "raw": "{{URL}}/members/denis/stats/history?groupIds=10,20000000,20000010&fields=userId,handle,groupId", "host": [ "{{URL}}" ], "path": [ "members", - "standlove", + "denis", "stats", "history" ], @@ -4062,6 +4180,114 @@ }, "response": [] }, + { + "name": "get member private statistics with admin token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{admin_token}}", + "type": "text" + } + ], + "url": { + "raw": "{{URL}}/members/denis/stats?groupIds=10,d6bf6bb6-457c-461c-a4d6-0a6b1a87fde9", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "denis", + "stats" + ], + "query": [ + { + "key": "groupIds", + "value": "10,d6bf6bb6-457c-461c-a4d6-0a6b1a87fde9" + } + ] + } + }, + "response": [] + }, + { + "name": "get member private statistics by user himself", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{user_token}}", + "type": "text" + } + ], + "url": { + "raw": "{{URL}}/members/denis/stats?groupIds=10,d6bf6bb6-457c-461c-a4d6-0a6b1a87fde9", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "denis", + "stats" + ], + "query": [ + { + "key": "groupIds", + "value": "10,d6bf6bb6-457c-461c-a4d6-0a6b1a87fde9" + } + ] + } + }, + "response": [] + }, { "name": "get member private statistics - multiple group Ids", "event": [ @@ -4115,6 +4341,64 @@ }, "response": [] }, + { + "name": "get member private statistics - multiple group Ids with admin token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "type": "text", + "value": "application/json" + }, + { + "key": "Content-Type", + "type": "text", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{admin_token}}", + "type": "text" + } + ], + "url": { + "raw": "{{URL}}/members/denis/stats?groupIds=10,d6bf6bb6-457c-461c-a4d6-0a6b1a87fde9&fields=userId,handle,wins,groupId,challenges", + "host": [ + "{{URL}}" + ], + "path": [ + "members", + "denis", + "stats" + ], + "query": [ + { + "key": "groupIds", + "value": "10,d6bf6bb6-457c-461c-a4d6-0a6b1a87fde9" + }, + { + "key": "fields", + "value": "userId,handle,wins,groupId,challenges" + } + ] + } + }, + "response": [] + }, { "name": "get member statistics - userId,handle,wins,develop,design", "event": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index f4c383a..cad7944 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -881,7 +881,7 @@ paths: in: query required: false type: string - description: comma separated group ids + description: comma separated group ids (returns public stats if group ids is not provided) - name: fields in: query required: false @@ -954,7 +954,7 @@ paths: in: query required: false type: string - description: comma separated group ids + description: comma separated group ids (returns members public history statistics if groupIds is not provided) - name: fields in: query required: false diff --git a/package-lock.json b/package-lock.json index 5965819..e99650a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4598,8 +4598,8 @@ } }, "tc-core-library-js": { - "version": "github:appirio-tech/tc-core-library-js#df0b36c51cf80918194cbff777214b3c0cf5a151", - "from": "github:appirio-tech/tc-core-library-js#v2.6.4", + "version": "github:appirio-tech/tc-core-library-js#081138e1f5eae76171abeff34b8f326b3fb2b504", + "from": "github:appirio-tech/tc-core-library-js#v2.6.5", "requires": { "axios": "^0.19.0", "bunyan": "^1.8.12", diff --git a/src/common/eshelper.js b/src/common/eshelper.js index 12de156..3fcd9ea 100644 --- a/src/common/eshelper.js +++ b/src/common/eshelper.js @@ -3,8 +3,6 @@ */ const _ = require('lodash') const config = require('config') -const helper = require('../common/helper') -const { response } = require('express') /** * Fetch members profile form ES @@ -151,7 +149,7 @@ async function getSuggestion (query, esClient, currentUser) { * @returns {Object} total */ function getTotal (docs) { - const total = docs.hits.total + let total = docs.hits.total if (_.isObject(total)) { total = total.value || 0 } diff --git a/src/common/helper.js b/src/common/helper.js index 5546cc3..1c1cd3f 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -9,10 +9,8 @@ const AWS = require('aws-sdk') const config = require('config') 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 logger = require("./logger"); // Color schema for Ratings const RATING_COLORS = [{ @@ -44,7 +42,7 @@ const awsConfig = { } if (config.AMAZON.AWS_ACCESS_KEY_ID && config.AMAZON.AWS_SECRET_ACCESS_KEY) { awsConfig.accessKeyId = config.AMAZON.AWS_ACCESS_KEY_ID - awsConfig.secretAccessKey = config.AMAZON.AWS_SECRET_ACCESS_KEY + awsConfig.secretAccessKey = config.AMAZON.AWS_SECRET_ACCESS_KEY } AWS.config.update(awsConfig) @@ -662,41 +660,74 @@ function paginate (array, page_size, page_number) { } async function parseGroupIds (groupIds) { - const idArray = _.split(groupIds, ',') + const idArray = _.filter(_.map(_.split(groupIds, ','), id => _.trim(id)), _.size) const newIdArray = [] for (const id of idArray) { if (_.isInteger(_.toNumber(id))) { newIdArray.push(id) } else { try { - logger.info(`parseGroupIds: fetch old id from uuid ${id}`) - const { oldId } = await getGroupId(id) - if (oldId != null && oldId.trim() != '') { + const { oldId } = await getGroupId(id) + if (!_.isNil(oldId)) { newIdArray.push(oldId) - logger.info(`parseGroupIds: old id found ${oldId}`) - } else { - logger.info(`parseGroupIds: old id not found for uuid ${id}`) } } catch (err) { } } } - return newIdArray + return _.filter(_.uniq(newIdArray), _.size) } async function getGroupId (id) { const token = await getM2MToken() return new Promise(function (resolve, reject) { - logger.info(`calling groups API ${config.GROUPS_API_URL}/${id}`) request({ url: `${config.GROUPS_API_URL}/${id}`, headers: { Authorization: `Bearer ${token}` } }, function (error, response, body) { if (response.statusCode === 200) { - logger.info(`response from groups API ${response.body}`) resolve(JSON.parse(body)) } else { - logger.error(error) + reject(error) + } + } + ) + }) +} + +async function getAllowedGroupIds (currentUser, subjectUser, groupIds) { + // always load public stats if no groupId is provided + if (_.isUndefined(groupIds) || _.isEmpty(groupIds)) { + return [config.PUBLIC_GROUP_ID] + } + + // if caller is anonymous user return public group. + if (_.isUndefined(currentUser)) { + return groupIds.split(',').indexOf(config.PUBLIC_GROUP_ID) != -1 ? [config.PUBLIC_GROUP_ID] : [] + } + const groups = await parseGroupIds(groupIds) + + // admins and members themselves should be able to view all stats from all the groups. + if (canManageMember(currentUser, subjectUser)) { + return groups + } + const currentUserGroups = await getMemberGroups(currentUser.userId) + currentUserGroups.push(config.PUBLIC_GROUP_ID) + const commonGroups = _.intersection(groups, currentUserGroups) + return _.difference(commonGroups, config.PRIVATE_GROUP_IDS) +} + +async function getMemberGroups (memberId) { + const token = await getM2MToken() + return new Promise(function (resolve, reject) { + request({ url: `${config.GROUPS_API_URL}/memberGroups/${memberId}`, + headers: { + Authorization: `Bearer ${token}` + } }, + function (error, response, body) { + if (response.statusCode === 200) { + resolve(JSON.parse(body)) + } else { reject(error) } } @@ -744,5 +775,7 @@ module.exports = { paginate, parseGroupIds, getGroupId, + getAllowedGroupIds, + getMemberGroups, getM2MToken } diff --git a/src/scripts/seed-data.js b/src/scripts/seed-data.js index 4727e10..41de270 100644 --- a/src/scripts/seed-data.js +++ b/src/scripts/seed-data.js @@ -175,6 +175,59 @@ const historyStats = { updatedBy: 'test2' } +const historyStatsPrivate = { + userId: 123, + groupId: 20000001, + handle: 'denis', + handleLower: 'denis', + DEVELOP: { + subTracks: [ + { + id: 1111, + name: 'name', + history: [ + { + challengeId: 789789, + challengeName: 'test', + ratingDate: '2020-02-15T14:04:22.544Z', + newRating: 1888 + } + ] + } + ] + }, + DATA_SCIENCE: { + SRM: { + history: [ + { + challengeId: 754545, + challengeName: 'test2', + date: '2020-02-15T14:04:22.544Z', + rating: 1565, + placement: 1, + percentile: 100 + } + ] + }, + MARATHON_MATCH: { + history: [ + { + challengeId: 121212, + challengeName: 'test3', + date: '2020-02-15T14:04:22.544Z', + rating: 1232, + placement: 2, + percentile: 80 + } + ] + } + }, + updatedAt: '2020-02-08T07:38:50.088Z', + createdAt: '2020-02-09T07:38:50.088Z', + createdBy: 'test1', + updatedBy: 'test2' +} + const memberStats = { userId: 123, handle: 'denis', @@ -619,6 +672,7 @@ async function seedData () { await helper.create('MemberDistributionStats', distribution1) await helper.create('MemberDistributionStats', distribution2) await helper.create('MemberHistoryStats', historyStats) + await helper.create('MemberHistoryStatsPrivate', historyStatsPrivate) await helper.create('MemberStats', memberStats) await helper.create('MemberStatsPrivate', memberPrivateStats) await helper.create('MemberFinancial', memberFinancial) diff --git a/src/services/MemberService.js b/src/services/MemberService.js index a23f7a7..eb8d814 100644 --- a/src/services/MemberService.js +++ b/src/services/MemberService.js @@ -148,7 +148,7 @@ async function updateMember (currentUser, handle, query, data) { } } let checkEmail = await esClient.count(esCheckEmail) - if (checkEmail.count == 0) { + if (checkEmail.count === 0) { data.newEmail = data.email delete data.email data.emailVerifyToken = uuid() diff --git a/src/services/MiscService.js b/src/services/MiscService.js index 6d197d2..932ee2d 100644 --- a/src/services/MiscService.js +++ b/src/services/MiscService.js @@ -2,11 +2,9 @@ * This service provides operations of statistics. */ -const _ = require('lodash') const Joi = require('joi') const helper = require('../common/helper') const logger = require('../common/logger') -const errors = require('../common/errors') const MEMBER_FINANCIAL_FIELDS = ['userId', 'amount', 'status', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'] diff --git a/src/services/SearchService.js b/src/services/SearchService.js index 9d87d99..18f512e 100644 --- a/src/services/SearchService.js +++ b/src/services/SearchService.js @@ -18,7 +18,7 @@ const MEMBER_AUTOCOMPLETE_FIELDS = ['userId', 'handle', 'handleLower', 'firstNam 'status', 'email', 'createdAt', 'updatedAt'] var MEMBER_STATS_FIELDS = ['userId', 'handle', 'handleLower', 'maxRating', - 'challenges', 'wins', 'DEVELOP', 'DESIGN', 'DATA_SCIENCE', 'copilot'] + 'challenges', 'wins', 'DEVELOP', 'DESIGN', 'DATA_SCIENCE', 'COPILOT'] const esClient = helper.getESClient() diff --git a/src/services/StatisticsService.js b/src/services/StatisticsService.js index 9a5c2e9..adc701d 100644 --- a/src/services/StatisticsService.js +++ b/src/services/StatisticsService.js @@ -17,14 +17,12 @@ 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', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'] -var allTags - /** * Get distribution statistics. * @param {Object} query the query parameters @@ -102,33 +100,25 @@ async function getHistoryStats (currentUser, handle, query) { const fields = helper.parseCommaSeparatedString(query.fields, HISTORY_STATS_FIELDS) || HISTORY_STATS_FIELDS // get member by handle const member = await helper.getMemberByHandle(handle) - let groupIds = query.groupIds - if (!groupIds) { - // get statistics by member user id from dynamodb - let statsDb = await helper.getEntityByHashKey(handle, 'MemberHistoryStats', 'userId', member.userId, true) - if (!_.isEmpty(statsDb)) { - statsDb.originalItem().groupId = 10 - overallStat.push(statsDb.originalItem()) - } - } - if (groupIds) { - for (const groupId of groupIds.split(',')) { - let statsDb - if (groupId === '10') { - // get statistics by member user id from dynamodb - statsDb = await helper.getEntityByHashKey(handle, 'MemberHistoryStats', 'userId', member.userId, false) - 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) - } + const groupIds = await helper.getAllowedGroupIds(currentUser, member, query.groupIds) + + for (const groupId of groupIds) { + let statsDb + if (groupId === config.PUBLIC_GROUP_ID) { + // get statistics by member user id from dynamodb + statsDb = await helper.getEntityByHashKey(handle, 'MemberHistoryStats', 'userId', member.userId, false) if (!_.isEmpty(statsDb)) { - overallStat.push(statsDb.originalItem()) + statsDb.originalItem().groupId = _.toNumber(groupId) } + } 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)) { + overallStat.push(statsDb.originalItem()) } } + var result = helper.cleanUpStatistics(overallStat, fields) // remove identifiable info fields if user is not admin, not M2M and not member himself if (!helper.canManageMember(currentUser, member)) { @@ -158,72 +148,38 @@ async function getMemberStats (currentUser, handle, query, throwError) { const fields = helper.parseCommaSeparatedString(query.fields, MEMBER_STATS_FIELDS) || MEMBER_STATS_FIELDS // get member by handle const member = await helper.getMemberByHandle(handle) - let groupIds = query.groupIds - if (!groupIds) { + const groupIds = await helper.getAllowedGroupIds(currentUser, member, query.groupIds) + + for (const groupId of groupIds) { let stat try { - // get statistics by member user id from Elasticsearch + // 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 + '_10' + id: member.userId + '_' + groupId }) if (stat.hasOwnProperty('_source')) { stat = stat._source } } catch (error) { 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() + if (groupId === config.PUBLIC_GROUP_ID) { + // get statistics by member user id from dynamodb + stat = await helper.getEntityByHashKey(handle, 'MemberStats', 'userId', member.userId, false) + if (!_.isEmpty(stat)) { + stat = _.assign(stat.originalItem(), { groupId: _.toNumber(groupId) }) + } + } else { + // get statistics private by member user id from dynamodb + stat = await helper.getEntityByHashRangeKey(handle, 'MemberStatsPrivate', 'userId', member.userId, 'groupId', groupId, false) } } } - if (!_.isEmpty(stat, true)) { + if (!_.isEmpty(stat)) { stats.push(stat) } } - if (groupIds) { - 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 - logger.info(`getMemberStats: fetching stats for group ${groupId}`) - 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')) { - stat = stat._source - logger.info(`getMemberStats: stats found for groupId ${groupId}`) - } - } catch (error) { - logger.info(`getMemberStats: failed to get stats from es for groupId ${groupId}`) - 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() - } - } else { - // get statistics private by member user id from dynamodb - stat = await helper.getEntityByHashRangeKey(handle, 'MemberStatsPrivate', 'userId', member.userId, 'groupId', groupId, false) - logger.info(`getMemberStats: retrieved ${JSON.stringify(stat)} for groupId ${groupId}.`) - } - } - } - if (!_.isEmpty(stat, true)) { - stats.push(stat) - } - } - } var result = helper.cleanUpStatistics(stats, fields) // remove identifiable info fields if user is not admin, not M2M and not member himself if (!helper.canManageMember(currentUser, member)) { @@ -333,7 +289,7 @@ async function partiallyUpdateMemberSkills (currentUser, handle, data) { _.assignIn(memberEnteredSkill.skills, tempSkill) memberEnteredSkill.updatedAt = new Date().getTime() memberEnteredSkill.updatedBy = currentUser.handle || currentUser.sub - const result = await helper.update(memberEnteredSkill, {}) + await helper.update(memberEnteredSkill, {}) // get skills by member handle const memberSkill = await this.getMemberSkills(currentUser, handle, {}, true) return memberSkill