Skip to content

Commit 4a65856

Browse files
authored
Merge pull request #322 from topcoder-platform/feat/traits-v5-upgrade
feat: use v5 api to save traits
2 parents 29cde61 + 9e3af51 commit 4a65856

File tree

7 files changed

+44
-131
lines changed

7 files changed

+44
-131
lines changed

__tests__/actions/profile.js

-4
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ const linkedAccounts = [{
1818

1919
// Mock services
2020
const mockMembersService = {
21-
getPresignedUrl: jest.fn().mockReturnValue(Promise.resolve()),
22-
uploadFileToS3: jest.fn().mockReturnValue(Promise.resolve()),
2321
updateMemberPhoto: jest.fn().mockReturnValue(Promise.resolve('url-of-photo')),
2422
updateMemberProfile: jest.fn().mockReturnValue(Promise.resolve(profile)),
2523
addSkill: jest.fn().mockReturnValue(Promise.resolve({ skills: [skill] })),
@@ -47,8 +45,6 @@ test('Module exports', () => expect(actions).toMatchSnapshot());
4745
test('profile.uploadPhotoDone', async () => {
4846
const actionResult = await redux.resolveAction(actions.profile.uploadPhotoDone(handle, tokenV3));
4947
expect(actionResult).toMatchSnapshot();
50-
expect(mockMembersService.getPresignedUrl).toBeCalled();
51-
expect(mockMembersService.uploadFileToS3).toBeCalled();
5248
expect(mockMembersService.updateMemberPhoto).toBeCalled();
5349
});
5450

docs/services.members.md

+6-34
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@ members via API V3.
2525
* [.addSkill(handle, skillTagId)](#module_services.members..MembersService+addSkill) ⇒ <code>Promise</code>
2626
* [.hideSkill(handle, skillTagId)](#module_services.members..MembersService+hideSkill) ⇒ <code>Promise</code>
2727
* [.updateMemberProfile(profile)](#module_services.members..MembersService+updateMemberProfile) ⇒ <code>Promise</code>
28-
* [.getPresignedUrl(userHandle, file)](#module_services.members..MembersService+getPresignedUrl) ⇒ <code>Promise</code>
29-
* [.updateMemberPhoto(S3Response)](#module_services.members..MembersService+updateMemberPhoto) ⇒ <code>Promise</code>
30-
* [.uploadFileToS3(presignedUrlResponse)](#module_services.members..MembersService+uploadFileToS3) ⇒ <code>Promise</code>
28+
* [.updateMemberPhoto(userHandle, file)](#module_services.members..MembersService+updateMemberPhoto) ⇒ <code>Promise</code>
3129
* [.verifyMemberNewEmail(handle, emailVerifyToken)](#module_services.members..MembersService+verifyMemberNewEmail) ⇒ <code>Promise</code>
3230

3331
<a name="module_services.members.getService"></a>
@@ -65,9 +63,7 @@ Service class.
6563
* [.addSkill(handle, skillTagId)](#module_services.members..MembersService+addSkill) ⇒ <code>Promise</code>
6664
* [.hideSkill(handle, skillTagId)](#module_services.members..MembersService+hideSkill) ⇒ <code>Promise</code>
6765
* [.updateMemberProfile(profile)](#module_services.members..MembersService+updateMemberProfile) ⇒ <code>Promise</code>
68-
* [.getPresignedUrl(userHandle, file)](#module_services.members..MembersService+getPresignedUrl) ⇒ <code>Promise</code>
69-
* [.updateMemberPhoto(S3Response)](#module_services.members..MembersService+updateMemberPhoto) ⇒ <code>Promise</code>
70-
* [.uploadFileToS3(presignedUrlResponse)](#module_services.members..MembersService+uploadFileToS3) ⇒ <code>Promise</code>
66+
* [.updateMemberPhoto(userHandle, file)](#module_services.members..MembersService+updateMemberPhoto) ⇒ <code>Promise</code>
7167
* [.verifyMemberNewEmail(handle, emailVerifyToken)](#module_services.members..MembersService+verifyMemberNewEmail) ⇒ <code>Promise</code>
7268

7369
<a name="new_module_services.members..MembersService_new"></a>
@@ -256,42 +252,18 @@ Updates member profile.
256252
| --- | --- | --- |
257253
| profile | <code>Object</code> | The profile to update. |
258254

259-
<a name="module_services.members..MembersService+getPresignedUrl"></a>
260-
261-
#### membersService.getPresignedUrl(userHandle, file) ⇒ <code>Promise</code>
262-
Gets presigned url for member photo file.
263-
264-
**Kind**: instance method of [<code>MembersService</code>](#module_services.members..MembersService)
265-
**Returns**: <code>Promise</code> - Resolves to the api response content
266-
267-
| Param | Type | Description |
268-
| --- | --- | --- |
269-
| userHandle | <code>String</code> | The user handle |
270-
| file | <code>File</code> | The file to get its presigned url |
271-
272255
<a name="module_services.members..MembersService+updateMemberPhoto"></a>
273256

274-
#### membersService.updateMemberPhoto(S3Response) ⇒ <code>Promise</code>
275-
Updates member photo.
257+
#### membersService.updateMemberPhoto(userHandle, file) ⇒ <code>Promise</code>
258+
Uploads and updates member photo.
276259

277260
**Kind**: instance method of [<code>MembersService</code>](#module_services.members..MembersService)
278261
**Returns**: <code>Promise</code> - Resolves to the api response content
279262

280263
| Param | Type | Description |
281264
| --- | --- | --- |
282-
| S3Response | <code>Object</code> | The response from uploadFileToS3() function. |
283-
284-
<a name="module_services.members..MembersService+uploadFileToS3"></a>
285-
286-
#### membersService.uploadFileToS3(presignedUrlResponse) ⇒ <code>Promise</code>
287-
Uploads file to S3.
288-
289-
**Kind**: instance method of [<code>MembersService</code>](#module_services.members..MembersService)
290-
**Returns**: <code>Promise</code> - Resolves to the api response content
291-
292-
| Param | Type | Description |
293-
| --- | --- | --- |
294-
| presignedUrlResponse | <code>Object</code> | The presigned url response from getPresignedUrl() function. |
265+
| userHandle | <code>String</code> | The user handle |
266+
| file | <code>File</code> | The file to be uploaded |
295267

296268
<a name="module_services.members..MembersService+verifyMemberNewEmail"></a>
297269

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": "1.2.2",
34+
"version": "1.2.3",
3535
"dependencies": {
3636
"auth0-js": "^6.8.4",
3737
"config": "^3.2.0",

src/actions/profile.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,7 @@ function uploadPhotoInit() {}
216216
*/
217217
function uploadPhotoDone(handle, tokenV3, file) {
218218
const service = getMembersService(tokenV3);
219-
return service.getPresignedUrl(handle, file)
220-
.then(res => service.uploadFileToS3(res))
221-
.then(res => service.updateMemberPhoto(res))
219+
return service.updateMemberPhoto(handle, file)
222220
.then(photoURL => ({ handle, photoURL }));
223221
}
224222

src/services/members.js

+14-64
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@
44
* members via API V3.
55
*/
66

7-
/* global XMLHttpRequest */
7+
/* global FormData */
88
import _ from 'lodash';
99
import qs from 'qs';
1010
import { decodeToken } from '@topcoder-platform/tc-auth-lib';
11-
import logger from '../utils/logger';
1211
import { getApiResponsePayload, handleApiResponse } from '../utils/tc';
1312
import { getApi } from './api';
1413

@@ -238,73 +237,24 @@ class MembersService {
238237
return getApiResponsePayload(res);
239238
}
240239

241-
/**
242-
* Gets presigned url for member photo file.
243-
* @param {String} userHandle The user handle
244-
* @param {File} file The file to get its presigned url
245-
* @return {Promise} Resolves to the api response content
246-
*/
247-
async getPresignedUrl(userHandle, file) {
248-
const res = await this.private.api.postJson(`/members/${userHandle}/photoUploadUrl`, { param: { contentType: file.type } });
249-
const payload = await getApiResponsePayload(res);
250-
251-
return {
252-
preSignedURL: payload.preSignedURL,
253-
token: payload.token,
254-
file,
255-
userHandle,
256-
};
257-
}
258-
259240
/**
260241
* Updates member photo.
261-
* @param {Object} S3Response The response from uploadFileToS3() function.
262-
* @return {Promise} Resolves to the api response content
263-
*/
264-
async updateMemberPhoto(S3Response) {
265-
const res = await this.private.api.putJson(`/members/${S3Response.userHandle}/photo`, { param: S3Response.body });
266-
return getApiResponsePayload(res);
267-
}
268-
269-
/**
270-
* Uploads file to S3.
271-
* @param {Object} presignedUrlResponse The presigned url response from
272-
* getPresignedUrl() function.
242+
* @param {String} userHandle The user handle
243+
* @param {File} file The photo to upload
273244
* @return {Promise} Resolves to the api response content
274245
*/
275-
uploadFileToS3(presignedUrlResponse) {
276-
_.noop(this);
277-
return new Promise((resolve, reject) => {
278-
const xhr = new XMLHttpRequest();
279-
280-
xhr.open('PUT', presignedUrlResponse.preSignedURL, true);
281-
xhr.setRequestHeader('Content-Type', presignedUrlResponse.file.type);
282-
283-
xhr.onreadystatechange = () => {
284-
const { status } = xhr;
285-
if (((status >= 200 && status < 300) || status === 304) && xhr.readyState === 4) {
286-
resolve({
287-
userHandle: presignedUrlResponse.userHandle,
288-
body: {
289-
token: presignedUrlResponse.token,
290-
contentType: presignedUrlResponse.file.type,
291-
},
292-
});
293-
} else if (status >= 400) {
294-
const err = new Error('Could not upload image to S3');
295-
err.status = status;
296-
reject(err);
297-
}
298-
};
299-
300-
xhr.onerror = (err) => {
301-
logger.error('Could not upload image to S3', err);
302-
303-
reject(err);
304-
};
305-
306-
xhr.send(presignedUrlResponse.file);
246+
async updateMemberPhoto(userHandle, file) {
247+
const formData = new FormData();
248+
formData.append('photo', file);
249+
const res = await this.private.apiV5.fetch(`/members/${userHandle}/photo`, {
250+
method: 'POST',
251+
headers: {
252+
'Content-Type': null,
253+
},
254+
body: formData,
307255
});
256+
return handleApiResponse(res)
257+
.then(({ photoURL }) => photoURL);
308258
}
309259

310260
/**

src/services/user-traits.js

+20-24
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* via API V3.
55
*/
66
import toCapitalCase from 'to-capital-case';
7-
import { getApiResponsePayload } from '../utils/tc';
7+
import { handleApiResponse } from '../utils/tc';
88
import { getApi } from './api';
99

1010
/**
@@ -16,7 +16,7 @@ class UserTraitsService {
1616
*/
1717
constructor(tokenV3) {
1818
this.private = {
19-
api: getApi('V3', tokenV3),
19+
api: getApi('V5', tokenV3),
2020
tokenV3,
2121
};
2222
}
@@ -29,7 +29,7 @@ class UserTraitsService {
2929
async getAllUserTraits(handle) {
3030
// FIXME: Remove the .toLowerCase() when the API is fixed to ignore the case in the route params
3131
const res = await this.private.api.get(`/members/${handle.toLowerCase()}/traits`);
32-
return getApiResponsePayload(res);
32+
return handleApiResponse(res);
3333
}
3434

3535
/**
@@ -40,18 +40,16 @@ class UserTraitsService {
4040
* @return {Promise} Resolves to the member traits.
4141
*/
4242
async addUserTrait(handle, traitId, data) {
43-
const body = {
44-
param: [{
45-
traitId,
46-
categoryName: toCapitalCase(traitId),
47-
traits: {
48-
data,
49-
},
50-
}],
51-
};
43+
const body = [{
44+
traitId,
45+
categoryName: toCapitalCase(traitId),
46+
traits: {
47+
data,
48+
},
49+
}];
5250

5351
const res = await this.private.api.postJson(`/members/${handle}/traits`, body);
54-
return getApiResponsePayload(res);
52+
return handleApiResponse(res);
5553
}
5654

5755
/**
@@ -62,18 +60,16 @@ class UserTraitsService {
6260
* @return {Promise} Resolves to the member traits.
6361
*/
6462
async updateUserTrait(handle, traitId, data) {
65-
const body = {
66-
param: [{
67-
traitId,
68-
categoryName: toCapitalCase(traitId),
69-
traits: {
70-
data,
71-
},
72-
}],
73-
};
63+
const body = [{
64+
traitId,
65+
categoryName: toCapitalCase(traitId),
66+
traits: {
67+
data,
68+
},
69+
}];
7470

7571
const res = await this.private.api.putJson(`/members/${handle}/traits`, body);
76-
return getApiResponsePayload(res);
72+
return handleApiResponse(res);
7773
}
7874

7975
/**
@@ -84,7 +80,7 @@ class UserTraitsService {
8480
*/
8581
async deleteUserTrait(handle, traitId) {
8682
const res = await this.private.api.delete(`/members/${handle}/traits?traitIds=${traitId}`);
87-
return getApiResponsePayload(res);
83+
return handleApiResponse(res);
8884
}
8985
}
9086

src/utils/tc.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ export async function getApiResponsePayload(res, shouldThrowError = true) {
8787
*/
8888
export function handleApiResponse(response) {
8989
if (!response.ok) throw new Error(response.statusText);
90-
return response.json();
90+
return response.json()
91+
.catch(() => null);
9192
}
9293

9394
/**

0 commit comments

Comments
 (0)