Skip to content

Commit 83a7c5c

Browse files
Merge pull request #325 from topcoder-platform/revert-322-feat/traits-v5-upgrade
Revert "feat: use v5 api to save traits"
2 parents 4a65856 + 2397bab commit 83a7c5c

File tree

7 files changed

+131
-44
lines changed

7 files changed

+131
-44
lines changed

__tests__/actions/profile.js

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

1919
// Mock services
2020
const mockMembersService = {
21+
getPresignedUrl: jest.fn().mockReturnValue(Promise.resolve()),
22+
uploadFileToS3: jest.fn().mockReturnValue(Promise.resolve()),
2123
updateMemberPhoto: jest.fn().mockReturnValue(Promise.resolve('url-of-photo')),
2224
updateMemberProfile: jest.fn().mockReturnValue(Promise.resolve(profile)),
2325
addSkill: jest.fn().mockReturnValue(Promise.resolve({ skills: [skill] })),
@@ -45,6 +47,8 @@ test('Module exports', () => expect(actions).toMatchSnapshot());
4547
test('profile.uploadPhotoDone', async () => {
4648
const actionResult = await redux.resolveAction(actions.profile.uploadPhotoDone(handle, tokenV3));
4749
expect(actionResult).toMatchSnapshot();
50+
expect(mockMembersService.getPresignedUrl).toBeCalled();
51+
expect(mockMembersService.uploadFileToS3).toBeCalled();
4852
expect(mockMembersService.updateMemberPhoto).toBeCalled();
4953
});
5054

docs/services.members.md

+34-6
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ 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-
* [.updateMemberPhoto(userHandle, file)](#module_services.members..MembersService+updateMemberPhoto) ⇒ <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>
2931
* [.verifyMemberNewEmail(handle, emailVerifyToken)](#module_services.members..MembersService+verifyMemberNewEmail) ⇒ <code>Promise</code>
3032

3133
<a name="module_services.members.getService"></a>
@@ -63,7 +65,9 @@ Service class.
6365
* [.addSkill(handle, skillTagId)](#module_services.members..MembersService+addSkill) ⇒ <code>Promise</code>
6466
* [.hideSkill(handle, skillTagId)](#module_services.members..MembersService+hideSkill) ⇒ <code>Promise</code>
6567
* [.updateMemberProfile(profile)](#module_services.members..MembersService+updateMemberProfile) ⇒ <code>Promise</code>
66-
* [.updateMemberPhoto(userHandle, file)](#module_services.members..MembersService+updateMemberPhoto) ⇒ <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>
6771
* [.verifyMemberNewEmail(handle, emailVerifyToken)](#module_services.members..MembersService+verifyMemberNewEmail) ⇒ <code>Promise</code>
6872

6973
<a name="new_module_services.members..MembersService_new"></a>
@@ -252,18 +256,42 @@ Updates member profile.
252256
| --- | --- | --- |
253257
| profile | <code>Object</code> | The profile to update. |
254258

255-
<a name="module_services.members..MembersService+updateMemberPhoto"></a>
259+
<a name="module_services.members..MembersService+getPresignedUrl"></a>
256260

257-
#### membersService.updateMemberPhoto(userHandle, file) ⇒ <code>Promise</code>
258-
Uploads and updates member photo.
261+
#### membersService.getPresignedUrl(userHandle, file) ⇒ <code>Promise</code>
262+
Gets presigned url for member photo file.
259263

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

263267
| Param | Type | Description |
264268
| --- | --- | --- |
265269
| userHandle | <code>String</code> | The user handle |
266-
| file | <code>File</code> | The file to be uploaded |
270+
| file | <code>File</code> | The file to get its presigned url |
271+
272+
<a name="module_services.members..MembersService+updateMemberPhoto"></a>
273+
274+
#### membersService.updateMemberPhoto(S3Response) ⇒ <code>Promise</code>
275+
Updates member photo.
276+
277+
**Kind**: instance method of [<code>MembersService</code>](#module_services.members..MembersService)
278+
**Returns**: <code>Promise</code> - Resolves to the api response content
279+
280+
| Param | Type | Description |
281+
| --- | --- | --- |
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. |
267295

268296
<a name="module_services.members..MembersService+verifyMemberNewEmail"></a>
269297

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

src/actions/profile.js

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

src/services/members.js

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

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

@@ -238,23 +239,72 @@ class MembersService {
238239
}
239240

240241
/**
241-
* Updates member photo.
242+
* Gets presigned url for member photo file.
242243
* @param {String} userHandle The user handle
243-
* @param {File} file The photo to upload
244+
* @param {File} file The file to get its presigned url
244245
* @return {Promise} Resolves to the api response content
245246
*/
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,
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+
259+
/**
260+
* 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.
273+
* @return {Promise} Resolves to the api response content
274+
*/
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);
255307
});
256-
return handleApiResponse(res)
257-
.then(({ photoURL }) => photoURL);
258308
}
259309

260310
/**

src/services/user-traits.js

+24-20
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 { handleApiResponse } from '../utils/tc';
7+
import { getApiResponsePayload } 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('V5', tokenV3),
19+
api: getApi('V3', 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 handleApiResponse(res);
32+
return getApiResponsePayload(res);
3333
}
3434

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

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

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

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

7579
/**
@@ -80,7 +84,7 @@ class UserTraitsService {
8084
*/
8185
async deleteUserTrait(handle, traitId) {
8286
const res = await this.private.api.delete(`/members/${handle}/traits?traitIds=${traitId}`);
83-
return handleApiResponse(res);
87+
return getApiResponsePayload(res);
8488
}
8589
}
8690

src/utils/tc.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,7 @@ 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()
91-
.catch(() => null);
90+
return response.json();
9291
}
9392

9493
/**

0 commit comments

Comments
 (0)