Browse Source

fix(core): Do not crash if asset filesize is over max size limit

Fixes #990
Michael Bromley 4 years ago
parent
commit
b289cc8d95

+ 35 - 0
packages/core/e2e/asset.e2e-spec.ts

@@ -3,6 +3,7 @@ import { omit } from '@vendure/common/lib/omit';
 import { pick } from '@vendure/common/lib/pick';
 import { pick } from '@vendure/common/lib/pick';
 import { mergeConfig } from '@vendure/core';
 import { mergeConfig } from '@vendure/core';
 import { createTestEnvironment } from '@vendure/testing';
 import { createTestEnvironment } from '@vendure/testing';
+import fs from 'fs-extra';
 import path from 'path';
 import path from 'path';
 
 
 import { initialData } from '../../../e2e-common/e2e-initial-data';
 import { initialData } from '../../../e2e-common/e2e-initial-data';
@@ -326,6 +327,40 @@ describe('Asset resolver', () => {
                 },
                 },
             ]);
             ]);
         });
         });
+
+        // https://github.com/vendure-ecommerce/vendure/issues/990
+        it('errors if the filesize is too large', async () => {
+            /**
+             * Based on https://stackoverflow.com/a/49433633/772859
+             */
+            function createEmptyFileOfSize(fileName: string, sizeInBytes: number) {
+                return new Promise((resolve, reject) => {
+                    const fh = fs.openSync(fileName, 'w');
+                    fs.writeSync(fh, 'ok', Math.max(0, sizeInBytes - 2));
+                    fs.closeSync(fh);
+                    resolve(true);
+                });
+            }
+
+            const twentyOneMib = 22020096;
+            const filename = path.join(__dirname, 'fixtures/assets/temp_large_file.pdf');
+            await createEmptyFileOfSize(filename, twentyOneMib);
+
+            try {
+                const { createAssets }: CreateAssets.Mutation = await adminClient.fileUploadMutation({
+                    mutation: CREATE_ASSETS,
+                    filePaths: [filename],
+                    mapVariables: filePaths => ({
+                        input: filePaths.map(p => ({ file: null })),
+                    }),
+                });
+                fail('Should have thrown');
+            } catch (e) {
+                expect(e.message).toContain('File truncated as it exceeds the 20971520 byte size limit');
+            } finally {
+                fs.rmSync(filename);
+            }
+        });
     });
     });
 
 
     describe('filter by tags', () => {
     describe('filter by tags', () => {

+ 26 - 14
packages/core/src/service/services/asset.service.ts

@@ -235,20 +235,32 @@ export class AssetService {
      * Create an Asset based on a file uploaded via the GraphQL API.
      * Create an Asset based on a file uploaded via the GraphQL API.
      */
      */
     async create(ctx: RequestContext, input: CreateAssetInput): Promise<CreateAssetResult> {
     async create(ctx: RequestContext, input: CreateAssetInput): Promise<CreateAssetResult> {
-        const { createReadStream, filename, mimetype } = await input.file;
-        const stream = createReadStream();
-        const result = await this.createAssetInternal(ctx, stream, filename, mimetype, input.customFields);
-        if (isGraphQlErrorResult(result)) {
-            return result;
-        }
-        await this.customFieldRelationService.updateRelations(ctx, Asset, input, result);
-        if (input.tags) {
-            const tags = await this.tagService.valuesToTags(ctx, input.tags);
-            result.tags = tags;
-            await this.connection.getRepository(ctx, Asset).save(result);
-        }
-        this.eventBus.publish(new AssetEvent(ctx, result, 'created'));
-        return result;
+        return new Promise(async (resolve, reject) => {
+            const { createReadStream, filename, mimetype } = await input.file;
+            const stream = createReadStream();
+            stream.on('error', (err: any) => {
+                reject(err);
+            });
+            const result = await this.createAssetInternal(
+                ctx,
+                stream,
+                filename,
+                mimetype,
+                input.customFields,
+            );
+            if (isGraphQlErrorResult(result)) {
+                resolve(result);
+                return;
+            }
+            await this.customFieldRelationService.updateRelations(ctx, Asset, input, result);
+            if (input.tags) {
+                const tags = await this.tagService.valuesToTags(ctx, input.tags);
+                result.tags = tags;
+                await this.connection.getRepository(ctx, Asset).save(result);
+            }
+            this.eventBus.publish(new AssetEvent(ctx, result, 'created'));
+            resolve(result);
+        });
     }
     }
 
 
     async update(ctx: RequestContext, input: UpdateAssetInput): Promise<Asset> {
     async update(ctx: RequestContext, input: UpdateAssetInput): Promise<Asset> {