From e04f229fc34847e1b18e8c8bde8468c0a170cab6 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 21 Dec 2021 10:51:40 +0000 Subject: [PATCH] update profile and skills to v5 --- __tests__/__snapshots__/index.js.snap | 2 + .../actions/__snapshots__/profile.js.snap | 2 + __tests__/actions/auth.js | 4 +- src/actions/auth.js | 8 ++-- src/actions/profile.js | 21 +++++++++ src/reducers/auth.js | 15 ++++++ src/reducers/profile.js | 45 ++++++++++++++++++ src/services/members.js | 46 +++++++++++-------- 8 files changed, 117 insertions(+), 26 deletions(-) diff --git a/__tests__/__snapshots__/index.js.snap b/__tests__/__snapshots__/index.js.snap index 283e8d01..351b4e3c 100644 --- a/__tests__/__snapshots__/index.js.snap +++ b/__tests__/__snapshots__/index.js.snap @@ -174,7 +174,9 @@ Object { "updatePasswordDone": [Function], "updatePasswordInit": [Function], "updateProfileDone": [Function], + "updateProfileDoneV5": [Function], "updateProfileInit": [Function], + "updateProfileInitV5": [Function], "uploadPhotoDone": [Function], "uploadPhotoInit": [Function], "verifyMemberNewEmailDone": [Function], diff --git a/__tests__/actions/__snapshots__/profile.js.snap b/__tests__/actions/__snapshots__/profile.js.snap index b784f238..0649581b 100644 --- a/__tests__/actions/__snapshots__/profile.js.snap +++ b/__tests__/actions/__snapshots__/profile.js.snap @@ -44,7 +44,9 @@ Object { "updatePasswordDone": [Function], "updatePasswordInit": [Function], "updateProfileDone": [Function], + "updateProfileDoneV5": [Function], "updateProfileInit": [Function], + "updateProfileInitV5": [Function], "uploadPhotoDone": [Function], "uploadPhotoInit": [Function], "verifyMemberNewEmailDone": [Function], diff --git a/__tests__/actions/auth.js b/__tests__/actions/auth.js index a36ef7e3..19cc26d5 100644 --- a/__tests__/actions/auth.js +++ b/__tests__/actions/auth.js @@ -1,5 +1,5 @@ const MOCK_GROUPS_REQ_URL = 'https://api.topcoder-dev.com/v5/groups?memberId=12345&membershipType=user'; -const MOCK_PROFILE_REQ_URL = 'https://api.topcoder-dev.com/v3/members/username12345'; +const MOCK_PROFILE_REQ_URL = 'https://api.topcoder-dev.com/v5/members/username12345'; jest.mock('isomorphic-fetch', () => jest.fn(url => Promise.resolve({ ok: true, @@ -10,7 +10,7 @@ jest.mock('isomorphic-fetch', () => jest.fn(url => Promise.resolve({ content = ['Group1', 'Group2']; break; case MOCK_PROFILE_REQ_URL: - content = { result: { content: { userId: 12345 }, status: 200 } }; + content = Promise.resolve({ userId: 12345 }); break; default: throw new Error('Unexpected URL!'); } diff --git a/src/actions/auth.js b/src/actions/auth.js index 3cec762a..c8a760b0 100644 --- a/src/actions/auth.js +++ b/src/actions/auth.js @@ -5,9 +5,10 @@ import { createActions } from 'redux-actions'; import { decodeToken } from '@topcoder-platform/tc-auth-lib'; -import { getApiV3, getApiV5 } from '../services/api'; +import { getApiV5 } from '../services/api'; import { setErrorIcon, ERROR_ICON_TYPES } from '../utils/errors'; import { getService } from '../services/groups'; +import { handleApiResponse } from '../utils/tc'; /** * Helper method that checks for HTTP error response v5 and throws Error in this case. @@ -41,11 +42,10 @@ async function checkErrorV5(res) { function loadProfileDone(userTokenV3) { if (!userTokenV3) return Promise.resolve(null); const user = decodeToken(userTokenV3); - const apiV3 = getApiV3(userTokenV3); const apiV5 = getApiV5(userTokenV3); return Promise.all([ - apiV3.get(`/members/${user.handle}`) - .then(res => res.json()).then(res => (res.result.status === 200 ? res.result.content : {})), + apiV5.get(`/members/${user.handle}`) + .then(handleApiResponse), apiV5.get(`/groups?memberId=${user.userId}&membershipType=user`) .then(checkErrorV5).then(res => res.result || []), ]).then(([profile, groups]) => ({ ...profile, groups })); diff --git a/src/actions/profile.js b/src/actions/profile.js index 96a9ef5e..e9368d0a 100644 --- a/src/actions/profile.js +++ b/src/actions/profile.js @@ -246,6 +246,25 @@ function updateProfileDone(profile, tokenV3) { return service.updateMemberProfile(profile); } +/** + * @static + * @desc Creates an action that signals beginning of updating user's profile. + * @return {Action} + */ +function updateProfileInitV5() {} + +/** + * @static + * @desc Creates an action that updates user's profile. + * @param {String} profile Topcoder user profile. + * @param {String} tokenV5 Topcoder auth token v5. + * @return {Action} + */ +function updateProfileDoneV5(profile, handle, tokenV3) { + const service = getMembersService(tokenV3); + return service.updateMemberProfileV5(profile, handle); +} + /** * @static * @desc Creates an action that signals beginning of adding user's skill. @@ -483,6 +502,8 @@ export default createActions({ DELETE_PHOTO_DONE: updateProfileDone, UPDATE_PROFILE_INIT: updateProfileInit, UPDATE_PROFILE_DONE: updateProfileDone, + UPDATE_PROFILE_INIT_V5: updateProfileInitV5, + UPDATE_PROFILE_DONE_V5: updateProfileDoneV5, ADD_SKILL_INIT: addSkillInit, ADD_SKILL_DONE: addSkillDone, HIDE_SKILL_INIT: hideSkillInit, diff --git a/src/reducers/auth.js b/src/reducers/auth.js index b20e7851..a957d6d8 100644 --- a/src/reducers/auth.js +++ b/src/reducers/auth.js @@ -106,6 +106,21 @@ function create(initialState) { }, }; }, + [profileActions.profile.updateProfileDoneV5]: (state, { payload, error }) => { + if (error) { + return state; + } + if (!state.profile || state.profile.handle !== payload.handle) { + return state; + } + return { + ...state, + profile: { + ...state.profile, + ...payload, + }, + }; + }, }, _.defaults(initialState, { authenticating: true, profile: null, diff --git a/src/reducers/profile.js b/src/reducers/profile.js index 8ca03fdf..e060f7d4 100644 --- a/src/reducers/profile.js +++ b/src/reducers/profile.js @@ -266,6 +266,49 @@ function onUpdateProfileDone(state, { payload, error }) { }; } +/** + * Handles PROFILE/UPDATE_PROFILE_DONE_V5 action. + * @param {Object} state + * @param {Object} action Payload will be JSON from api call + * @return {Object} New state + */ +function onUpdateProfileDoneV5(state, { payload, error }) { + const newState = { ...state, updatingProfile: false }; + + if (payload.isEmailConflict) { + return { + ...newState, + isEmailConflict: true, + updateProfileSuccess: false, + }; + } + + if (error) { + logger.error('Failed to update user profile', payload); + fireErrorMessage('ERROR: Failed to update user profile!'); + return { + ...newState, + updateProfileSuccess: false, + }; + } + + if (!newState.info || newState.info.handle !== payload.handle) { + return { + ...newState, + updateProfileSuccess: true, + }; + } + + return { + ...newState, + info: { + ...newState.info, + ...payload, + }, + updateProfileSuccess: true, + }; +} + /** * Handles PROFILE/ADD_SKILL_DONE action. * @param {Object} state @@ -530,6 +573,8 @@ function create(initialState) { [a.deletePhotoDone]: onDeletePhotoDone, [a.updateProfileInit]: state => ({ ...state, updatingProfile: true }), [a.updateProfileDone]: onUpdateProfileDone, + [a.updateProfileInitV5]: state => ({ ...state, updatingProfile: true }), + [a.updateProfileDoneV5]: onUpdateProfileDoneV5, [a.addSkillInit]: state => ({ ...state, addingSkill: true }), [a.addSkillDone]: onAddSkillDone, [a.hideSkillInit]: state => ({ ...state, hidingSkill: true }), diff --git a/src/services/members.js b/src/services/members.js index 61f3e9ab..5022b362 100644 --- a/src/services/members.js +++ b/src/services/members.js @@ -45,8 +45,8 @@ class MembersService { * @return {Promise} Resolves to the data object. */ async getMemberInfo(handle) { - const res = await this.private.api.get(`/members/${handle}`); - return getApiResponsePayload(res); + const res = await this.private.apiV5.get(`/members/${handle}`); + return handleApiResponse(res); } /** @@ -75,8 +75,8 @@ class MembersService { * @return {Promise} Resolves to the stats object. */ async getSkills(handle) { - const res = await this.private.api.get(`/members/${handle}/skills`); - return getApiResponsePayload(res); + const res = await this.private.apiV5.get(`/members/${handle}/skills`); + return handleApiResponse(res); } /** @@ -188,16 +188,12 @@ class MembersService { */ async addSkill(handle, skillTagId) { const body = { - param: { - skills: { - [skillTagId]: { - hidden: false, - }, - }, + [skillTagId]: { + hidden: false, }, }; - const res = await this.private.api.patchJson(`/members/${handle}/skills`, body); - return getApiResponsePayload(res); + const res = await this.private.apiV5.patchJson(`/members/${handle}/skills`, body); + return handleApiResponse(res); } /** @@ -208,19 +204,15 @@ class MembersService { */ async hideSkill(handle, skillTagId) { const body = { - param: { - skills: { - [skillTagId]: { - hidden: true, - }, - }, + [skillTagId]: { + hidden: true, }, }; - const res = await this.private.api.fetch(`/members/${handle}/skills`, { + const res = await this.private.apiV5.fetch(`/members/${handle}/skills`, { body: JSON.stringify(body), method: 'PATCH', }); - return getApiResponsePayload(res); + return handleApiResponse(res); } /** @@ -237,6 +229,20 @@ class MembersService { return getApiResponsePayload(res); } + /** + * Updates member profile. + * @param {Object} profile The profile to update. + * @return {Promise} Resolves to the api response content + */ + async updateMemberProfileV5(profile, handle) { + const url = profile.verifyUrl ? `/members/${handle}?verifyUrl=${profile.verifyUrl}` : `/members/${handle}`; + const res = await this.private.apiV5.putJson(url, profile.verifyUrl ? _.omit(profile, ['verifyUrl']) : profile); + if (profile.verifyUrl && res.status === 409) { + return Promise.resolve(Object.assign({}, profile, { isEmailConflict: true })); + } + return handleApiResponse(res); + } + /** * Updates member photo. * @param {String} userHandle The user handle