diff --git a/__tests__/actions/profile.js b/__tests__/actions/profile.js index b4d1848b..4ea366a3 100644 --- a/__tests__/actions/profile.js +++ b/__tests__/actions/profile.js @@ -18,8 +18,6 @@ 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] })), @@ -47,8 +45,6 @@ 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/docs/services.members.md b/docs/services.members.md index 856b90ce..8fe477f2 100644 --- a/docs/services.members.md +++ b/docs/services.members.md @@ -25,9 +25,7 @@ members via API V3. * [.addSkill(handle, skillTagId)](#module_services.members..MembersService+addSkill) ⇒ <code>Promise</code> * [.hideSkill(handle, skillTagId)](#module_services.members..MembersService+hideSkill) ⇒ <code>Promise</code> * [.updateMemberProfile(profile)](#module_services.members..MembersService+updateMemberProfile) ⇒ <code>Promise</code> - * [.getPresignedUrl(userHandle, file)](#module_services.members..MembersService+getPresignedUrl) ⇒ <code>Promise</code> - * [.updateMemberPhoto(S3Response)](#module_services.members..MembersService+updateMemberPhoto) ⇒ <code>Promise</code> - * [.uploadFileToS3(presignedUrlResponse)](#module_services.members..MembersService+uploadFileToS3) ⇒ <code>Promise</code> + * [.updateMemberPhoto(userHandle, file)](#module_services.members..MembersService+updateMemberPhoto) ⇒ <code>Promise</code> * [.verifyMemberNewEmail(handle, emailVerifyToken)](#module_services.members..MembersService+verifyMemberNewEmail) ⇒ <code>Promise</code> <a name="module_services.members.getService"></a> @@ -65,9 +63,7 @@ Service class. * [.addSkill(handle, skillTagId)](#module_services.members..MembersService+addSkill) ⇒ <code>Promise</code> * [.hideSkill(handle, skillTagId)](#module_services.members..MembersService+hideSkill) ⇒ <code>Promise</code> * [.updateMemberProfile(profile)](#module_services.members..MembersService+updateMemberProfile) ⇒ <code>Promise</code> - * [.getPresignedUrl(userHandle, file)](#module_services.members..MembersService+getPresignedUrl) ⇒ <code>Promise</code> - * [.updateMemberPhoto(S3Response)](#module_services.members..MembersService+updateMemberPhoto) ⇒ <code>Promise</code> - * [.uploadFileToS3(presignedUrlResponse)](#module_services.members..MembersService+uploadFileToS3) ⇒ <code>Promise</code> + * [.updateMemberPhoto(userHandle, file)](#module_services.members..MembersService+updateMemberPhoto) ⇒ <code>Promise</code> * [.verifyMemberNewEmail(handle, emailVerifyToken)](#module_services.members..MembersService+verifyMemberNewEmail) ⇒ <code>Promise</code> <a name="new_module_services.members..MembersService_new"></a> @@ -256,42 +252,18 @@ Updates member profile. | --- | --- | --- | | profile | <code>Object</code> | The profile to update. | -<a name="module_services.members..MembersService+getPresignedUrl"></a> - -#### membersService.getPresignedUrl(userHandle, file) ⇒ <code>Promise</code> -Gets presigned url for member photo file. - -**Kind**: instance method of [<code>MembersService</code>](#module_services.members..MembersService) -**Returns**: <code>Promise</code> - Resolves to the api response content - -| Param | Type | Description | -| --- | --- | --- | -| userHandle | <code>String</code> | The user handle | -| file | <code>File</code> | The file to get its presigned url | - <a name="module_services.members..MembersService+updateMemberPhoto"></a> -#### membersService.updateMemberPhoto(S3Response) ⇒ <code>Promise</code> -Updates member photo. +#### membersService.updateMemberPhoto(userHandle, file) ⇒ <code>Promise</code> +Uploads and updates member photo. **Kind**: instance method of [<code>MembersService</code>](#module_services.members..MembersService) **Returns**: <code>Promise</code> - Resolves to the api response content | Param | Type | Description | | --- | --- | --- | -| S3Response | <code>Object</code> | The response from uploadFileToS3() function. | - -<a name="module_services.members..MembersService+uploadFileToS3"></a> - -#### membersService.uploadFileToS3(presignedUrlResponse) ⇒ <code>Promise</code> -Uploads file to S3. - -**Kind**: instance method of [<code>MembersService</code>](#module_services.members..MembersService) -**Returns**: <code>Promise</code> - Resolves to the api response content - -| Param | Type | Description | -| --- | --- | --- | -| presignedUrlResponse | <code>Object</code> | The presigned url response from getPresignedUrl() function. | +| userHandle | <code>String</code> | The user handle | +| file | <code>File</code> | The file to be uploaded | <a name="module_services.members..MembersService+verifyMemberNewEmail"></a> diff --git a/package.json b/package.json index b1bf9678..7ab753e2 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.2", + "version": "1.2.3", "dependencies": { "auth0-js": "^6.8.4", "config": "^3.2.0", diff --git a/src/actions/profile.js b/src/actions/profile.js index bcd668cf..96a9ef5e 100644 --- a/src/actions/profile.js +++ b/src/actions/profile.js @@ -216,9 +216,7 @@ function uploadPhotoInit() {} */ function uploadPhotoDone(handle, tokenV3, file) { const service = getMembersService(tokenV3); - return service.getPresignedUrl(handle, file) - .then(res => service.uploadFileToS3(res)) - .then(res => service.updateMemberPhoto(res)) + return service.updateMemberPhoto(handle, file) .then(photoURL => ({ handle, photoURL })); } diff --git a/src/services/members.js b/src/services/members.js index 7b6648b0..61f3e9ab 100644 --- a/src/services/members.js +++ b/src/services/members.js @@ -4,11 +4,10 @@ * members via API V3. */ -/* global XMLHttpRequest */ +/* global FormData */ 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,73 +237,24 @@ class MembersService { return getApiResponsePayload(res); } - /** - * Gets presigned url for member photo file. - * @param {String} userHandle The user handle - * @param {File} file The file to get its presigned url - * @return {Promise} Resolves to the api response content - */ - 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. + * @param {String} userHandle The user handle + * @param {File} file The photo to upload * @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); + 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, }); + return handleApiResponse(res) + .then(({ photoURL }) => photoURL); } /** diff --git a/src/services/user-traits.js b/src/services/user-traits.js index c1d71b71..34d25a26 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 { getApiResponsePayload } from '../utils/tc'; +import { handleApiResponse } from '../utils/tc'; import { getApi } from './api'; /** @@ -16,7 +16,7 @@ class UserTraitsService { */ constructor(tokenV3) { this.private = { - api: getApi('V3', tokenV3), + api: getApi('V5', 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 getApiResponsePayload(res); + return handleApiResponse(res); } /** @@ -40,18 +40,16 @@ class UserTraitsService { * @return {Promise} Resolves to the member traits. */ async addUserTrait(handle, traitId, data) { - const body = { - param: [{ - traitId, - categoryName: toCapitalCase(traitId), - traits: { - data, - }, - }], - }; + const body = [{ + traitId, + categoryName: toCapitalCase(traitId), + traits: { + data, + }, + }]; const res = await this.private.api.postJson(`/members/${handle}/traits`, body); - return getApiResponsePayload(res); + return handleApiResponse(res); } /** @@ -62,18 +60,16 @@ class UserTraitsService { * @return {Promise} Resolves to the member traits. */ async updateUserTrait(handle, traitId, data) { - const body = { - param: [{ - traitId, - categoryName: toCapitalCase(traitId), - traits: { - data, - }, - }], - }; + const body = [{ + traitId, + categoryName: toCapitalCase(traitId), + traits: { + data, + }, + }]; const res = await this.private.api.putJson(`/members/${handle}/traits`, body); - return getApiResponsePayload(res); + return handleApiResponse(res); } /** @@ -84,7 +80,7 @@ class UserTraitsService { */ async deleteUserTrait(handle, traitId) { const res = await this.private.api.delete(`/members/${handle}/traits?traitIds=${traitId}`); - return getApiResponsePayload(res); + return handleApiResponse(res); } } diff --git a/src/utils/tc.js b/src/utils/tc.js index 2941de37..ff6d46f1 100644 --- a/src/utils/tc.js +++ b/src/utils/tc.js @@ -87,7 +87,8 @@ export async function getApiResponsePayload(res, shouldThrowError = true) { */ export function handleApiResponse(response) { if (!response.ok) throw new Error(response.statusText); - return response.json(); + return response.json() + .catch(() => null); } /**