From b72d0350302754576626f7800a822f84cd19733f Mon Sep 17 00:00:00 2001 From: Rakib Ansary Date: Fri, 20 Aug 2021 06:26:35 +0600 Subject: [PATCH] feat: add support to fetch memberGroups --- __tests__/__snapshots__/index.js.snap | 3 ++- __tests__/reducers/auth.js | 2 +- package.json | 2 +- src/actions/auth.js | 9 +++---- src/actions/groups.js | 11 ++++++++ src/reducers/auth.js | 8 +++--- src/reducers/groups.js | 7 +++++ src/services/groups.js | 39 +++++++++++++++++++++++++++ 8 files changed, 69 insertions(+), 12 deletions(-) diff --git a/__tests__/__snapshots__/index.js.snap b/__tests__/__snapshots__/index.js.snap index 6b4d5cc1..337071e0 100644 --- a/__tests__/__snapshots__/index.js.snap +++ b/__tests__/__snapshots__/index.js.snap @@ -4,7 +4,7 @@ exports[`Library interface test 1`] = ` Object { "actions": Object { "auth": Object { - "getMemberGroups": [Function], + "getAuthenticatedMemberGroups": [Function], "loadProfile": [Function], "setTcTokenV2": [Function], "setTcTokenV3": [Function], @@ -54,6 +54,7 @@ Object { "dropGroups": [Function], "getGroupsDone": [Function], "getGroupsInit": [Function], + "getMemberGroups": [Function], }, "looker": Object { "getLookerDone": [Function], diff --git a/__tests__/reducers/auth.js b/__tests__/reducers/auth.js index 072266ee..0704b56e 100644 --- a/__tests__/reducers/auth.js +++ b/__tests__/reducers/auth.js @@ -8,7 +8,7 @@ const photoURL = 'http://url'; const mockActions = { auth: { loadProfile: mockAction('LOAD_PROFILE', Promise.resolve('Profile')), - getMemberGroups: mockAction('GET_MEMBER_GROUPS', Promise.resolve(['Group'])), + getAuthenticatedMemberGroups: mockAction('GET_AUTHENTICATED_MEMBER_GROUPS', Promise.resolve(['Group'])), setTcTokenV2: mockAction('SET_TC_TOKEN_V2', 'Token V2'), setTcTokenV3: mockAction('SET_TC_TOKEN_V3', 'Token V3'), }, diff --git a/package.json b/package.json index 82093a10..c494d172 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "lint:js": "./node_modules/.bin/eslint --ext .js,.jsx .", "test": "npm run lint && npm run jest" }, - "version": "1000.27.15", + "version": "1000.27.16", "dependencies": { "auth0-js": "^6.8.4", "config": "^3.2.0", diff --git a/src/actions/auth.js b/src/actions/auth.js index 86177e9f..3cec762a 100644 --- a/src/actions/auth.js +++ b/src/actions/auth.js @@ -7,6 +7,7 @@ import { createActions } from 'redux-actions'; import { decodeToken } from '@topcoder-platform/tc-auth-lib'; import { getApiV3, getApiV5 } from '../services/api'; import { setErrorIcon, ERROR_ICON_TYPES } from '../utils/errors'; +import { getService } from '../services/groups'; /** * Helper method that checks for HTTP error response v5 and throws Error in this case. @@ -77,12 +78,10 @@ function setTcTokenV3(tokenV3) { * @param {*} tokenV3 the member's token * @returns */ -async function getMemberGroups(tokenV3) { +async function getAuthenticatedMemberGroups(tokenV3) { if (!tokenV3) return Promise.resolve([]); const user = decodeToken(tokenV3); - const apiV5 = getApiV5(tokenV3); - const res = await apiV5.get(`/groups/memberGroups/${user.userId}`).then(checkErrorV5).then(r => r.result || []); - return res; + return getService(tokenV3).getMemberGroups(user.userId); } export default createActions({ @@ -90,6 +89,6 @@ export default createActions({ LOAD_PROFILE: loadProfileDone, SET_TC_TOKEN_V2: setTcTokenV2, SET_TC_TOKEN_V3: setTcTokenV3, - GET_MEMBER_GROUPS: getMemberGroups, + GET_AUTHENTICATED_MEMBER_GROUPS: getAuthenticatedMemberGroups, }, }); diff --git a/src/actions/groups.js b/src/actions/groups.js index 8bdb4f9c..c42f1e0b 100644 --- a/src/actions/groups.js +++ b/src/actions/groups.js @@ -42,10 +42,21 @@ function getGroupsDone(groupIds, tokenV3) { return getService(tokenV3).getGroupMap(groupIds); } +/** + * Get groups that a member belong to + * @param {*} userId the member's userId + * @param {*} tokenV3 the logged in users token + * @returns + */ +function getMemberGroups(userId, tokenV3) { + return getService(tokenV3).getMemberGroups(userId); +} + export default createActions({ GROUPS: { DROP_GROUPS: dropGroups, GET_GROUPS_INIT: getGroupsInit, GET_GROUPS_DONE: getGroupsDone, + GET_MEMBER_GROUPS: getMemberGroups, }, }); diff --git a/src/reducers/auth.js b/src/reducers/auth.js index 55e3976e..b20e7851 100644 --- a/src/reducers/auth.js +++ b/src/reducers/auth.js @@ -18,7 +18,7 @@ import { redux } from 'topcoder-react-utils'; import actions from '../actions/auth'; import profileActions from '../actions/profile'; -function onGetMemberGroups(state, action) { +function onGetAuthenticatedMemberGroups(state, action) { return { ...state, memberGroups: action.payload }; } @@ -43,7 +43,7 @@ function onProfileLoaded(state, action) { */ function create(initialState) { return redux.handleActions({ - [actions.auth.getMemberGroups]: onGetMemberGroups, + [actions.auth.getAuthenticatedMemberGroups]: onGetAuthenticatedMemberGroups, [actions.auth.loadProfile]: onProfileLoaded, [actions.auth.setTcTokenV2]: (state, action) => ({ ...state, @@ -134,10 +134,10 @@ export async function factory(options = {}) { if (state.tokenV3) { state.user = decodeToken(state.tokenV3); let a = actions.auth.loadProfile(state.tokenV3); - let g = actions.auth.getMemberGroups(state.tokenV3); + let g = actions.auth.getAuthenticatedMemberGroups(state.tokenV3); a = await redux.resolveAction(a); g = await redux.resolveAction(g); - return create(onGetMemberGroups(onProfileLoaded(state, a), g)); + return create(onGetAuthenticatedMemberGroups(onProfileLoaded(state, a), g)); } return create(state); } diff --git a/src/reducers/groups.js b/src/reducers/groups.js index 4f2ddece..05c1f517 100644 --- a/src/reducers/groups.js +++ b/src/reducers/groups.js @@ -23,6 +23,7 @@ import _ from 'lodash'; import { handleActions } from 'redux-actions'; import actions from '../actions/groups'; +// import { getApiResponsePayload } from '../utils/tc'; /** * Private. Given two user group maps, it adds to "dst" the root group from @@ -84,6 +85,10 @@ function onGetGroupsDone(state, action) { return { ...state, groups, loading }; } +function onGetMemberGroups(state, action) { + return { ...state, memberGroups: action.payload }; +} + /** * Creates a new Groups reducer with the specified initial state. @@ -96,9 +101,11 @@ function create(initialState) { [a.dropGroups]: onDropGroups, [a.getGroupsInit]: onGetGroupsInit, [a.getGroupsDone]: onGetGroupsDone, + [a.getMemberGroups]: onGetMemberGroups, }, _.defaults(initialState ? _.clone(initialState) : {}, { groups: {}, loading: {}, + memberGroups: [], })); } diff --git a/src/services/groups.js b/src/services/groups.js index a3b42a80..2ba8b986 100644 --- a/src/services/groups.js +++ b/src/services/groups.js @@ -20,6 +20,7 @@ import _ from 'lodash'; import { config } from 'topcoder-react-utils'; import logger from '../utils/logger'; import { getApi } from './api'; +import { setErrorIcon, ERROR_ICON_TYPES } from '../utils/errors'; /* The value of USER_GROUP_MAXAGE constant converted to [ms]. */ const USER_GROUP_MAXAGE = config.USER_GROUP_MAXAGE * 1000; @@ -142,6 +143,29 @@ function handleApiResponse(response) { return response.json(); } +/** + * Helper method that checks for HTTP error response v5 and throws Error in this case. + * @param {Object} res HTTP response object + * @return {Object} API JSON response object + * @private + */ +async function checkErrorV5(res) { + if (!res.ok) { + if (res.status === 403) { + setErrorIcon(ERROR_ICON_TYPES.API, 'Auth0', res.statusText); + } + throw new Error(res.statusText); + } + const jsonRes = (await res.json()); + if (jsonRes.message) { + throw new Error(res.message); + } + return { + result: jsonRes, + headers: res.headers, + }; +} + /** * Private. Merges given user group (possibly a tree of user groups) into * groups map. This function intended only for internal use inside this module, @@ -354,6 +378,21 @@ class GroupService { getTokenV3() { return this.private.tokenV3; } + + /** + * Gets the corresponding user's groups information + * @param {*} userId the userId + * @returns + */ + async getMemberGroups(userId) { + const url = `/groups/memberGroups/${userId}`; + const response = await this.private.api.get(url) + .then(res => checkErrorV5(res)) + .then(r => r.result || []) + .catch(() => []); + + return response; + } } let lastInstance = null;