Skip to content

Commit 46e675c

Browse files
authored
Merge branch 'integration-v5-challenge-api' into issue_4552
2 parents 385fe1d + f5471db commit 46e675c

21 files changed

+282
-122
lines changed

__tests__/__snapshots__/index.js.snap

+2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ Object {
111111
"getSubtrackChallengesInit": [Function],
112112
"getUserMarathonDone": [Function],
113113
"getUserMarathonInit": [Function],
114+
"getUserResourcesDone": [Function],
115+
"getUserResourcesInit": [Function],
114116
"getUserSrmDone": [Function],
115117
"getUserSrmInit": [Function],
116118
},

__tests__/actions/auth.js

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
const MOCK_GROUPS_REQ_URL = 'https://api.topcoder-dev.com/v3/groups?memberId=12345&membershipType=user';
1+
const MOCK_GROUPS_REQ_URL = 'https://api.topcoder-dev.com/v5/groups?memberId=12345&membershipType=user';
22
const MOCK_PROFILE_REQ_URL = 'https://api.topcoder-dev.com/v3/members/username12345';
33

44
jest.mock('isomorphic-fetch', () => jest.fn(url => Promise.resolve({
5+
ok: true,
56
json: () => {
67
let content;
78
switch (url) {
8-
case MOCK_GROUPS_REQ_URL: content = ['Group1', 'Group2']; break;
9-
case MOCK_PROFILE_REQ_URL: content = { userId: 12345 }; break;
9+
case MOCK_GROUPS_REQ_URL:
10+
content = ['Group1', 'Group2'];
11+
break;
12+
case MOCK_PROFILE_REQ_URL:
13+
content = { result: { content: { userId: 12345 }, status: 200 } };
14+
break;
1015
default: throw new Error('Unexpected URL!');
1116
}
12-
return {
13-
result: { content, status: 200 },
14-
};
17+
return content;
1518
},
1619
})));
1720

config/test.js

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module.exports = {
22
API: {
33
V2: 'https://api.topcoder-dev.com/v2',
44
V3: 'https://api.topcoder-dev.com/v3',
5+
V5: 'https://api.topcoder-dev.com/v5',
56
},
67
dummyConfigKey: 'Dummy config value',
78
SECRET: {

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"lint:js": "./node_modules/.bin/eslint --ext .js,.jsx .",
3232
"test": "npm run lint && npm run jest"
3333
},
34-
"version": "1000.19.22",
34+
"version": "1000.19.33",
3535
"dependencies": {
3636
"auth0-js": "^6.8.4",
3737
"config": "^3.2.0",

src/actions/auth.js

+7-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { createActions } from 'redux-actions';
77
import { decodeToken } from 'tc-accounts';
8-
import { getApi } from '../services/api';
8+
import { getApiV3, getApiV5 } from '../services/api';
99

1010
/**
1111
* @static
@@ -16,12 +16,14 @@ import { getApi } from '../services/api';
1616
function loadProfileDone(userTokenV3) {
1717
if (!userTokenV3) return Promise.resolve(null);
1818
const user = decodeToken(userTokenV3);
19-
const api = getApi('V3', userTokenV3);
19+
const apiV3 = getApiV3(userTokenV3);
20+
const apiV5 = getApiV5(userTokenV3);
2021
return Promise.all([
21-
api.get(`/members/${user.handle}`)
22+
apiV3.get(`/members/${user.handle}`)
2223
.then(res => res.json()).then(res => (res.result.status === 200 ? res.result.content : {})),
23-
api.get(`/groups?memberId=${user.userId}&membershipType=user`)
24-
.then(res => res.json()).then(res => (res.result.status === 200 ? res.result.content : [])),
24+
apiV5.get(`/groups?memberId=${user.userId}&membershipType=user`)
25+
.then(res => (res.ok ? res.json() : new Error(res.statusText)))
26+
.then(res => (res.message ? new Error(res.message) : res)),
2527
]).then(([profile, groups]) => ({ ...profile, groups }));
2628
}
2729

src/actions/challenge.js

+14-9
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import _ from 'lodash';
88
import { config } from 'topcoder-react-utils';
99
import { createActions } from 'redux-actions';
10+
import { decodeToken } from 'tc-accounts';
1011
import { getService as getChallengesService } from '../services/challenges';
1112
import { getService as getSubmissionService } from '../services/submissions';
1213
import { getService as getMemberService } from '../services/members';
@@ -103,16 +104,20 @@ function getSubmissionsInit(challengeId) {
103104
* @desc Creates an action that loads user's submissions to the specified
104105
* challenge.
105106
* @param {String} challengeId Challenge ID.
106-
* @param {String} tokenV2 Topcoder auth token v2.
107+
* @param {String} tokenV3 Topcoder auth token v3.
107108
* @return {Action}
108109
*/
109-
function getSubmissionsDone(challengeId, tokenV2) {
110-
return getApi('V2', tokenV2)
111-
.fetch(`/challenges/submissions/${challengeId}/mySubmissions`)
112-
.then(response => response.json())
113-
.then(response => ({
110+
function getSubmissionsDone(challengeId, tokenV3) {
111+
const user = decodeToken(tokenV3);
112+
const submissionsService = getSubmissionService(tokenV3);
113+
const filters = {
114+
challengeId,
115+
memberId: user.userId,
116+
};
117+
return submissionsService.getSubmissions(filters)
118+
.then(submissions => ({
114119
challengeId: _.toString(challengeId),
115-
submissions: response.submissions,
120+
submissions,
116121
}))
117122
.catch((error) => {
118123
const err = { challengeId: _.toString(challengeId), error };
@@ -289,13 +294,13 @@ function fetchCheckpointsDone(tokenV2, challengeId) {
289294
response.checkpointResults[index].expanded = false;
290295
});
291296
return {
292-
challengeId: Number(challengeId),
297+
challengeId: String(challengeId),
293298
checkpoints: response,
294299
};
295300
})
296301
.catch(error => ({
297302
error,
298-
challengeId: Number(challengeId),
303+
challengeId: String(challengeId),
299304
}));
300305
}
301306

src/actions/members.js

+26
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,30 @@ async function getUserMarathonDone(
357357
}));
358358
}
359359

360+
/**
361+
* @static
362+
* @desc Create an action that fetch user registered challenge's resources.
363+
* @param {String} memberId Member id.
364+
* @param {String} uuid Operation UUID.
365+
* @return {Action}
366+
*/
367+
async function getUserResourcesInit(memberId, uuid) {
368+
return { memberId, uuid };
369+
}
370+
371+
/**
372+
* @static
373+
* @desc Create an action that fetch user registered challenge's resources.
374+
* @param {String} handle Member handle.
375+
* @param {String} uuid Operation UUID.
376+
* @return {Action}
377+
*/
378+
async function getUserResourcesDone(memberId, tokenV3, uuid) {
379+
const resources = await getService(tokenV3).getUserResources(memberId);
380+
381+
return { resources, uuid };
382+
}
383+
360384
export default createActions({
361385
MEMBERS: {
362386
DROP: drop,
@@ -380,5 +404,7 @@ export default createActions({
380404
GET_USER_SRM_DONE: getUserSRMDone,
381405
GET_USER_MARATHON_INIT: getUserMarathonInit,
382406
GET_USER_MARATHON_DONE: getUserMarathonDone,
407+
GET_USER_RESOURCES_INIT: getUserResourcesInit,
408+
GET_USER_RESOURCES_DONE: getUserResourcesDone,
383409
},
384410
});

src/actions/smp.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ function deleteSubmissionInit() {}
2222
* @return {Action}
2323
*/
2424
function deleteSubmissionDone(tokenV3, submissionId) {
25-
return getApi('V3', tokenV3).delete(`/submissions/${submissionId}`)
25+
return getApi('V5', tokenV3).delete(`/submissions/${submissionId}`)
2626
.then(() => submissionId);
2727
}
2828

src/actions/terms.js

+12-12
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function getTermsInit(arg) {
3232
* @return {Action}
3333
*/
3434
function getTermsDone(entity, tokens, mockAgreed) {
35-
const service = getService(tokens.tokenV2);
35+
const service = getService(tokens.tokenV3);
3636
let termsPromise;
3737

3838
// if mockAgreed=true passed, then we create an array of 10 true which we pass to the
@@ -44,7 +44,7 @@ function getTermsDone(entity, tokens, mockAgreed) {
4444

4545
switch (entity.type) {
4646
case 'challenge': {
47-
termsPromise = service.getChallengeTerms(entity.id, mockAgreedArray);
47+
termsPromise = service.getChallengeTerms(entity.terms, mockAgreedArray);
4848
break;
4949
}
5050
case 'community': {
@@ -59,7 +59,7 @@ function getTermsDone(entity, tokens, mockAgreed) {
5959
throw new Error(`Entity type '${entity.type}' is not supported by getTermsDone.`);
6060
}
6161

62-
return termsPromise.then(res => ({ entity, terms: res.terms }));
62+
return termsPromise.then(res => ({ entity, terms: res }));
6363
}
6464

6565
/**
@@ -152,11 +152,11 @@ function getTermDetailsInit(termId) {
152152
* @static
153153
* @desc Creates an action that fetches details of the specified term.
154154
* @param {Number|String} termId
155-
* @param {String} tokenV2
155+
* @param {String} tokenV3
156156
* @return {Action}
157157
*/
158-
function getTermDetailsDone(termId, tokenV2) {
159-
const service = getService(tokenV2);
158+
function getTermDetailsDone(termId, tokenV3) {
159+
const service = getService(tokenV3);
160160
return service.getTermDetails(termId).then(details => ({ termId, details }));
161161
}
162162

@@ -175,11 +175,11 @@ function getDocuSignUrlInit(templateId) {
175175
* @desc Creates an action that generates the url of DoduSign term
176176
* @param {Number|String} templateId id of document template to sign
177177
* @param {String} returnUrl callback url after finishing singing
178-
* @param {String} tokenV2 auth token
178+
* @param {String} tokenV3 auth token
179179
* @return {Action}
180180
*/
181-
function getDocuSignUrlDone(templateId, returnUrl, tokenV2) {
182-
const service = getService(tokenV2);
181+
function getDocuSignUrlDone(templateId, returnUrl, tokenV3) {
182+
const service = getService(tokenV3);
183183
return service.getDocuSignUrl(templateId, returnUrl)
184184
.then(resp => ({ templateId, docuSignUrl: resp.recipientViewUrl }));
185185
}
@@ -198,11 +198,11 @@ function agreeTermInit(termId) {
198198
* @static
199199
* @desc Creates an action that agrees to a term.
200200
* @param {Number|String} termId id of term
201-
* @param {String} tokenV2 auth token
201+
* @param {String} tokenV3 auth token
202202
* @return {Action}
203203
*/
204-
function agreeTermDone(termId, tokenV2) {
205-
const service = getService(tokenV2);
204+
function agreeTermDone(termId, tokenV3) {
205+
const service = getService(tokenV3);
206206
return service.agreeTerm(termId).then(resp => ({ termId, success: resp.success }));
207207
}
208208

src/reducers/challenge.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ function onFetchCheckpointsDone(state, action) {
171171
loadingCheckpoints: false,
172172
};
173173
}
174-
if (state.details && state.details.id === action.payload.challengeId) {
174+
if (state.details && state.details.legacyId === action.payload.challengeId) {
175175
return {
176176
...state,
177177
checkpoints: action.payload.checkpoints,

src/reducers/members.js

+39
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,43 @@ function onGetUserMarathonDone(state, { error, payload }) {
426426
};
427427
}
428428

429+
/**
430+
* Inits the loading of user challenge resources.
431+
* @param {Object} state
432+
* @param {Object} action
433+
* @return {Object} New state.
434+
*/
435+
function onGetUserResourcesInit(state, { payload }) {
436+
const { uuid } = payload;
437+
return {
438+
...state,
439+
userResources: { resources: [], loadingUserResources: uuid },
440+
};
441+
}
442+
443+
/**
444+
* Finalizes the loading of user challenge resources.
445+
* @param {Object} state
446+
* @param {Object} action
447+
* @return {Object} New state.
448+
*/
449+
function onGetUserResourcesDone(state, { error, payload }) {
450+
if (error) {
451+
logger.error('Failed to get user resources', payload);
452+
fireErrorMessage('Failed to get user resources', '');
453+
return state;
454+
}
455+
456+
const { uuid, resources } = payload;
457+
458+
if (uuid !== state.userResources.loadingUserResources) return state;
459+
460+
return {
461+
...state,
462+
userResources: { resources, loadingUserResources: '' },
463+
};
464+
}
465+
429466
/**
430467
* Creates a new Members reducer with the specified initial state.
431468
* @param {Object} initialState Optional. Initial state.
@@ -455,6 +492,8 @@ function create(initialState = {}) {
455492
[a.getUserSrmDone]: onGetUserSRMDone,
456493
[a.getUserMarathonInit]: onGetUserMarathonInit,
457494
[a.getUserMarathonDone]: onGetUserMarathonDone,
495+
[a.getUserResourcesInit]: onGetUserResourcesInit,
496+
[a.getUserResourcesDone]: onGetUserResourcesDone,
458497
}, initialState);
459498
}
460499

src/reducers/reviewOpportunity.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ function buildRequiredTermsList(details) {
2323
// Sometimes roles such as Primary Reviewer have no directly equal
2424
// terms entry. Include the plain Reviewer terms when present as a back-up.
2525
.filter(term => term.role === 'Reviewer' || _.includes(roles, term.role))
26-
.map(term => _.pick(term, ['termsOfUseId', 'agreed', 'title'])),
27-
term => term.termsOfUseId,
26+
.map(term => _.pick(term, ['id', 'agreed', 'title'])),
27+
term => term.id,
2828
);
2929

3030
return requiredTerms || [];

src/reducers/terms.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ function onAgreeTermDone(state, action) {
181181
}
182182
if (action.payload.success) {
183183
const terms = _.cloneDeep(state.terms);
184-
const term = _.find(terms, ['termsOfUseId', action.payload.termId]);
184+
const term = _.find(terms, ['id', action.payload.termId]);
185185
term.agreed = true;
186186
const selectedTerm = _.find(terms, t => !t.agreed);
187187
return {

src/services/__mocks__/challenges.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ export function normalizeChallengeDetails(v3, v3Filtered, v3User, v2, username)
8585
// Fill missing data from v3_filtered
8686
if (v3Filtered) {
8787
const groups = {};
88-
if (v3Filtered.groupIds) {
89-
v3Filtered.groupIds.forEach((id) => {
88+
if (v3Filtered.groups) {
89+
v3Filtered.groups.forEach((id) => {
9090
groups[id] = true;
9191
});
9292
}
@@ -165,8 +165,8 @@ export function normalizeChallengeDetails(v3, v3Filtered, v3User, v2, username)
165165
export function normalizeChallenge(challenge, username) {
166166
const registrationOpen = challenge.allPhases.filter(d => d.name === 'Registration')[0].isOpen ? 'Yes' : 'No';
167167
const groups = {};
168-
if (challenge.groupIds) {
169-
challenge.groupIds.forEach((id) => {
168+
if (challenge.groups) {
169+
challenge.groups.forEach((id) => {
170170
groups[id] = true;
171171
});
172172
}

src/services/challenges.js

+12-4
Original file line numberDiff line numberDiff line change
@@ -365,13 +365,11 @@ class ChallengesService {
365365
if (memberId) {
366366
isRegistered = _.some(registrants, r => r.memberId === memberId);
367367

368-
/**
369-
* TODO: Currenlty using legacyId until submissions_api fix issue with UUID
370-
*/
371368
const subParams = {
372-
challengeId: challenge.legacyId,
369+
challengeId,
373370
perPage: 100,
374371
};
372+
375373
submissions = await this.private.submissionsService.getSubmissions(subParams);
376374

377375
if (submissions) {
@@ -532,6 +530,16 @@ class ChallengesService {
532530
};
533531
}
534532

533+
/**
534+
* Gets user resources.
535+
* @param {String} userId User id whose challenges we want to fetch.
536+
* @return {Promise} Resolves to the api response.
537+
*/
538+
async getUserResources(userId) {
539+
const res = await this.private.apiV5.get(`/resources/${userId}/challenges`);
540+
return res.json();
541+
}
542+
535543
/**
536544
* Gets marathon matches of the specified user.
537545
* @param {String} memberId User whose challenges we want to fetch.

0 commit comments

Comments
 (0)