Skip to content

Commit 364781f

Browse files
authored
Merge pull request #179 from topcoder-platform/issue-4408
Issue 4408 : Fix registrants and submissions tabs in challenge details
2 parents 63d7e1e + 48e4321 commit 364781f

File tree

3 files changed

+91
-26
lines changed

3 files changed

+91
-26
lines changed

__tests__/__snapshots__/index.js.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ Object {
291291
"getApiV4": [Function],
292292
"getApiV5": [Function],
293293
"getTcM2mToken": [Function],
294+
"proxyApi": [Function],
294295
},
295296
"billing": Object {
296297
"default": [Function],

src/services/api.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,25 @@ export async function getTcM2mToken() {
290290
const token = await m2m.getMachineToken(TC_M2M.CLIENT_ID, TC_M2M.CLIENT_SECRET);
291291
return token;
292292
}
293+
294+
/**
295+
* Call API via proxy
296+
*
297+
* @param {String} url to API endpoint
298+
*/
299+
export async function proxyApi(url) {
300+
let base = '';
301+
if (isomorphy.isServerSide()) {
302+
base = `http://localhost:${process.env.PORT || 80}`;
303+
}
304+
const proxyUrl = `${base}/community-app-assets/api/proxy-get?url=${
305+
encodeURIComponent(url)
306+
}`;
307+
let res = await fetch(proxyUrl, {
308+
headers: { Authorization: `ApiKey ${config.SERVER_API_KEY}` },
309+
});
310+
if (!res.ok) throw new Error(res.statusText);
311+
res = (await res.json());
312+
if (res.message) throw new Error(res.message);
313+
return res;
314+
}

src/services/challenges.js

Lines changed: 68 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ import _ from 'lodash';
88
import moment from 'moment';
99
import qs from 'qs';
1010
import { decodeToken } from 'tc-accounts';
11+
import { config } from 'topcoder-react-utils';
1112
import logger from '../utils/logger';
1213
import { setErrorIcon, ERROR_ICON_TYPES } from '../utils/errors';
1314
import { COMPETITION_TRACKS, getApiResponsePayload } from '../utils/tc';
14-
import { getApi } from './api';
15+
import { getTcM2mToken, getApi, proxyApi } from './api';
1516
import { getService as getMembersService } from './members';
1617

1718
export const ORDER_BY = {
@@ -147,7 +148,7 @@ class ChallengesService {
147148
const url = `${endpoint}?${qs.stringify(query)}`;
148149
const res = await this.private.apiV5.get(url).then(checkErrorV5);
149150
let myChallenges;
150-
if (typeof this.private.tokenV3 !== 'undefined') {
151+
if (this.private.tokenV3) {
151152
const { userId } = decodeToken(this.private.tokenV3);
152153
myChallenges = await this.private.apiV5.get(`/resources/${userId}/challenges`)
153154
.then(checkErrorV5).then(userChallenges => userChallenges);
@@ -198,6 +199,8 @@ class ChallengesService {
198199
apiV5: getApi('V5', tokenV3),
199200
apiV2: getApi('V2', tokenV2),
200201
apiV3: getApi('V3', tokenV3),
202+
getTcM2mToken,
203+
proxyApi,
201204
getChallenges,
202205
getMemberChallenges,
203206
tokenV2,
@@ -318,34 +321,52 @@ class ChallengesService {
318321

319322
/**
320323
* Gets challenge details from Topcoder API.
321-
* NOTE: This function also uses API v2 and other endpoints for now, due
324+
* NOTE: This function also uses other endpoints for now, due
322325
* to some information is missing or
323326
* incorrect in the main endpoint. This may change in the future.
324327
* @param {Number|String} challengeId
325328
* @return {Promise} Resolves to the challenge object.
326329
*/
327330
async getChallengeDetails(challengeId) {
331+
let challenge = {};
328332
let isLegacyChallenge = false;
329-
const filters = {};
330333
// condition based on ROUTE used for Review Opportunities, change if needed
331-
if (challengeId.length >= 5 && challengeId.length <= 8) {
334+
if (/^[\d]{5,8}$/.test(challengeId)) {
332335
isLegacyChallenge = true;
333-
filters.legacyId = challengeId;
336+
challenge = await this.private.getChallenges('/challenges/', { legacyId: challengeId })
337+
.then(res => res.challenges[0]);
334338
} else {
335-
filters.id = challengeId;
339+
challenge = await this.private.getChallenges(`/challenges/${challengeId}`)
340+
.then(res => res.challenges);
336341
}
337-
const challengeFiltered = await this.private.getChallenges('/challenges/', filters)
338-
.then(res => res.challenges[0]);
339-
340-
if (challengeFiltered) {
341-
challengeFiltered.isLegacyChallenge = isLegacyChallenge;
342-
challengeFiltered.events = _.map(challengeFiltered.events, e => ({
343-
eventName: e.key,
344-
eventId: e.id,
345-
description: e.name,
346-
}));
347-
}
348-
return challengeFiltered;
342+
343+
/**
344+
* TODO: Currenlty using legacyId until submissions_api fix issue with UUID
345+
*/
346+
const submissions = await this.getChallengeSubmissions(challenge.legacyId);
347+
challenge.submissions = submissions;
348+
349+
const registrants = await this.getChallengeRegistrants(challenge.id);
350+
// Add submission date to registrants
351+
registrants.forEach((r, i) => {
352+
const submission = submissions.find(s => s.memberId === Number(r.memberId));
353+
if (submission) {
354+
registrants[i].submissionDate = submission.created;
355+
}
356+
});
357+
challenge.registrants = registrants;
358+
359+
challenge.isLegacyChallenge = isLegacyChallenge;
360+
361+
challenge.events = _.map(challenge.events, e => ({
362+
eventName: e.key,
363+
eventId: e.id,
364+
description: e.name,
365+
}));
366+
367+
challenge.fetchedWithAuth = Boolean(this.private.apiV5.private.token);
368+
369+
return challenge;
349370
}
350371

351372
/**
@@ -354,11 +375,31 @@ class ChallengesService {
354375
* @return {Promise} Resolves to the challenge registrants array.
355376
*/
356377
async getChallengeRegistrants(challengeId) {
357-
const registrants = await this.private.apiV5.get(`/resources/challengeId=${challengeId}`)
358-
.then(checkError).then(res => res);
378+
const roleId = await this.getResourceRoleId('Submitter');
379+
const params = {
380+
challengeId,
381+
roleId,
382+
};
383+
const url = `${config.API.V5}/resources?${qs.stringify(params)}`;
384+
const registrants = await this.private.proxyApi(url);
359385
return registrants || [];
360386
}
361387

388+
/**
389+
* Gets challenge submissions from Topcoder API.
390+
* @param {Number|String} challengeId
391+
* @return {Promise} Resolves to the challenge registrants array.
392+
*/
393+
async getChallengeSubmissions(challengeId) {
394+
const params = {
395+
challengeId,
396+
perPage: 100,
397+
};
398+
const url = `${config.API.V5}/submissions?${qs.stringify(params)}`;
399+
const submissions = await this.private.proxyApi(url);
400+
return submissions || [];
401+
}
402+
362403
/**
363404
* Gets possible challenge types.
364405
* @return {Promise} Resolves to the array of subtrack names.
@@ -523,14 +564,15 @@ class ChallengesService {
523564
name: roleName,
524565
isActive: true,
525566
};
526-
const roles = await this.private.apiV5.get(`/resource-roles?${qs.stringify(params)}`)
527-
.then(checkErrorV5).then(res => res);
528567

529-
if (_.isEmpty(roles.result)) {
568+
const url = `${config.API.V5}/resource-roles?${qs.stringify(params)}`;
569+
const roles = await this.private.proxyApi(url);
570+
571+
if (_.isEmpty(roles)) {
530572
throw new Error('Resource Role not found!');
531573
}
532574

533-
return roles.result[0].id;
575+
return roles[0].id;
534576
}
535577

536578
/**
@@ -564,7 +606,7 @@ class ChallengesService {
564606
memberHandle: user.handle,
565607
roleId,
566608
};
567-
const res = await this.private.apiV5.delete('/resources', params);
609+
const res = await this.private.apiV5.delete('/resources', JSON.stringify(params));
568610
if (!res.ok) throw new Error(res.statusText);
569611
return res.json();
570612
}

0 commit comments

Comments
 (0)