From 94ada5f8acd7bc3082caf0c0fe33ff93a63a7053 Mon Sep 17 00:00:00 2001 From: Robin Gagnon Date: Fri, 4 Oct 2024 13:32:43 -0400 Subject: [PATCH] feat(Moments): Add function to create Moment without uploading media * Rename `createMoment` to `createMomentAndUploadMedia` * Create new `createMoment` function to create a Moment without uploading media. * Change visibility to `public` for `uploadMediaList` and `awaitForMediaProcessing` * Allows clients to upload media separately from Moments creation * Adjust MomentsClient unit tests --- docs/pages/packages/moments/UploadMoments.mdx | 2 +- packages/moments/README.md | 2 +- .../moments/src/client/MomentsClient.spec.ts | 61 +++++++++++++++++-- packages/moments/src/client/MomentsClient.ts | 21 +++++-- .../dtos/create/CreateAndUploadInput.ts | 25 ++++++++ .../src/client/dtos/create/CreateInput.ts | 6 +- 6 files changed, 101 insertions(+), 16 deletions(-) create mode 100644 packages/moments/src/client/dtos/create/CreateAndUploadInput.ts diff --git a/docs/pages/packages/moments/UploadMoments.mdx b/docs/pages/packages/moments/UploadMoments.mdx index e8141f0..1c4b7fb 100644 --- a/docs/pages/packages/moments/UploadMoments.mdx +++ b/docs/pages/packages/moments/UploadMoments.mdx @@ -15,7 +15,7 @@ const fileBuffer = await fsPromises.readFile('path/to/poap-moment.png'); const mimeType = mime.getType('path/to/poap-moment.png'); // Upload it. -const moment: Moment = await client.createMoment({ +const moment: Moment = await client.createMomentAndUploadMedia({ /** * Moments are associated with a Drop. */ diff --git a/packages/moments/README.md b/packages/moments/README.md index 945ac1e..7aedfb2 100644 --- a/packages/moments/README.md +++ b/packages/moments/README.md @@ -61,7 +61,7 @@ const input: CreateMomentInput = { }, timeOut: 5000, // Optional: Set a timeout for the media processing }; -const moment: Moment = await client.createMoment(input); +const moment: Moment = await client.createMomentAndUploadMedia(input); ``` Explanations for each step: diff --git a/packages/moments/src/client/MomentsClient.spec.ts b/packages/moments/src/client/MomentsClient.spec.ts index 2d7fb5e..7fa6c47 100644 --- a/packages/moments/src/client/MomentsClient.spec.ts +++ b/packages/moments/src/client/MomentsClient.spec.ts @@ -5,6 +5,7 @@ import { CreateMomentInput } from './dtos/create/CreateInput'; import { CreateSteps } from './dtos/create/CreateSteps'; import { v4 } from 'uuid'; import { PatchMomentInput } from './dtos/patch/PatchInput'; +import { CreateAndUploadMomentInput } from './dtos/create/CreateAndUploadInput'; describe('MomentsClient', () => { const MOMENT_ID = 'this-is-a-moment-id'; @@ -29,6 +30,10 @@ describe('MomentsClient', () => { fileType: FILE_2_TYPE, }, ]; + const MEDIA_KEYS = [ + '45201634-1996-4243-9c24-31da706be427', + 'fbc3cf5f-fd65-4d2f-88fa-7025ebc7d631', + ]; let poapMomentsAPIMocked: MockProxy; let compassProviderMocked: MockProxy; @@ -40,14 +45,14 @@ describe('MomentsClient', () => { onStepUpdate = jest.fn(); }); - describe('createMoment', () => { - it('should create a moment', async () => { + describe('createMomentAndUploadMedia', () => { + it('should create a moment and upload media', async () => { // GIVEN const client = new MomentsClient( poapMomentsAPIMocked, compassProviderMocked, ); - const inputs: CreateMomentInput = { + const inputs: CreateAndUploadMomentInput = { dropId: DROP_ID, tokenId: TOKEN_ID, media: MEDIAS_TO_CREATE, @@ -81,7 +86,7 @@ describe('MomentsClient', () => { }; // WHEN - const moment = await client.createMoment(inputs); + const moment = await client.createMomentAndUploadMedia(inputs); // THEN expect(moment.id).toBe(MOMENT_ID); @@ -111,6 +116,54 @@ describe('MomentsClient', () => { expect(onStepUpdate).toHaveBeenCalledTimes(4); }); }); + describe('createMoment', () => { + it('should create a moment and attach media keys', async () => { + // GIVEN + const client = new MomentsClient( + poapMomentsAPIMocked, + compassProviderMocked, + ); + const inputs: CreateMomentInput = { + dropId: DROP_ID, + tokenId: TOKEN_ID, + mediaKeys: MEDIA_KEYS, + author: AUTHOR, + onStepUpdate, + description: DESCRIPTION, + }; + poapMomentsAPIMocked.createMoment.mockResolvedValue({ + id: MOMENT_ID, + author: AUTHOR, + createdOn: new Date(), + dropId: DROP_ID, + tokenId: TOKEN_ID, + }); + + const EXPECTED_MOMENT_CREATE_INPUT = { + dropId: DROP_ID, + tokenId: TOKEN_ID, + author: AUTHOR, + description: DESCRIPTION, + mediaKeys: MEDIA_KEYS, + }; + + // WHEN + const moment = await client.createMoment(inputs); + + // THEN + expect(moment.id).toBe(MOMENT_ID); + expect(moment.author).toBe(AUTHOR); + expect(moment.dropId).toBe(DROP_ID); + expect(moment.tokenId).toBe(TOKEN_ID); + expect(poapMomentsAPIMocked.createMoment).toHaveBeenCalledWith( + EXPECTED_MOMENT_CREATE_INPUT, + ); + expect(poapMomentsAPIMocked.uploadFile).not.toHaveBeenCalledWith(); + expect(onStepUpdate).toHaveBeenCalledWith(CreateSteps.UPLOADING_MOMENT); + expect(onStepUpdate).toHaveBeenCalledWith(CreateSteps.FINISHED); + expect(onStepUpdate).toHaveBeenCalledTimes(2); + }); + }); describe('updateMoment', () => { it('should update a moment', async () => { // GIVEN diff --git a/packages/moments/src/client/MomentsClient.ts b/packages/moments/src/client/MomentsClient.ts index 9daf487..1cd2f13 100644 --- a/packages/moments/src/client/MomentsClient.ts +++ b/packages/moments/src/client/MomentsClient.ts @@ -20,6 +20,7 @@ import { CreateSteps } from './dtos/create/CreateSteps'; import { PatchMomentInput } from './dtos/patch/PatchInput'; import { FetchMomentsInput } from './dtos/fetch/FetchMomentsInput'; import { MomentsSortFields } from './dtos/fetch/MomentsSortFields'; +import { CreateAndUploadMomentInput } from './dtos/create/CreateAndUploadInput'; export class MomentsClient { constructor( @@ -27,10 +28,13 @@ export class MomentsClient { private CompassProvider: CompassProvider, ) {} - public async createMoment(input: CreateMomentInput): Promise { + /** Uploads media files first, then creates the Moment. */ + public async createMomentAndUploadMedia( + input: CreateAndUploadMomentInput, + ): Promise { let mediaKeys: string[] = []; if (input.media && input.media.length > 0) { - mediaKeys = await this.uploadMedias( + mediaKeys = await this.uploadMediaList( input.media, input.onStepUpdate, input.onFileUploadProgress, @@ -39,20 +43,25 @@ export class MomentsClient { } if (mediaKeys.length > 0) { - await this.awaitForMediasProcessing( + await this.awaitForMediaProcessing( mediaKeys, input.onStepUpdate, input.timeOut, ); } + return this.createMoment({ ...input, mediaKeys }); + } + + /** Creates the Moment, attaching previously uploaded media when applicable. */ + public async createMoment(input: CreateMomentInput): Promise { void input.onStepUpdate?.(CreateSteps.UPLOADING_MOMENT); const response = await this.poapMomentsApi.createMoment({ dropId: input.dropId, author: input.author, tokenId: input.tokenId, description: input.description, - mediaKeys, + mediaKeys: input.mediaKeys || [], }); void input.onStepUpdate?.(CreateSteps.FINISHED); @@ -60,7 +69,7 @@ export class MomentsClient { } // eslint-disable-next-line max-statements - private async uploadMedias( + public async uploadMediaList( mediaArray: CreateMedia[], onStepUpdate?: (step: CreateSteps) => void | Promise, onFileUploadProgress?: (progress: number) => void | Promise, @@ -89,7 +98,7 @@ export class MomentsClient { return mediaKeys; } - private async awaitForMediasProcessing( + public async awaitForMediaProcessing( mediaKeys: string[], onStepUpdate?: (step: CreateSteps) => void | Promise, timeOut?: number, diff --git a/packages/moments/src/client/dtos/create/CreateAndUploadInput.ts b/packages/moments/src/client/dtos/create/CreateAndUploadInput.ts new file mode 100644 index 0000000..27ef4fe --- /dev/null +++ b/packages/moments/src/client/dtos/create/CreateAndUploadInput.ts @@ -0,0 +1,25 @@ +import { CreateSteps } from './CreateSteps'; +import { CreateMedia } from './CreateMedia'; + +/** + * Interface representing the input needed to create a moment and upload media in one action. + * @interface + * @property {number} dropId - The ID of the drop related to the moment. + * @property {number} [tokenId] - The ID of the token related to the moment (optional). + * @property {string} author - The author of the moment. An Ethereum address. + * @property {string} description - The description of the moment (optional). + * @property {string} timeOut - The amount of time to wait until media is processed. + * @property {(step: CreateSteps) => void | Promise} [onStepUpdate] - Optional callback function to be called when the step changes. + * @property {(progress: number) => void | Promise} [onFileProgress] - Optional callback function to be called when the file upload progress change - progress is a number between 0 and 1. + * @property {CreateMedia[]} media - The media to be uploaded. + */ +export interface CreateAndUploadMomentInput { + author: string; + description?: string; + dropId: number; + tokenId?: number; + timeOut?: number; + onStepUpdate?: (step: CreateSteps) => void | Promise; + onFileUploadProgress?: (progress: number) => void | Promise; + media?: CreateMedia[]; +} diff --git a/packages/moments/src/client/dtos/create/CreateInput.ts b/packages/moments/src/client/dtos/create/CreateInput.ts index 264d04d..3c60298 100644 --- a/packages/moments/src/client/dtos/create/CreateInput.ts +++ b/packages/moments/src/client/dtos/create/CreateInput.ts @@ -1,5 +1,4 @@ import { CreateSteps } from './CreateSteps'; -import { CreateMedia } from './CreateMedia'; /** * Interface representing the input needed to create a moment. @@ -11,7 +10,7 @@ import { CreateMedia } from './CreateMedia'; * @property {string} timeOut - The amount of time to wait until media is processed. * @property {(step: CreateSteps) => void | Promise} [onStepUpdate] - Optional callback function to be called when the step changes. * @property {(progress: number) => void | Promise} [onFileProgress] - Optional callback function to be called when the file upload progress change - progress is a number between 0 and 1. - * @property {CreateMedia[]} media - The media to be uploaded. + * @property {string[]} mediaKeys - The media keys previously uploaded to attach to the Moment. */ export interface CreateMomentInput { author: string; @@ -20,6 +19,5 @@ export interface CreateMomentInput { tokenId?: number; timeOut?: number; onStepUpdate?: (step: CreateSteps) => void | Promise; - onFileUploadProgress?: (progress: number) => void | Promise; - media?: CreateMedia[]; + mediaKeys?: string[]; }