Skip to content

Commit

Permalink
Add Moments create upload multiple medias (#62)
Browse files Browse the repository at this point in the history
* Moments: add support for multiple files upload

* Add file progress calc

* bump package version

* fix test

* rename createMedia to uploadMedia
  • Loading branch information
nacho9900 authored Aug 31, 2023
1 parent 7c12674 commit 43b9dd4
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 74 deletions.
6 changes: 3 additions & 3 deletions packages/drops/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@poap-xyz/drops",
"version": "0.0.36",
"version": "0.0.37",
"description": "Drops module for the poap.js library",
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.mjs",
Expand All @@ -26,7 +26,7 @@
"build": "rollup -c --bundleConfigAsCjs"
},
"dependencies": {
"@poap-xyz/providers": "0.0.36",
"@poap-xyz/utils": "0.0.36"
"@poap-xyz/providers": "0.0.37",
"@poap-xyz/utils": "0.0.37"
}
}
10 changes: 7 additions & 3 deletions packages/moments/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@poap-xyz/moments",
"version": "0.0.36",
"version": "0.0.37",
"description": "Moments module for the poap.js library",
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.mjs",
Expand All @@ -26,7 +26,11 @@
"build": "rollup -c --bundleConfigAsCjs"
},
"dependencies": {
"@poap-xyz/providers": "0.0.36",
"@poap-xyz/utils": "0.0.36"
"@poap-xyz/providers": "0.0.37",
"@poap-xyz/utils": "0.0.37",
"uuid": "^9.0.0"
},
"devDependencies": {
"@types/uuid": "^9.0.2"
}
}
59 changes: 39 additions & 20 deletions packages/moments/src/client/MomentsClient.spec.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import { mock, MockProxy } from 'jest-mock-extended';
import { anyFunction, mock, MockProxy } from 'jest-mock-extended';
import { PoapCompass, PoapMomentsApi } from '@poap-xyz/providers';
import { MomentsClient } from './MomentsClient';
import { CreateMomentInput } from './dtos/create/CreateInput';
import { CreateSteps } from './dtos/create/CreateSteps';
import { v4 } from 'uuid';

describe('MomentsClient', () => {
const MOMENT_ID = 'this-is-a-moment-id';
const DROP_ID = 420;
const TOKEN_ID = 69;
const FILE = Buffer.from('');
const AUTHOR = '0x7CE5368171cC3D988157d7dab3D313d7bd43de3e';
const FILE_TYPE = 'image/png';
const MEDIA_KEY = 'this-is-a-media-key';
const FILE_1 = Buffer.from('This is the file 1');
const FILE_1_TYPE = 'image/png';
const FILE_2 = Buffer.from('This is the file 2');
const FILE_2_TYPE = 'image/jpeg';
const MEDIA_UPLOAD_URL = 'this-is-a-media-upload-url';
const DESCRIPTION = 'This is a description';
const MEDIAS_TO_CREATE = [
{
fileBinary: FILE_1,
fileType: FILE_1_TYPE,
},
{
fileBinary: FILE_2,
fileType: FILE_2_TYPE,
},
];

let poapMomentsAPIMocked: MockProxy<PoapMomentsApi>;
let compassProviderMocked: MockProxy<PoapCompass>;
Expand All @@ -35,12 +47,7 @@ describe('MomentsClient', () => {
const inputs: CreateMomentInput = {
dropId: DROP_ID,
tokenId: TOKEN_ID,
medias: [
{
fileBinary: FILE,
fileType: FILE_TYPE,
},
],
medias: MEDIAS_TO_CREATE,
author: AUTHOR,
onStepUpdate,
description: DESCRIPTION,
Expand All @@ -52,17 +59,22 @@ describe('MomentsClient', () => {
dropId: DROP_ID,
tokenId: TOKEN_ID,
});
poapMomentsAPIMocked.getSignedUrl.mockResolvedValue({
key: MEDIA_KEY,
url: MEDIA_UPLOAD_URL,
const mediaKeys: string[] = [];
poapMomentsAPIMocked.getSignedUrl.mockImplementation(async () => {
const key = v4();
mediaKeys.push(key);
return {
url: MEDIA_UPLOAD_URL,
key,
};
});

const EXPECTED_MOMENT_CREATE_INPUT = {
dropId: DROP_ID,
tokenId: TOKEN_ID,
author: AUTHOR,
description: DESCRIPTION,
mediaKeys: [MEDIA_KEY],
mediaKeys,
};

// WHEN
Expand All @@ -76,17 +88,24 @@ describe('MomentsClient', () => {
expect(poapMomentsAPIMocked.createMoment).toHaveBeenCalledWith(
EXPECTED_MOMENT_CREATE_INPUT,
);
expect(onStepUpdate).toHaveBeenCalledWith(
CreateSteps.REQUESTING_MEDIA_UPLOAD_URL,
expect(poapMomentsAPIMocked.getSignedUrl).toHaveBeenCalledTimes(2);
expect(poapMomentsAPIMocked.uploadFile).toHaveBeenCalledWith(
FILE_1,
MEDIA_UPLOAD_URL,
FILE_1_TYPE,
anyFunction(),
);
expect(onStepUpdate).toHaveBeenCalledWith(CreateSteps.UPLOADING_MEDIA);
expect(onStepUpdate).toHaveBeenCalledWith(
CreateSteps.UPLOADING_MEDIA_METADATA,
expect(poapMomentsAPIMocked.uploadFile).toHaveBeenCalledWith(
FILE_2,
MEDIA_UPLOAD_URL,
FILE_2_TYPE,
anyFunction(),
);
expect(onStepUpdate).toHaveBeenCalledWith(CreateSteps.UPLOADING_MEDIA);
expect(onStepUpdate).toHaveBeenCalledWith(CreateSteps.PROCESSING_MEDIA);
expect(onStepUpdate).toHaveBeenCalledWith(CreateSteps.UPLOADING_MOMENT);
expect(onStepUpdate).toHaveBeenCalledWith(CreateSteps.FINISHED);
expect(onStepUpdate).toHaveBeenCalledTimes(6);
expect(onStepUpdate).toHaveBeenCalledTimes(4);
});
});
});
99 changes: 68 additions & 31 deletions packages/moments/src/client/MomentsClient.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { PoapMomentsApi, CompassProvider } from '@poap-xyz/providers';
import { CompassProvider, PoapMomentsApi } from '@poap-xyz/providers';
import {
PaginatedResult,
nextCursor,
createBetweenFilter,
createFilter,
createInFilter,
creatEqFilter,
filterUndefinedProperties,
nextCursor,
PaginatedResult,
} from '@poap-xyz/utils';
import { Moment } from '../domain/Moment';
import {
Expand All @@ -20,45 +20,40 @@ import { FetchMomentsInput } from './dtos/fetch/FetchMomentsInput';
import { CreateMedia } from './dtos/create/CreateMedia';

export class MomentsClient {
private static readonly DEFAULT_ON_STEP_UPDATE = (): void => {
//do nothing
};

constructor(
private poapMomentsApi: PoapMomentsApi,
private CompassProvider: CompassProvider,
) {}

public async createMoment(input: CreateMomentInput): Promise<Moment> {
if (input.medias && input.medias.length > 1) {
// TODO: implement multiple medias
throw new Error('Multiple medias not supported yet');
}

const onStepUpdate =
input.onStepUpdate || MomentsClient.DEFAULT_ON_STEP_UPDATE;

const mediaKeys: string[] = [];
let mediaKeys: string[] = [];
if (input.medias && input.medias.length > 0) {
const media = input.medias[0];
const key = await this.createMedia(
media,
onStepUpdate,
mediaKeys = await this.uploadMedias(
input.medias,
input.onStepUpdate,
input.onFileUploadProgress,
input.timeOut,
);
mediaKeys.push(key);
}

void onStepUpdate(CreateSteps.UPLOADING_MOMENT);
if (mediaKeys.length > 0) {
await this.awaitForMediasProcessing(
mediaKeys,
input.onStepUpdate,
input.timeOut,
);
}

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,
});
void onStepUpdate(CreateSteps.FINISHED);

void input.onStepUpdate?.(CreateSteps.FINISHED);
return new Moment(
response.id,
response.author,
Expand All @@ -69,28 +64,70 @@ export class MomentsClient {
);
}

private async createMedia(
private async uploadMedias(
medias: CreateMedia[],
onStepUpdate?: (step: CreateSteps) => void | Promise<void>,
onFileUploadProgress?: (progress: number) => void | Promise<void>,
timeOut?: number,
): Promise<string[]> {
void onStepUpdate?.(CreateSteps.UPLOADING_MEDIA);
const mediaKeys: string[] = [];
const progressPerMedia = 1 / medias.length;
let progress = 0;

for (const media of medias) {
const mediaOnFileUploadProgress = (mediaProgress: number): void => {
const totalProgress = progressPerMedia * mediaProgress + progress;
void onFileUploadProgress?.(totalProgress);
};
const key = await this.uploadMedia(
media,
mediaOnFileUploadProgress,
timeOut,
);
mediaKeys.push(key);
progress += progressPerMedia;
void onFileUploadProgress?.(progress);
}

return mediaKeys;
}

private async awaitForMediasProcessing(
mediaKeys: string[],
onStepUpdate?: (step: CreateSteps) => void | Promise<void>,
timeOut?: number,
): Promise<void> {
void onStepUpdate?.(CreateSteps.PROCESSING_MEDIA);
const promises: Promise<void>[] = [];

for (const key of mediaKeys) {
promises.push(this.poapMomentsApi.waitForMediaProcessing(key, timeOut));
}

try {
await Promise.all(promises);
} catch (error) {
void onStepUpdate?.(CreateSteps.PROCESSING_MEDIA_ERROR);
throw error;
}
}

private async uploadMedia(
media: CreateMedia,
onStepUpdate: (step: CreateSteps) => void | Promise<void>,
onFileUploadProgress?: (progress: number) => void | Promise<void>,
timeOut?: number,
): Promise<string> {
void onStepUpdate(CreateSteps.REQUESTING_MEDIA_UPLOAD_URL);
const { url, key } = await this.poapMomentsApi.getSignedUrl();
void onStepUpdate(CreateSteps.UPLOADING_MEDIA);
await this.poapMomentsApi.uploadFile(
media.fileBinary,
url,
media.fileType,
onFileUploadProgress,
);
void onStepUpdate(CreateSteps.UPLOADING_MEDIA_METADATA);
// we will be adding metadata to the media in the future
void onStepUpdate(CreateSteps.PROCESSING_MEDIA);
try {
await this.poapMomentsApi.waitForMediaProcessing(key, timeOut);
} catch (error) {
void onStepUpdate(CreateSteps.PROCESSING_MEDIA_ERROR);
throw error;
}

Expand Down
2 changes: 0 additions & 2 deletions packages/moments/src/client/dtos/create/CreateSteps.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
export enum CreateSteps {
REQUESTING_MEDIA_UPLOAD_URL,
UPLOADING_MEDIA,
UPLOADING_MEDIA_METADATA,
PROCESSING_MEDIA,
PROCESSING_MEDIA_ERROR,
UPLOADING_MOMENT,
Expand Down
2 changes: 1 addition & 1 deletion packages/performance/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@poap-xyz/performance",
"version": "0.0.36",
"version": "0.0.37",
"description": "Performance module for the poap.js library",
"type": "module",
"main": "dist/cjs/index.cjs",
Expand Down
6 changes: 3 additions & 3 deletions packages/poaps/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@poap-xyz/poaps",
"version": "0.0.36",
"version": "0.0.37",
"description": "Poaps module for the poap.js library",
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.mjs",
Expand All @@ -26,7 +26,7 @@
"build": "rollup -c --bundleConfigAsCjs"
},
"dependencies": {
"@poap-xyz/providers": "0.0.36",
"@poap-xyz/utils": "0.0.36"
"@poap-xyz/providers": "0.0.37",
"@poap-xyz/utils": "0.0.37"
}
}
2 changes: 1 addition & 1 deletion packages/providers/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@poap-xyz/providers",
"version": "0.0.36",
"version": "0.0.37",
"description": "Providers module for the poap.js library",
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.mjs",
Expand Down
4 changes: 2 additions & 2 deletions packages/utils/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@poap-xyz/utils",
"version": "0.0.36",
"version": "0.0.37",
"description": "Utils module for the poap.js library",
"type": "module",
"main": "dist/cjs/index.cjs",
Expand All @@ -25,5 +25,5 @@
"scripts": {
"build": "rollup -c --bundleConfigAsCjs"
},
"stableVersion": "0.0.36"
"stableVersion": "0.0.37"
}
Loading

0 comments on commit 43b9dd4

Please sign in to comment.