diff --git a/__tests__/__snapshots__/index.js.snap b/__tests__/__snapshots__/index.js.snap
index 337071e0..283e8d01 100644
--- a/__tests__/__snapshots__/index.js.snap
+++ b/__tests__/__snapshots__/index.js.snap
@@ -12,6 +12,8 @@ Object {
"challenge": Object {
"dropCheckpoints": [Function],
"dropResults": [Function],
+ "fetchChallengeStatisticsDone": [Function],
+ "fetchChallengeStatisticsInit": [Function],
"fetchCheckpointsDone": [Function],
"fetchCheckpointsInit": [Function],
"getActiveChallengesCountDone": [Function],
diff --git a/__tests__/actions/profile.js b/__tests__/actions/profile.js
index 4ea366a3..b4d1848b 100644
--- a/__tests__/actions/profile.js
+++ b/__tests__/actions/profile.js
@@ -18,6 +18,8 @@ const linkedAccounts = [{
// Mock services
const mockMembersService = {
+ getPresignedUrl: jest.fn().mockReturnValue(Promise.resolve()),
+ uploadFileToS3: jest.fn().mockReturnValue(Promise.resolve()),
updateMemberPhoto: jest.fn().mockReturnValue(Promise.resolve('url-of-photo')),
updateMemberProfile: jest.fn().mockReturnValue(Promise.resolve(profile)),
addSkill: jest.fn().mockReturnValue(Promise.resolve({ skills: [skill] })),
@@ -45,6 +47,8 @@ test('Module exports', () => expect(actions).toMatchSnapshot());
test('profile.uploadPhotoDone', async () => {
const actionResult = await redux.resolveAction(actions.profile.uploadPhotoDone(handle, tokenV3));
expect(actionResult).toMatchSnapshot();
+ expect(mockMembersService.getPresignedUrl).toBeCalled();
+ expect(mockMembersService.uploadFileToS3).toBeCalled();
expect(mockMembersService.updateMemberPhoto).toBeCalled();
});
diff --git a/__tests__/reducers/__snapshots__/challenge.js.snap b/__tests__/reducers/__snapshots__/challenge.js.snap
index 009e5691..e744b7c2 100644
--- a/__tests__/reducers/__snapshots__/challenge.js.snap
+++ b/__tests__/reducers/__snapshots__/challenge.js.snap
@@ -15,6 +15,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -40,6 +41,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -65,6 +67,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -87,6 +90,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -118,6 +122,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -151,6 +156,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -182,6 +188,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -211,6 +218,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -243,6 +251,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -276,6 +285,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -309,6 +319,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -342,6 +353,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -375,6 +387,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -406,6 +419,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -439,6 +453,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -470,6 +485,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -499,6 +515,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -531,6 +548,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -552,6 +570,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -577,6 +596,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -602,6 +622,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -624,6 +645,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -655,6 +677,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -688,6 +711,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -719,6 +743,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -748,6 +773,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -780,6 +806,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -801,6 +828,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -826,6 +854,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -851,6 +880,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -873,6 +903,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -904,6 +935,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -937,6 +969,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -968,6 +1001,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -997,6 +1031,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
@@ -1029,6 +1064,7 @@ Object {
"registering": false,
"results": null,
"resultsLoadedForChallengeId": "",
+ "statisticsData": null,
"submissionInformation": null,
"unregistering": false,
"updatingChallengeUuid": "",
diff --git a/docs/services.members.md b/docs/services.members.md
index 8fe477f2..856b90ce 100644
--- a/docs/services.members.md
+++ b/docs/services.members.md
@@ -25,7 +25,9 @@ members via API V3.
* [.addSkill(handle, skillTagId)](#module_services.members..MembersService+addSkill) ⇒ Promise
* [.hideSkill(handle, skillTagId)](#module_services.members..MembersService+hideSkill) ⇒ Promise
* [.updateMemberProfile(profile)](#module_services.members..MembersService+updateMemberProfile) ⇒ Promise
- * [.updateMemberPhoto(userHandle, file)](#module_services.members..MembersService+updateMemberPhoto) ⇒ Promise
+ * [.getPresignedUrl(userHandle, file)](#module_services.members..MembersService+getPresignedUrl) ⇒ Promise
+ * [.updateMemberPhoto(S3Response)](#module_services.members..MembersService+updateMemberPhoto) ⇒ Promise
+ * [.uploadFileToS3(presignedUrlResponse)](#module_services.members..MembersService+uploadFileToS3) ⇒ Promise
* [.verifyMemberNewEmail(handle, emailVerifyToken)](#module_services.members..MembersService+verifyMemberNewEmail) ⇒ Promise
@@ -63,7 +65,9 @@ Service class.
* [.addSkill(handle, skillTagId)](#module_services.members..MembersService+addSkill) ⇒ Promise
* [.hideSkill(handle, skillTagId)](#module_services.members..MembersService+hideSkill) ⇒ Promise
* [.updateMemberProfile(profile)](#module_services.members..MembersService+updateMemberProfile) ⇒ Promise
- * [.updateMemberPhoto(userHandle, file)](#module_services.members..MembersService+updateMemberPhoto) ⇒ Promise
+ * [.getPresignedUrl(userHandle, file)](#module_services.members..MembersService+getPresignedUrl) ⇒ Promise
+ * [.updateMemberPhoto(S3Response)](#module_services.members..MembersService+updateMemberPhoto) ⇒ Promise
+ * [.uploadFileToS3(presignedUrlResponse)](#module_services.members..MembersService+uploadFileToS3) ⇒ Promise
* [.verifyMemberNewEmail(handle, emailVerifyToken)](#module_services.members..MembersService+verifyMemberNewEmail) ⇒ Promise
@@ -252,10 +256,10 @@ Updates member profile.
| --- | --- | --- |
| profile | Object
| The profile to update. |
-
+
-#### membersService.updateMemberPhoto(userHandle, file) ⇒ Promise
-Uploads and updates member photo.
+#### membersService.getPresignedUrl(userHandle, file) ⇒ Promise
+Gets presigned url for member photo file.
**Kind**: instance method of [MembersService
](#module_services.members..MembersService)
**Returns**: Promise
- Resolves to the api response content
@@ -263,7 +267,31 @@ Uploads and updates member photo.
| Param | Type | Description |
| --- | --- | --- |
| userHandle | String
| The user handle |
-| file | File
| The file to be uploaded |
+| file | File
| The file to get its presigned url |
+
+
+
+#### membersService.updateMemberPhoto(S3Response) ⇒ Promise
+Updates member photo.
+
+**Kind**: instance method of [MembersService
](#module_services.members..MembersService)
+**Returns**: Promise
- Resolves to the api response content
+
+| Param | Type | Description |
+| --- | --- | --- |
+| S3Response | Object
| The response from uploadFileToS3() function. |
+
+
+
+#### membersService.uploadFileToS3(presignedUrlResponse) ⇒ Promise
+Uploads file to S3.
+
+**Kind**: instance method of [MembersService
](#module_services.members..MembersService)
+**Returns**: Promise
- Resolves to the api response content
+
+| Param | Type | Description |
+| --- | --- | --- |
+| presignedUrlResponse | Object
| The presigned url response from getPresignedUrl() function. |
diff --git a/package.json b/package.json
index 7ab753e2..442f898e 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": "1.2.3",
+ "version": "1.2.4",
"dependencies": {
"auth0-js": "^6.8.4",
"config": "^3.2.0",
diff --git a/src/actions/challenge.js b/src/actions/challenge.js
index 86655807..9a1c048f 100644
--- a/src/actions/challenge.js
+++ b/src/actions/challenge.js
@@ -392,6 +392,25 @@ function getSubmissionInformationDone(challengeId, submissionId, tokenV3) {
});
}
+/**
+ * @static
+ * @desc Creates an action that signals beginning of fetching challenge statistics
+ * @return {Action}
+ */
+function fetchChallengeStatisticsInit() {}
+
+/**
+ * @static
+ * @desc Creates an action that gets challenge statistics from the backend.
+ * @param {String} challengeId The challenge id
+ * @param {String} tokenV3 Topcoder auth token v3.
+ * @return {Action}
+ */
+function fetchChallengeStatisticsDone(challengeId, tokenV3) {
+ const challengeService = getChallengesService(tokenV3);
+ return challengeService.getChallengeStatistics(challengeId);
+}
+
export default createActions({
CHALLENGE: {
DROP_CHECKPOINTS: dropCheckpoints,
@@ -417,5 +436,7 @@ export default createActions({
GET_MM_SUBMISSIONS_DONE: getMMSubmissionsDone,
GET_SUBMISSION_INFORMATION_INIT: getSubmissionInformationInit,
GET_SUBMISSION_INFORMATION_DONE: getSubmissionInformationDone,
+ FETCH_CHALLENGE_STATISTICS_INIT: fetchChallengeStatisticsInit,
+ FETCH_CHALLENGE_STATISTICS_DONE: fetchChallengeStatisticsDone,
},
});
diff --git a/src/actions/profile.js b/src/actions/profile.js
index 96a9ef5e..bcd668cf 100644
--- a/src/actions/profile.js
+++ b/src/actions/profile.js
@@ -216,7 +216,9 @@ function uploadPhotoInit() {}
*/
function uploadPhotoDone(handle, tokenV3, file) {
const service = getMembersService(tokenV3);
- return service.updateMemberPhoto(handle, file)
+ return service.getPresignedUrl(handle, file)
+ .then(res => service.uploadFileToS3(res))
+ .then(res => service.updateMemberPhoto(res))
.then(photoURL => ({ handle, photoURL }));
}
diff --git a/src/reducers/challenge.js b/src/reducers/challenge.js
index 7dde8670..db116631 100644
--- a/src/reducers/challenge.js
+++ b/src/reducers/challenge.js
@@ -368,6 +368,26 @@ function onGetSubmissionInformationDone(state, action) {
};
}
+/**
+ * Handles CHALLENGE/GET_CHALLENGE_STATISTICS_DONE action.
+ * @param {Object} state Previous state.
+ * @param {Object} action Action.
+ */
+function onFetchChallengeStatisticsDone(state, action) {
+ if (action.error) {
+ logger.error('Failed to get challenge statistics', action.payload);
+ return {
+ ...state,
+ statisticsData: null,
+ };
+ }
+
+ return {
+ ...state,
+ statisticsData: action.payload,
+ };
+}
+
/**
* Creates a new Challenge reducer with the specified initial state.
* @param {Object} initialState Optional. Initial state.
@@ -411,6 +431,8 @@ function create(initialState) {
[a.getActiveChallengesCountDone]: onGetActiveChallengesCountDone,
[a.getSubmissionInformationInit]: onGetSubmissionInformationInit,
[a.getSubmissionInformationDone]: onGetSubmissionInformationDone,
+ [a.fetchChallengeStatisticsInit]: state => state,
+ [a.fetchChallengeStatisticsDone]: onFetchChallengeStatisticsDone,
}, _.defaults(initialState, {
details: null,
loadingCheckpoints: false,
@@ -427,6 +449,7 @@ function create(initialState) {
updatingChallengeUuid: '',
mmSubmissions: [],
submissionInformation: null,
+ statisticsData: null,
}));
}
diff --git a/src/services/challenges.js b/src/services/challenges.js
index bf2efe20..38ae8c5e 100644
--- a/src/services/challenges.js
+++ b/src/services/challenges.js
@@ -253,6 +253,21 @@ class ChallengesService {
};
}
+ /**
+ * Gets challenge statistics.
+ * @param {Number} challengeId
+ * @return {Promise} The array of statistics
+ */
+ async getChallengeStatistics(challengeId) {
+ return this.private.apiV5.get(`/challenges/${challengeId}/statistics`)
+ .then(res => (res.ok ? res.json() : new Error(res.statusText)))
+ .then(res => (
+ res.message
+ ? new Error(res.message)
+ : res
+ ));
+ }
+
/**
* Activates the specified challenge.
* @param {Number} challengeId
diff --git a/src/services/members.js b/src/services/members.js
index 61f3e9ab..7b6648b0 100644
--- a/src/services/members.js
+++ b/src/services/members.js
@@ -4,10 +4,11 @@
* members via API V3.
*/
-/* global FormData */
+/* global XMLHttpRequest */
import _ from 'lodash';
import qs from 'qs';
import { decodeToken } from '@topcoder-platform/tc-auth-lib';
+import logger from '../utils/logger';
import { getApiResponsePayload, handleApiResponse } from '../utils/tc';
import { getApi } from './api';
@@ -238,23 +239,72 @@ class MembersService {
}
/**
- * Updates member photo.
+ * Gets presigned url for member photo file.
* @param {String} userHandle The user handle
- * @param {File} file The photo to upload
+ * @param {File} file The file to get its presigned url
* @return {Promise} Resolves to the api response content
*/
- async updateMemberPhoto(userHandle, file) {
- const formData = new FormData();
- formData.append('photo', file);
- const res = await this.private.apiV5.fetch(`/members/${userHandle}/photo`, {
- method: 'POST',
- headers: {
- 'Content-Type': null,
- },
- body: formData,
+ async getPresignedUrl(userHandle, file) {
+ const res = await this.private.api.postJson(`/members/${userHandle}/photoUploadUrl`, { param: { contentType: file.type } });
+ const payload = await getApiResponsePayload(res);
+
+ return {
+ preSignedURL: payload.preSignedURL,
+ token: payload.token,
+ file,
+ userHandle,
+ };
+ }
+
+ /**
+ * Updates member photo.
+ * @param {Object} S3Response The response from uploadFileToS3() function.
+ * @return {Promise} Resolves to the api response content
+ */
+ async updateMemberPhoto(S3Response) {
+ const res = await this.private.api.putJson(`/members/${S3Response.userHandle}/photo`, { param: S3Response.body });
+ return getApiResponsePayload(res);
+ }
+
+ /**
+ * Uploads file to S3.
+ * @param {Object} presignedUrlResponse The presigned url response from
+ * getPresignedUrl() function.
+ * @return {Promise} Resolves to the api response content
+ */
+ uploadFileToS3(presignedUrlResponse) {
+ _.noop(this);
+ return new Promise((resolve, reject) => {
+ const xhr = new XMLHttpRequest();
+
+ xhr.open('PUT', presignedUrlResponse.preSignedURL, true);
+ xhr.setRequestHeader('Content-Type', presignedUrlResponse.file.type);
+
+ xhr.onreadystatechange = () => {
+ const { status } = xhr;
+ if (((status >= 200 && status < 300) || status === 304) && xhr.readyState === 4) {
+ resolve({
+ userHandle: presignedUrlResponse.userHandle,
+ body: {
+ token: presignedUrlResponse.token,
+ contentType: presignedUrlResponse.file.type,
+ },
+ });
+ } else if (status >= 400) {
+ const err = new Error('Could not upload image to S3');
+ err.status = status;
+ reject(err);
+ }
+ };
+
+ xhr.onerror = (err) => {
+ logger.error('Could not upload image to S3', err);
+
+ reject(err);
+ };
+
+ xhr.send(presignedUrlResponse.file);
});
- return handleApiResponse(res)
- .then(({ photoURL }) => photoURL);
}
/**
diff --git a/src/services/user-traits.js b/src/services/user-traits.js
index 34d25a26..c1d71b71 100644
--- a/src/services/user-traits.js
+++ b/src/services/user-traits.js
@@ -4,7 +4,7 @@
* via API V3.
*/
import toCapitalCase from 'to-capital-case';
-import { handleApiResponse } from '../utils/tc';
+import { getApiResponsePayload } from '../utils/tc';
import { getApi } from './api';
/**
@@ -16,7 +16,7 @@ class UserTraitsService {
*/
constructor(tokenV3) {
this.private = {
- api: getApi('V5', tokenV3),
+ api: getApi('V3', tokenV3),
tokenV3,
};
}
@@ -29,7 +29,7 @@ class UserTraitsService {
async getAllUserTraits(handle) {
// FIXME: Remove the .toLowerCase() when the API is fixed to ignore the case in the route params
const res = await this.private.api.get(`/members/${handle.toLowerCase()}/traits`);
- return handleApiResponse(res);
+ return getApiResponsePayload(res);
}
/**
@@ -40,16 +40,18 @@ class UserTraitsService {
* @return {Promise} Resolves to the member traits.
*/
async addUserTrait(handle, traitId, data) {
- const body = [{
- traitId,
- categoryName: toCapitalCase(traitId),
- traits: {
- data,
- },
- }];
+ const body = {
+ param: [{
+ traitId,
+ categoryName: toCapitalCase(traitId),
+ traits: {
+ data,
+ },
+ }],
+ };
const res = await this.private.api.postJson(`/members/${handle}/traits`, body);
- return handleApiResponse(res);
+ return getApiResponsePayload(res);
}
/**
@@ -60,16 +62,18 @@ class UserTraitsService {
* @return {Promise} Resolves to the member traits.
*/
async updateUserTrait(handle, traitId, data) {
- const body = [{
- traitId,
- categoryName: toCapitalCase(traitId),
- traits: {
- data,
- },
- }];
+ const body = {
+ param: [{
+ traitId,
+ categoryName: toCapitalCase(traitId),
+ traits: {
+ data,
+ },
+ }],
+ };
const res = await this.private.api.putJson(`/members/${handle}/traits`, body);
- return handleApiResponse(res);
+ return getApiResponsePayload(res);
}
/**
@@ -80,7 +84,7 @@ class UserTraitsService {
*/
async deleteUserTrait(handle, traitId) {
const res = await this.private.api.delete(`/members/${handle}/traits?traitIds=${traitId}`);
- return handleApiResponse(res);
+ return getApiResponsePayload(res);
}
}
diff --git a/src/utils/tc.js b/src/utils/tc.js
index ff6d46f1..2941de37 100644
--- a/src/utils/tc.js
+++ b/src/utils/tc.js
@@ -87,8 +87,7 @@ export async function getApiResponsePayload(res, shouldThrowError = true) {
*/
export function handleApiResponse(response) {
if (!response.ok) throw new Error(response.statusText);
- return response.json()
- .catch(() => null);
+ return response.json();
}
/**