Skip to content

Commit

Permalink
BC-6041 - solution with promise
Browse files Browse the repository at this point in the history
  • Loading branch information
SevenWaysDP committed Jan 3, 2024
1 parent 031d12d commit 1d8ed40
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DeepMocked, createMock } from '@golevelup/ts-jest';
import { GetFile, S3ClientAdapter } from '@infra/s3-client';
import { UnprocessableEntityException } from '@nestjs/common';
import { InternalServerErrorException, UnprocessableEntityException } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { Logger } from '@src/core/logger';
import { Readable } from 'node:stream';
Expand Down Expand Up @@ -286,5 +286,44 @@ describe('PreviewGeneratorService', () => {
expect(resizeMock).not.toHaveBeenCalledTimes(1);
});
});

describe('WHEN stream throw an error', () => {
const setup = () => {
const params = {
originFilePath: 'file/test.jpeg',
previewFilePath: 'preview/text.webp',
previewOptions: {
format: 'webp',
},
};
const originFile = createFile(undefined, 'image/jpeg');
s3ClientAdapter.get.mockResolvedValueOnce(originFile);

const error = new Error('imagemagic is not found');
streamMock.mockReturnValueOnce({
on: jest.fn().mockImplementation((event: string, callback) => {
if (event === 'error') {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
callback(error);
}
}),
});

const expectedFileData = {
data: '',
mimeType: params.previewOptions.format,
};

return { params, originFile, expectedFileData };
};

it('should throw error', async () => {
const { params } = setup();

const error = new InternalServerErrorException('ImageMagick problem');

await expect(service.generatePreview(params)).rejects.toThrowError(error);
});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { GetFile, S3ClientAdapter } from '@infra/s3-client';
import { Injectable, UnprocessableEntityException } from '@nestjs/common';
import { Injectable, InternalServerErrorException, UnprocessableEntityException } from '@nestjs/common';
import { ErrorUtils } from '@src/core/error/utils';
import { Logger } from '@src/core/logger';
import { subClass } from 'gm';
import m, { subClass } from 'gm';
import { PassThrough } from 'stream';
import { PreviewFileOptions, PreviewInputMimeTypes, PreviewOptions, PreviewResponseMessage } from './interface';
import { PreviewActionsLoggable } from './loggable/preview-actions.loggable';
Expand All @@ -23,8 +24,7 @@ export class PreviewGeneratorService {

this.checkIfPreviewPossible(original, params);

const preview = this.resizeAndConvert(original, previewOptions);

const preview = await this.resizeAndConvert(original, previewOptions);
const file = PreviewGeneratorBuilder.buildFile(preview, params.previewOptions);

await this.storageClient.create(previewFilePath, file);
Expand Down Expand Up @@ -53,7 +53,7 @@ export class PreviewGeneratorService {
return file;
}

private resizeAndConvert(original: GetFile, previewParams: PreviewOptions): PassThrough {
private async resizeAndConvert(original: GetFile, previewParams: PreviewOptions): Promise<PassThrough> {
const { format, width } = previewParams;

const preview = this.imageMagick(original.data);
Expand All @@ -70,8 +70,51 @@ export class PreviewGeneratorService {
preview.resize(width, undefined, '>');
}

const result = preview.stream(format);
return this.convert(preview, format);
}

return result;
private convert(preview: m.State, format: string) {
const promise = new Promise<PassThrough>((resolve, reject) => {
preview.stream(format, (err, stdout, stderr) => {
if (err) {
stderr.emit('error', err);
}

const errorChunks: Array<Uint8Array> = [];

stdout.on('error', (error) => {
stderr.emit('error', error);
});

stderr.on('error', (error) => {
reject(
new InternalServerErrorException(
'CREATE_PREVIEW_NOT_POSSIBLE',
ErrorUtils.createHttpExceptionOptions(error)
)
);
});

stderr.on('data', (chunk: Uint8Array) => {
errorChunks.push(chunk);
});

stderr.on('end', () => {
let errorMessage = '';
Buffer.concat(errorChunks).forEach((chunk) => {
errorMessage += String.fromCharCode(chunk);
});
if (errorMessage !== '') {
stderr.emit('error', errorMessage);
} else {
const throughStream = new PassThrough();
stdout.pipe(throughStream);
resolve(throughStream);
}
});
});
});

return promise;
}
}

0 comments on commit 1d8ed40

Please sign in to comment.