From 573d328e2b1134b08bb9947279814037de8dbed5 Mon Sep 17 00:00:00 2001 From: Gustavo Perdomo Date: Tue, 23 Apr 2019 20:53:14 -0400 Subject: [PATCH 1/5] multer package Split multer code from platform-express to his own package --- gulpfile.js | 2 + packages/multer/Readme.md | 89 +++++++++++++++++++ .../multer/files.constants.ts | 0 packages/multer/index.ts | 5 ++ .../files-upload-module.interface.ts | 0 .../multer/interfaces/index.ts | 1 + .../interfaces/multer-options.interface.ts | 0 .../multer/multer.module.ts | 0 .../multer/multer/multer.constants.ts | 0 .../multer/multer/multer.utils.ts | 0 packages/multer/package.json | 19 ++++ .../test}/multer/multer.module.spec.ts | 4 +- .../test}/multer/multer.utils.spec.ts | 6 +- packages/multer/tsconfig.json | 7 ++ packages/platform-express/multer/index.ts | 2 - .../interceptors/file-fields.interceptor.ts | 12 +-- .../multer/interceptors/file.interceptor.ts | 14 +-- .../multer/interceptors/files.interceptor.ts | 14 +-- packages/platform-express/package.json | 3 +- packages/platform-fastify/index.ts | 1 + packages/platform-fastify/multer/index.ts | 1 + .../interceptors/file-fields.interceptor.ts | 64 +++++++++++++ .../multer/interceptors/file.interceptor.ts | 63 +++++++++++++ .../multer/interceptors/files.interceptor.ts | 64 +++++++++++++ .../multer/interceptors/index.ts | 3 + packages/platform-fastify/package.json | 5 +- .../file-fields.interceptor.spec.ts | 65 ++++++++++++++ .../interceptors/file.interceptor.spec.ts | 47 ++++++++++ .../interceptors/files.interceptor.spec.ts | 49 ++++++++++ 29 files changed, 513 insertions(+), 27 deletions(-) create mode 100644 packages/multer/Readme.md rename packages/{platform-express => }/multer/files.constants.ts (100%) create mode 100644 packages/multer/index.ts rename packages/{platform-express => }/multer/interfaces/files-upload-module.interface.ts (100%) rename packages/{platform-express => }/multer/interfaces/index.ts (52%) rename packages/{platform-express => }/multer/interfaces/multer-options.interface.ts (100%) rename packages/{platform-express => }/multer/multer.module.ts (100%) rename packages/{platform-express => }/multer/multer/multer.constants.ts (100%) rename packages/{platform-express => }/multer/multer/multer.utils.ts (100%) create mode 100644 packages/multer/package.json rename packages/{platform-express/test/multer => multer/test}/multer/multer.module.spec.ts (95%) rename packages/{platform-express/test/multer => multer/test}/multer/multer.utils.spec.ts (89%) create mode 100644 packages/multer/tsconfig.json create mode 100644 packages/platform-fastify/multer/index.ts create mode 100644 packages/platform-fastify/multer/interceptors/file-fields.interceptor.ts create mode 100644 packages/platform-fastify/multer/interceptors/file.interceptor.ts create mode 100644 packages/platform-fastify/multer/interceptors/files.interceptor.ts create mode 100644 packages/platform-fastify/multer/interceptors/index.ts create mode 100644 packages/platform-fastify/test/multer/interceptors/file-fields.interceptor.spec.ts create mode 100644 packages/platform-fastify/test/multer/interceptors/file.interceptor.spec.ts create mode 100644 packages/platform-fastify/test/multer/interceptors/files.interceptor.spec.ts diff --git a/gulpfile.js b/gulpfile.js index ff767809956..44cfbe58798 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -9,6 +9,7 @@ const deleteEmpty = require('delete-empty'); const packages = { common: ts.createProject('packages/common/tsconfig.json'), core: ts.createProject('packages/core/tsconfig.json'), + multer: ts.createProject('packages/multer/tsconfig.json'), microservices: ts.createProject('packages/microservices/tsconfig.json'), websockets: ts.createProject('packages/websockets/tsconfig.json'), testing: ts.createProject('packages/testing/tsconfig.json'), @@ -42,6 +43,7 @@ gulp.task('copy-misc', function() { .src(['Readme.md', 'LICENSE', '.npmignore']) .pipe(gulp.dest(`${source}/common`)) .pipe(gulp.dest(`${source}/core`)) + .pipe(gulp.dest(`${source}/multer`)) .pipe(gulp.dest(`${source}/microservices`)) .pipe(gulp.dest(`${source}/websockets`)) .pipe(gulp.dest(`${source}/testing`)) diff --git a/packages/multer/Readme.md b/packages/multer/Readme.md new file mode 100644 index 00000000000..f33512e474f --- /dev/null +++ b/packages/multer/Readme.md @@ -0,0 +1,89 @@ +

+ Nest Logo +

+ +[travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master +[travis-url]: https://travis-ci.org/nestjs/nest +[linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux +[linux-url]: https://travis-ci.org/nestjs/nest + +

A progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.

+

+NPM Version +Package License +NPM Downloads +Travis +Linux +Coverage +Gitter +Discord +Backers on Open Collective +Sponsors on Open Collective + + +

+ + +## Description + +Nest is a framework for building efficient, scalable Node.js server-side applications. It uses modern JavaScript, is built with TypeScript (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming). + +

Under the hood, Nest makes use of Express, but also, provides compatibility with a wide range of other libraries, like e.g. Fastify, allowing for easy use of the myriad third-party plugins which are available.

+ +## Philosophy + +

In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front and backend applications, giving rise to awesome projects like Angular, React and Vue which improve developer productivity and enable the construction of fast, testable, extensible frontend applications. However, on the server-side, while there are a lot of superb libraries, helpers and tools for Node, none of them effectively solve the main problem - the architecture.

+

Nest aims to provide an application architecture out of the box which allows for effortless creation of highly testable, scalable, loosely coupled and easily maintainable applications.

+ +## Getting started + +* To check out the [guide](https://docs.nestjs.com), visit [docs.nestjs.com](https://docs.nestjs.com). :books: +* 要查看中文 [指南](readme_zh.md), 请访问 [docs.nestjs.cn](https://docs.nestjs.cn). :books: + +## Consulting + +With official support, you can get expert help straight from Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://docs.nestjs.com/enterprise). + +## Support + +Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). + +#### Principal Sponsor + + + +#### Base Sponsor + +   + + +#### Silver Sponsors +   +       +   + + + +#### Sponsors + +               + +   +   + + + +## Backers + + + +## Stay in touch + +* Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) +* Website - [https://nestjs.com](https://nestjs.com/) +* Twitter - [@nestframework](https://twitter.com/nestframework) + +## License + +Nest is [MIT licensed](LICENSE). diff --git a/packages/platform-express/multer/files.constants.ts b/packages/multer/files.constants.ts similarity index 100% rename from packages/platform-express/multer/files.constants.ts rename to packages/multer/files.constants.ts diff --git a/packages/multer/index.ts b/packages/multer/index.ts new file mode 100644 index 00000000000..bf56c90cd95 --- /dev/null +++ b/packages/multer/index.ts @@ -0,0 +1,5 @@ +export * from './files.constants'; +export * from './interfaces'; +export * from './multer.module'; +export * from './multer/multer.constants'; +export * from './multer/multer.utils'; diff --git a/packages/platform-express/multer/interfaces/files-upload-module.interface.ts b/packages/multer/interfaces/files-upload-module.interface.ts similarity index 100% rename from packages/platform-express/multer/interfaces/files-upload-module.interface.ts rename to packages/multer/interfaces/files-upload-module.interface.ts diff --git a/packages/platform-express/multer/interfaces/index.ts b/packages/multer/interfaces/index.ts similarity index 52% rename from packages/platform-express/multer/interfaces/index.ts rename to packages/multer/interfaces/index.ts index be94ec24afd..fe2246c78d6 100644 --- a/packages/platform-express/multer/interfaces/index.ts +++ b/packages/multer/interfaces/index.ts @@ -1 +1,2 @@ export * from './files-upload-module.interface'; +export * from './multer-options.interface'; diff --git a/packages/platform-express/multer/interfaces/multer-options.interface.ts b/packages/multer/interfaces/multer-options.interface.ts similarity index 100% rename from packages/platform-express/multer/interfaces/multer-options.interface.ts rename to packages/multer/interfaces/multer-options.interface.ts diff --git a/packages/platform-express/multer/multer.module.ts b/packages/multer/multer.module.ts similarity index 100% rename from packages/platform-express/multer/multer.module.ts rename to packages/multer/multer.module.ts diff --git a/packages/platform-express/multer/multer/multer.constants.ts b/packages/multer/multer/multer.constants.ts similarity index 100% rename from packages/platform-express/multer/multer/multer.constants.ts rename to packages/multer/multer/multer.constants.ts diff --git a/packages/platform-express/multer/multer/multer.utils.ts b/packages/multer/multer/multer.utils.ts similarity index 100% rename from packages/platform-express/multer/multer/multer.utils.ts rename to packages/multer/multer/multer.utils.ts diff --git a/packages/multer/package.json b/packages/multer/package.json new file mode 100644 index 00000000000..989ac29f379 --- /dev/null +++ b/packages/multer/package.json @@ -0,0 +1,19 @@ +{ + "name": "@nestjs/multer", + "version": "6.1.1", + "description": "...", + "author": "Kamil Mysliwiec", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/nestjs/nest" + }, + "publishConfig": { + "access": "public" + }, + "dependencies": {}, + "peerDependencies": { + "@nestjs/common": "^6.0.0", + "@nestjs/core": "^6.0.0" + } +} diff --git a/packages/platform-express/test/multer/multer/multer.module.spec.ts b/packages/multer/test/multer/multer.module.spec.ts similarity index 95% rename from packages/platform-express/test/multer/multer/multer.module.spec.ts rename to packages/multer/test/multer/multer.module.spec.ts index 759110a927e..94d7621d855 100644 --- a/packages/platform-express/test/multer/multer/multer.module.spec.ts +++ b/packages/multer/test/multer/multer.module.spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; -import { MULTER_MODULE_OPTIONS } from '../../../multer/files.constants'; -import { MulterModule } from '../../../multer/multer.module'; +import { MULTER_MODULE_OPTIONS } from '../../files.constants'; +import { MulterModule } from '../../multer.module'; describe('MulterModule', () => { describe('register', () => { diff --git a/packages/platform-express/test/multer/multer/multer.utils.spec.ts b/packages/multer/test/multer/multer.utils.spec.ts similarity index 89% rename from packages/platform-express/test/multer/multer/multer.utils.spec.ts rename to packages/multer/test/multer/multer.utils.spec.ts index 7ad1eec9069..8486ffb84e8 100644 --- a/packages/platform-express/test/multer/multer/multer.utils.spec.ts +++ b/packages/multer/test/multer/multer.utils.spec.ts @@ -1,11 +1,11 @@ +import { expect } from 'chai'; +import { multerExceptions } from '../../multer/multer.constants'; +import { transformException } from '../../multer/multer.utils'; import { BadRequestException, HttpException, PayloadTooLargeException, } from '@nestjs/common'; -import { expect } from 'chai'; -import { multerExceptions } from '../../../multer/multer/multer.constants'; -import { transformException } from '../../../multer/multer/multer.utils'; describe('transformException', () => { describe('if error does not exist', () => { diff --git a/packages/multer/tsconfig.json b/packages/multer/tsconfig.json new file mode 100644 index 00000000000..b238080a42e --- /dev/null +++ b/packages/multer/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "./../tsconfig.base.json", + "compilerOptions": { + "baseUrl": "./" + }, + "include": ["*.ts", "**/*.ts"] +} diff --git a/packages/platform-express/multer/index.ts b/packages/platform-express/multer/index.ts index 7afd4aab899..34856f47c1c 100644 --- a/packages/platform-express/multer/index.ts +++ b/packages/platform-express/multer/index.ts @@ -1,3 +1 @@ export * from './interceptors'; -export * from './interfaces'; -export * from './multer.module'; diff --git a/packages/platform-express/multer/interceptors/file-fields.interceptor.ts b/packages/platform-express/multer/interceptors/file-fields.interceptor.ts index 535efa7429f..4de15eb41b6 100644 --- a/packages/platform-express/multer/interceptors/file-fields.interceptor.ts +++ b/packages/platform-express/multer/interceptors/file-fields.interceptor.ts @@ -1,3 +1,5 @@ +import * as multer from 'multer'; +import { Observable } from 'rxjs'; import { CallHandler, ExecutionContext, @@ -7,15 +9,13 @@ import { Optional, Type, } from '@nestjs/common'; -import * as multer from 'multer'; -import { Observable } from 'rxjs'; -import { MULTER_MODULE_OPTIONS } from '../files.constants'; -import { MulterModuleOptions } from '../interfaces'; import { + transformException, MulterField, + MulterModuleOptions, + MULTER_MODULE_OPTIONS, MulterOptions, -} from '../interfaces/multer-options.interface'; -import { transformException } from '../multer/multer.utils'; +} from '@nestjs/multer'; type MulterInstance = any; diff --git a/packages/platform-express/multer/interceptors/file.interceptor.ts b/packages/platform-express/multer/interceptors/file.interceptor.ts index 450ae2aecf1..f025fcaa2cd 100644 --- a/packages/platform-express/multer/interceptors/file.interceptor.ts +++ b/packages/platform-express/multer/interceptors/file.interceptor.ts @@ -1,3 +1,5 @@ +import * as multer from 'multer'; +import { Observable } from 'rxjs'; import { CallHandler, ExecutionContext, @@ -7,12 +9,12 @@ import { Optional, Type, } from '@nestjs/common'; -import * as multer from 'multer'; -import { Observable } from 'rxjs'; -import { MULTER_MODULE_OPTIONS } from '../files.constants'; -import { MulterModuleOptions } from '../interfaces'; -import { MulterOptions } from '../interfaces/multer-options.interface'; -import { transformException } from '../multer/multer.utils'; +import { + MulterOptions, + MULTER_MODULE_OPTIONS, + MulterModuleOptions, + transformException, +} from '@nestjs/multer'; type MulterInstance = any; diff --git a/packages/platform-express/multer/interceptors/files.interceptor.ts b/packages/platform-express/multer/interceptors/files.interceptor.ts index 5440a944924..712ea1d84d7 100644 --- a/packages/platform-express/multer/interceptors/files.interceptor.ts +++ b/packages/platform-express/multer/interceptors/files.interceptor.ts @@ -1,3 +1,5 @@ +import * as multer from 'multer'; +import { Observable } from 'rxjs'; import { CallHandler, ExecutionContext, @@ -7,12 +9,12 @@ import { Optional, Type, } from '@nestjs/common'; -import * as multer from 'multer'; -import { Observable } from 'rxjs'; -import { MULTER_MODULE_OPTIONS } from '../files.constants'; -import { MulterModuleOptions } from '../interfaces'; -import { MulterOptions } from '../interfaces/multer-options.interface'; -import { transformException } from '../multer/multer.utils'; +import { + MulterOptions, + MULTER_MODULE_OPTIONS, + MulterModuleOptions, + transformException, +} from '@nestjs/multer'; type MulterInstance = any; diff --git a/packages/platform-express/package.json b/packages/platform-express/package.json index de787ce5f23..b39e1171b87 100644 --- a/packages/platform-express/package.json +++ b/packages/platform-express/package.json @@ -19,6 +19,7 @@ }, "peerDependencies": { "@nestjs/common": "^6.0.0", - "@nestjs/core": "^6.0.0" + "@nestjs/core": "^6.0.0", + "@nestjs/multer": "^6.0.0" } } diff --git a/packages/platform-fastify/index.ts b/packages/platform-fastify/index.ts index 277f7c0eaf8..2b00533258f 100644 --- a/packages/platform-fastify/index.ts +++ b/packages/platform-fastify/index.ts @@ -7,3 +7,4 @@ export * from './adapters'; export * from './interfaces'; +export * from './multer'; diff --git a/packages/platform-fastify/multer/index.ts b/packages/platform-fastify/multer/index.ts new file mode 100644 index 00000000000..34856f47c1c --- /dev/null +++ b/packages/platform-fastify/multer/index.ts @@ -0,0 +1 @@ +export * from './interceptors'; diff --git a/packages/platform-fastify/multer/interceptors/file-fields.interceptor.ts b/packages/platform-fastify/multer/interceptors/file-fields.interceptor.ts new file mode 100644 index 00000000000..27b1052213b --- /dev/null +++ b/packages/platform-fastify/multer/interceptors/file-fields.interceptor.ts @@ -0,0 +1,64 @@ +import * as multer from 'fastify-multer'; +import { Observable } from 'rxjs'; +import { + CallHandler, + ExecutionContext, + Inject, + mixin, + NestInterceptor, + Optional, + Type, +} from '@nestjs/common'; +import { + MulterField, + MulterOptions, + MULTER_MODULE_OPTIONS, + MulterModuleOptions, + transformException, +} from '@nestjs/multer'; + +type MulterInstance = any; + +export function FileFieldsInterceptor( + uploadFields: MulterField[], + localOptions?: MulterOptions, +): Type { + class MixinInterceptor implements NestInterceptor { + protected multer: MulterInstance; + + constructor( + @Optional() + @Inject(MULTER_MODULE_OPTIONS) + options: MulterModuleOptions = {}, + ) { + this.multer = (multer as any)({ + ...options, + ...localOptions, + }); + } + + async intercept( + context: ExecutionContext, + next: CallHandler, + ): Promise> { + const ctx = context.switchToHttp(); + + await new Promise((resolve, reject) => + this.multer.fields(uploadFields)( + ctx.getRequest(), + ctx.getResponse(), + (err: any) => { + if (err) { + const error = transformException(err); + return reject(error); + } + resolve(); + }, + ), + ); + return next.handle(); + } + } + const Interceptor = mixin(MixinInterceptor); + return Interceptor as Type; +} diff --git a/packages/platform-fastify/multer/interceptors/file.interceptor.ts b/packages/platform-fastify/multer/interceptors/file.interceptor.ts new file mode 100644 index 00000000000..dfe77d5bc34 --- /dev/null +++ b/packages/platform-fastify/multer/interceptors/file.interceptor.ts @@ -0,0 +1,63 @@ +import * as multer from 'fastify-multer'; +import { Observable } from 'rxjs'; +import { + MulterOptions, + MULTER_MODULE_OPTIONS, + MulterModuleOptions, + transformException, +} from '@nestjs/multer'; +import { + CallHandler, + ExecutionContext, + Inject, + mixin, + NestInterceptor, + Optional, + Type, +} from '@nestjs/common'; + +type MulterInstance = any; + +export function FileInterceptor( + fieldName: string, + localOptions?: MulterOptions, +): Type { + class MixinInterceptor implements NestInterceptor { + protected multer: MulterInstance; + + constructor( + @Optional() + @Inject(MULTER_MODULE_OPTIONS) + options: MulterModuleOptions = {}, + ) { + this.multer = (multer as any)({ + ...options, + ...localOptions, + }); + } + + async intercept( + context: ExecutionContext, + next: CallHandler, + ): Promise> { + const ctx = context.switchToHttp(); + + await new Promise((resolve, reject) => + this.multer.single(fieldName)( + ctx.getRequest(), + ctx.getResponse(), + (err: any) => { + if (err) { + const error = transformException(err); + return reject(error); + } + resolve(); + }, + ), + ); + return next.handle(); + } + } + const Interceptor = mixin(MixinInterceptor); + return Interceptor as Type; +} diff --git a/packages/platform-fastify/multer/interceptors/files.interceptor.ts b/packages/platform-fastify/multer/interceptors/files.interceptor.ts new file mode 100644 index 00000000000..175bfa21757 --- /dev/null +++ b/packages/platform-fastify/multer/interceptors/files.interceptor.ts @@ -0,0 +1,64 @@ +import * as multer from 'fastify-multer'; +import { Observable } from 'rxjs'; +import { + CallHandler, + ExecutionContext, + Inject, + mixin, + NestInterceptor, + Optional, + Type, +} from '@nestjs/common'; +import { + MulterOptions, + MULTER_MODULE_OPTIONS, + MulterModuleOptions, + transformException, +} from '@nestjs/multer'; + +type MulterInstance = any; + +export function FilesInterceptor( + fieldName: string, + maxCount?: number, + localOptions?: MulterOptions, +): Type { + class MixinInterceptor implements NestInterceptor { + protected multer: MulterInstance; + + constructor( + @Optional() + @Inject(MULTER_MODULE_OPTIONS) + options: MulterModuleOptions = {}, + ) { + this.multer = (multer as any)({ + ...options, + ...localOptions, + }); + } + + async intercept( + context: ExecutionContext, + next: CallHandler, + ): Promise> { + const ctx = context.switchToHttp(); + + await new Promise((resolve, reject) => + this.multer.array(fieldName, maxCount)( + ctx.getRequest(), + ctx.getResponse(), + (err: any) => { + if (err) { + const error = transformException(err); + return reject(error); + } + resolve(); + }, + ), + ); + return next.handle(); + } + } + const Interceptor = mixin(MixinInterceptor); + return Interceptor as Type; +} diff --git a/packages/platform-fastify/multer/interceptors/index.ts b/packages/platform-fastify/multer/interceptors/index.ts new file mode 100644 index 00000000000..3de0e24ce6d --- /dev/null +++ b/packages/platform-fastify/multer/interceptors/index.ts @@ -0,0 +1,3 @@ +export * from './file-fields.interceptor'; +export * from './file.interceptor'; +export * from './files.interceptor'; diff --git a/packages/platform-fastify/package.json b/packages/platform-fastify/package.json index c4851c49417..0d8654a9619 100644 --- a/packages/platform-fastify/package.json +++ b/packages/platform-fastify/package.json @@ -15,10 +15,13 @@ "fastify": "2.3.0", "fastify-cors": "2.1.2", "fastify-formbody": "3.1.0", + "fastify-multer": "^1.4.2", + "fastify-multipart": "^0.8.1", "path-to-regexp": "3.0.0" }, "peerDependencies": { "@nestjs/common": "^6.0.0", - "@nestjs/core": "^6.0.0" + "@nestjs/core": "^6.0.0", + "@nestjs/multer": "^6.0.0" } } diff --git a/packages/platform-fastify/test/multer/interceptors/file-fields.interceptor.spec.ts b/packages/platform-fastify/test/multer/interceptors/file-fields.interceptor.spec.ts new file mode 100644 index 00000000000..7ab6fea50df --- /dev/null +++ b/packages/platform-fastify/test/multer/interceptors/file-fields.interceptor.spec.ts @@ -0,0 +1,65 @@ +import { CallHandler } from '@nestjs/common'; +import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; +import { expect } from 'chai'; +import { of } from 'rxjs'; +import * as sinon from 'sinon'; +import { FileFieldsInterceptor } from '../../../multer/interceptors/file-fields.interceptor'; + +describe('FileFieldsInterceptor', () => { + it('should return metatype with expected structure', async () => { + const targetClass = FileFieldsInterceptor([ + { name: 'file', maxCount: 1 }, + { name: 'anotherFile', maxCount: 1 }, + ]); + expect(targetClass.prototype.intercept).to.not.be.undefined; + }); + describe('intercept', () => { + let handler: CallHandler; + beforeEach(() => { + handler = { + handle: () => of('test'), + }; + }); + it('should call object with expected params', async () => { + const fieldName1 = 'file'; + const maxCount1 = 1; + const fieldName2 = 'anotherFile'; + const maxCount2 = 2; + const argument = [ + { name: fieldName1, maxCount: maxCount1 }, + { name: fieldName2, maxCount: maxCount2 }, + ]; + const target = new (FileFieldsInterceptor(argument))(); + + const callback = (req, res, next) => next(); + const fieldsSpy = sinon + .stub((target as any).multer, 'fields') + .returns(callback); + + await target.intercept(new ExecutionContextHost([]), handler); + + expect(fieldsSpy.called).to.be.true; + expect(fieldsSpy.calledWith(argument)).to.be.true; + }); + it('should transform exception', async () => { + const fieldName1 = 'file'; + const maxCount1 = 1; + const fieldName2 = 'anotherFile'; + const maxCount2 = 2; + const argument = [ + { name: fieldName1, maxCount: maxCount1 }, + { name: fieldName2, maxCount: maxCount2 }, + ]; + const target = new (FileFieldsInterceptor(argument))(); + const err = {}; + const callback = (req, res, next) => next(err); + + (target as any).fields = { + array: () => callback, + }; + (target.intercept(new ExecutionContextHost([]), handler) as any).catch( + error => expect(error).to.not.be.undefined, + ); + }); + }); +}); diff --git a/packages/platform-fastify/test/multer/interceptors/file.interceptor.spec.ts b/packages/platform-fastify/test/multer/interceptors/file.interceptor.spec.ts new file mode 100644 index 00000000000..7f4178db44e --- /dev/null +++ b/packages/platform-fastify/test/multer/interceptors/file.interceptor.spec.ts @@ -0,0 +1,47 @@ +import { CallHandler } from '@nestjs/common'; +import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; +import { expect } from 'chai'; +import { of } from 'rxjs'; +import * as sinon from 'sinon'; +import { FileInterceptor } from '../../../multer/interceptors/file.interceptor'; + +describe('FileInterceptor', () => { + it('should return metatype with expected structure', async () => { + const targetClass = FileInterceptor('file'); + expect(targetClass.prototype.intercept).to.not.be.undefined; + }); + describe('intercept', () => { + let handler: CallHandler; + beforeEach(() => { + handler = { + handle: () => of('test'), + }; + }); + it('should call single() with expected params', async () => { + const fieldName = 'file'; + const target = new (FileInterceptor(fieldName))(); + const callback = (req, res, next) => next(); + const singleSpy = sinon + .stub((target as any).multer, 'single') + .returns(callback); + + await target.intercept(new ExecutionContextHost([]), handler); + + expect(singleSpy.called).to.be.true; + expect(singleSpy.calledWith(fieldName)).to.be.true; + }); + it('should transform exception', async () => { + const fieldName = 'file'; + const target = new (FileInterceptor(fieldName))(); + const err = {}; + const callback = (req, res, next) => next(err); + + (target as any).multer = { + single: () => callback, + }; + (target.intercept(new ExecutionContextHost([]), handler) as any).catch( + error => expect(error).to.not.be.undefined, + ); + }); + }); +}); diff --git a/packages/platform-fastify/test/multer/interceptors/files.interceptor.spec.ts b/packages/platform-fastify/test/multer/interceptors/files.interceptor.spec.ts new file mode 100644 index 00000000000..c6caadd62aa --- /dev/null +++ b/packages/platform-fastify/test/multer/interceptors/files.interceptor.spec.ts @@ -0,0 +1,49 @@ +import { CallHandler } from '@nestjs/common'; +import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; +import { expect } from 'chai'; +import { of } from 'rxjs'; +import * as sinon from 'sinon'; +import { FilesInterceptor } from '../../../multer/interceptors/files.interceptor'; + +describe('FilesInterceptor', () => { + it('should return metatype with expected structure', async () => { + const targetClass = FilesInterceptor('file'); + expect(targetClass.prototype.intercept).to.not.be.undefined; + }); + describe('intercept', () => { + let handler: CallHandler; + beforeEach(() => { + handler = { + handle: () => of('test'), + }; + }); + it('should call array() with expected params', async () => { + const fieldName = 'file'; + const maxCount = 10; + const target = new (FilesInterceptor(fieldName, maxCount))(); + + const callback = (req, res, next) => next(); + const arraySpy = sinon + .stub((target as any).multer, 'array') + .returns(callback); + + await target.intercept(new ExecutionContextHost([]), handler); + + expect(arraySpy.called).to.be.true; + expect(arraySpy.calledWith(fieldName, maxCount)).to.be.true; + }); + it('should transform exception', async () => { + const fieldName = 'file'; + const target = new (FilesInterceptor(fieldName))(); + const err = {}; + const callback = (req, res, next) => next(err); + + (target as any).multer = { + array: () => callback, + }; + (target.intercept(new ExecutionContextHost([]), handler) as any).catch( + error => expect(error).to.not.be.undefined, + ); + }); + }); +}); From f30c06e2d2ba9f18fe57bc8769145760d9527a7f Mon Sep 17 00:00:00 2001 From: Gustavo Perdomo Date: Tue, 23 Apr 2019 21:11:39 -0400 Subject: [PATCH 2/5] update --- packages/multer/{multer/multer.constants.ts => constants.ts} | 2 ++ packages/multer/files.constants.ts | 1 - packages/multer/index.ts | 5 ++--- packages/multer/multer.module.ts | 2 +- packages/multer/test/multer/multer.module.spec.ts | 2 +- packages/multer/test/multer/multer.utils.spec.ts | 4 ++-- packages/multer/utils/index.ts | 1 + .../multer.utils.ts => utils/transform-exception.util.ts} | 2 +- 8 files changed, 10 insertions(+), 9 deletions(-) rename packages/multer/{multer/multer.constants.ts => constants.ts} (83%) delete mode 100644 packages/multer/files.constants.ts create mode 100644 packages/multer/utils/index.ts rename packages/multer/{multer/multer.utils.ts => utils/transform-exception.util.ts} (92%) diff --git a/packages/multer/multer/multer.constants.ts b/packages/multer/constants.ts similarity index 83% rename from packages/multer/multer/multer.constants.ts rename to packages/multer/constants.ts index e3c7b4557f2..c875f454fa3 100644 --- a/packages/multer/multer/multer.constants.ts +++ b/packages/multer/constants.ts @@ -1,3 +1,5 @@ +export const MULTER_MODULE_OPTIONS = 'MULTER_MODULE_OPTIONS'; + export const multerExceptions = { LIMIT_PART_COUNT: 'Too many parts', LIMIT_FILE_SIZE: 'File too large', diff --git a/packages/multer/files.constants.ts b/packages/multer/files.constants.ts deleted file mode 100644 index fd2ef8091cc..00000000000 --- a/packages/multer/files.constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const MULTER_MODULE_OPTIONS = 'MULTER_MODULE_OPTIONS'; diff --git a/packages/multer/index.ts b/packages/multer/index.ts index bf56c90cd95..d4f283b7a1f 100644 --- a/packages/multer/index.ts +++ b/packages/multer/index.ts @@ -1,5 +1,4 @@ -export * from './files.constants'; +export * from './constants'; export * from './interfaces'; export * from './multer.module'; -export * from './multer/multer.constants'; -export * from './multer/multer.utils'; +export * from './utils'; diff --git a/packages/multer/multer.module.ts b/packages/multer/multer.module.ts index 5d28a99e867..f9e97f58d27 100644 --- a/packages/multer/multer.module.ts +++ b/packages/multer/multer.module.ts @@ -1,5 +1,5 @@ import { DynamicModule, Module, Provider } from '@nestjs/common'; -import { MULTER_MODULE_OPTIONS } from './files.constants'; +import { MULTER_MODULE_OPTIONS } from './constants'; import { MulterModuleAsyncOptions, MulterModuleOptions, diff --git a/packages/multer/test/multer/multer.module.spec.ts b/packages/multer/test/multer/multer.module.spec.ts index 94d7621d855..11d6fdfa6cf 100644 --- a/packages/multer/test/multer/multer.module.spec.ts +++ b/packages/multer/test/multer/multer.module.spec.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; -import { MULTER_MODULE_OPTIONS } from '../../files.constants'; +import { MULTER_MODULE_OPTIONS } from '../../constants'; import { MulterModule } from '../../multer.module'; describe('MulterModule', () => { diff --git a/packages/multer/test/multer/multer.utils.spec.ts b/packages/multer/test/multer/multer.utils.spec.ts index 8486ffb84e8..ddd2cd9da66 100644 --- a/packages/multer/test/multer/multer.utils.spec.ts +++ b/packages/multer/test/multer/multer.utils.spec.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; -import { multerExceptions } from '../../multer/multer.constants'; -import { transformException } from '../../multer/multer.utils'; +import { multerExceptions } from '../../constants'; +import { transformException } from '../../utils'; import { BadRequestException, HttpException, diff --git a/packages/multer/utils/index.ts b/packages/multer/utils/index.ts new file mode 100644 index 00000000000..6771918e7f6 --- /dev/null +++ b/packages/multer/utils/index.ts @@ -0,0 +1 @@ +export * from './transform-exception.util'; diff --git a/packages/multer/multer/multer.utils.ts b/packages/multer/utils/transform-exception.util.ts similarity index 92% rename from packages/multer/multer/multer.utils.ts rename to packages/multer/utils/transform-exception.util.ts index 81e19739ca9..1ab0e2c7d56 100644 --- a/packages/multer/multer/multer.utils.ts +++ b/packages/multer/utils/transform-exception.util.ts @@ -1,9 +1,9 @@ +import { multerExceptions } from '../constants'; import { BadRequestException, HttpException, PayloadTooLargeException, } from '@nestjs/common'; -import { multerExceptions } from './multer.constants'; export function transformException(error: Error | undefined) { if (!error || error instanceof HttpException) { From 416cfa4a574ff70022a63cfb32e49c9d5b325325 Mon Sep 17 00:00:00 2001 From: Gustavo Perdomo Date: Tue, 23 Apr 2019 23:01:52 -0400 Subject: [PATCH 3/5] multer with adapter --- packages/multer/constants.ts | 1 + packages/multer/index.ts | 1 + .../interceptors/file-fields.interceptor.ts | 15 ++--- .../multer/interceptors/file.interceptor.ts | 14 ++-- .../multer/interceptors/files.interceptor.ts | 14 ++-- .../multer/interceptors/index.ts | 0 packages/multer/multer.module.ts | 41 +++++++++--- packages/multer/test/multer/fake-multer.ts | 7 ++ .../file-fields.interceptor.spec.ts | 7 +- .../interceptors/file.interceptor.spec.ts | 10 ++- .../interceptors/files.interceptor.spec.ts | 10 ++- .../multer/test/multer/multer.module.spec.ts | 60 ++++++++++++++--- packages/platform-express/index.ts | 1 - packages/platform-express/multer/index.ts | 1 - .../interceptors/file-fields.interceptor.ts | 64 ------------------ .../multer/interceptors/file.interceptor.ts | 63 ------------------ .../multer/interceptors/files.interceptor.ts | 64 ------------------ .../interceptors/file.interceptor.spec.ts | 47 -------------- packages/platform-fastify/index.ts | 1 - packages/platform-fastify/multer/index.ts | 1 - .../multer/interceptors/index.ts | 3 - .../file-fields.interceptor.spec.ts | 65 ------------------- .../interceptors/files.interceptor.spec.ts | 49 -------------- 23 files changed, 127 insertions(+), 412 deletions(-) rename packages/{platform-fastify => }/multer/interceptors/file-fields.interceptor.ts (79%) rename packages/{platform-fastify => }/multer/interceptors/file.interceptor.ts (80%) rename packages/{platform-fastify => }/multer/interceptors/files.interceptor.ts (80%) rename packages/{platform-express => }/multer/interceptors/index.ts (100%) create mode 100644 packages/multer/test/multer/fake-multer.ts rename packages/{platform-express => multer}/test/multer/interceptors/file-fields.interceptor.spec.ts (87%) rename packages/{platform-fastify => multer}/test/multer/interceptors/file.interceptor.spec.ts (84%) rename packages/{platform-express => multer}/test/multer/interceptors/files.interceptor.spec.ts (86%) delete mode 100644 packages/platform-express/multer/index.ts delete mode 100644 packages/platform-express/multer/interceptors/file-fields.interceptor.ts delete mode 100644 packages/platform-express/multer/interceptors/file.interceptor.ts delete mode 100644 packages/platform-express/multer/interceptors/files.interceptor.ts delete mode 100644 packages/platform-express/test/multer/interceptors/file.interceptor.spec.ts delete mode 100644 packages/platform-fastify/multer/index.ts delete mode 100644 packages/platform-fastify/multer/interceptors/index.ts delete mode 100644 packages/platform-fastify/test/multer/interceptors/file-fields.interceptor.spec.ts delete mode 100644 packages/platform-fastify/test/multer/interceptors/files.interceptor.spec.ts diff --git a/packages/multer/constants.ts b/packages/multer/constants.ts index c875f454fa3..5e7d6314e09 100644 --- a/packages/multer/constants.ts +++ b/packages/multer/constants.ts @@ -1,4 +1,5 @@ export const MULTER_MODULE_OPTIONS = 'MULTER_MODULE_OPTIONS'; +export const MULTER_MODULE_ADAPTER = 'MULTER_MODULE_ADAPTER'; export const multerExceptions = { LIMIT_PART_COUNT: 'Too many parts', diff --git a/packages/multer/index.ts b/packages/multer/index.ts index d4f283b7a1f..1c4ea39a36f 100644 --- a/packages/multer/index.ts +++ b/packages/multer/index.ts @@ -2,3 +2,4 @@ export * from './constants'; export * from './interfaces'; export * from './multer.module'; export * from './utils'; +export * from './interceptors'; diff --git a/packages/platform-fastify/multer/interceptors/file-fields.interceptor.ts b/packages/multer/interceptors/file-fields.interceptor.ts similarity index 79% rename from packages/platform-fastify/multer/interceptors/file-fields.interceptor.ts rename to packages/multer/interceptors/file-fields.interceptor.ts index 27b1052213b..bb6b6a4a472 100644 --- a/packages/platform-fastify/multer/interceptors/file-fields.interceptor.ts +++ b/packages/multer/interceptors/file-fields.interceptor.ts @@ -1,5 +1,7 @@ -import * as multer from 'fastify-multer'; import { Observable } from 'rxjs'; +import { MULTER_MODULE_ADAPTER, MULTER_MODULE_OPTIONS } from '../constants'; +import { MulterField, MulterModuleOptions, MulterOptions } from '../interfaces'; +import { transformException } from '../utils'; import { CallHandler, ExecutionContext, @@ -9,13 +11,6 @@ import { Optional, Type, } from '@nestjs/common'; -import { - MulterField, - MulterOptions, - MULTER_MODULE_OPTIONS, - MulterModuleOptions, - transformException, -} from '@nestjs/multer'; type MulterInstance = any; @@ -30,8 +25,10 @@ export function FileFieldsInterceptor( @Optional() @Inject(MULTER_MODULE_OPTIONS) options: MulterModuleOptions = {}, + @Inject(MULTER_MODULE_ADAPTER) + adapter: MulterInstance, ) { - this.multer = (multer as any)({ + this.multer = (adapter as any)({ ...options, ...localOptions, }); diff --git a/packages/platform-fastify/multer/interceptors/file.interceptor.ts b/packages/multer/interceptors/file.interceptor.ts similarity index 80% rename from packages/platform-fastify/multer/interceptors/file.interceptor.ts rename to packages/multer/interceptors/file.interceptor.ts index dfe77d5bc34..93b56179b43 100644 --- a/packages/platform-fastify/multer/interceptors/file.interceptor.ts +++ b/packages/multer/interceptors/file.interceptor.ts @@ -1,11 +1,7 @@ -import * as multer from 'fastify-multer'; import { Observable } from 'rxjs'; -import { - MulterOptions, - MULTER_MODULE_OPTIONS, - MulterModuleOptions, - transformException, -} from '@nestjs/multer'; +import { MULTER_MODULE_ADAPTER, MULTER_MODULE_OPTIONS } from '../constants'; +import { MulterModuleOptions, MulterOptions } from '../interfaces'; +import { transformException } from '../utils'; import { CallHandler, ExecutionContext, @@ -29,8 +25,10 @@ export function FileInterceptor( @Optional() @Inject(MULTER_MODULE_OPTIONS) options: MulterModuleOptions = {}, + @Inject(MULTER_MODULE_ADAPTER) + adapter: MulterInstance, ) { - this.multer = (multer as any)({ + this.multer = (adapter as any)({ ...options, ...localOptions, }); diff --git a/packages/platform-fastify/multer/interceptors/files.interceptor.ts b/packages/multer/interceptors/files.interceptor.ts similarity index 80% rename from packages/platform-fastify/multer/interceptors/files.interceptor.ts rename to packages/multer/interceptors/files.interceptor.ts index 175bfa21757..31fb2208f81 100644 --- a/packages/platform-fastify/multer/interceptors/files.interceptor.ts +++ b/packages/multer/interceptors/files.interceptor.ts @@ -1,5 +1,7 @@ -import * as multer from 'fastify-multer'; import { Observable } from 'rxjs'; +import { MULTER_MODULE_ADAPTER, MULTER_MODULE_OPTIONS } from '../constants'; +import { MulterModuleOptions, MulterOptions } from '../interfaces'; +import { transformException } from '../utils'; import { CallHandler, ExecutionContext, @@ -9,12 +11,6 @@ import { Optional, Type, } from '@nestjs/common'; -import { - MulterOptions, - MULTER_MODULE_OPTIONS, - MulterModuleOptions, - transformException, -} from '@nestjs/multer'; type MulterInstance = any; @@ -30,8 +26,10 @@ export function FilesInterceptor( @Optional() @Inject(MULTER_MODULE_OPTIONS) options: MulterModuleOptions = {}, + @Inject(MULTER_MODULE_ADAPTER) + adapter: MulterInstance, ) { - this.multer = (multer as any)({ + this.multer = (adapter as any)({ ...options, ...localOptions, }); diff --git a/packages/platform-express/multer/interceptors/index.ts b/packages/multer/interceptors/index.ts similarity index 100% rename from packages/platform-express/multer/interceptors/index.ts rename to packages/multer/interceptors/index.ts diff --git a/packages/multer/multer.module.ts b/packages/multer/multer.module.ts index f9e97f58d27..9aea1a74d7b 100644 --- a/packages/multer/multer.module.ts +++ b/packages/multer/multer.module.ts @@ -1,5 +1,5 @@ -import { DynamicModule, Module, Provider } from '@nestjs/common'; -import { MULTER_MODULE_OPTIONS } from './constants'; +import { DynamicModule, Module, Optional, Provider } from '@nestjs/common'; +import { MULTER_MODULE_ADAPTER, MULTER_MODULE_OPTIONS } from './constants'; import { MulterModuleAsyncOptions, MulterModuleOptions, @@ -8,31 +8,54 @@ import { @Module({}) export class MulterModule { - static register(options: MulterModuleOptions = {}): DynamicModule { + static register( + options: MulterModuleOptions = {}, + adapter: any, + ): DynamicModule { return { module: MulterModule, - providers: [{ provide: MULTER_MODULE_OPTIONS, useValue: options }], - exports: [MULTER_MODULE_OPTIONS], + providers: [ + { provide: MULTER_MODULE_OPTIONS, useValue: options }, + { + provide: MULTER_MODULE_ADAPTER, + useValue: adapter, + }, + ], + exports: [MULTER_MODULE_OPTIONS, MULTER_MODULE_ADAPTER], }; } - static registerAsync(options: MulterModuleAsyncOptions): DynamicModule { + static registerAsync( + options: MulterModuleAsyncOptions, + adapter: any, + ): DynamicModule { return { module: MulterModule, imports: options.imports, - providers: this.createAsyncProviders(options), - exports: [MULTER_MODULE_OPTIONS], + providers: this.createAsyncProviders(options, adapter), + exports: [MULTER_MODULE_OPTIONS, MULTER_MODULE_ADAPTER], }; } private static createAsyncProviders( options: MulterModuleAsyncOptions, + adapter: any, ): Provider[] { if (options.useExisting || options.useFactory) { - return [this.createAsyncOptionsProvider(options)]; + return [ + this.createAsyncOptionsProvider(options), + { + provide: MULTER_MODULE_ADAPTER, + useValue: adapter, + }, + ]; } return [ this.createAsyncOptionsProvider(options), + { + provide: MULTER_MODULE_ADAPTER, + useValue: adapter, + }, { provide: options.useClass, useClass: options.useClass, diff --git a/packages/multer/test/multer/fake-multer.ts b/packages/multer/test/multer/fake-multer.ts new file mode 100644 index 00000000000..5e988f3768f --- /dev/null +++ b/packages/multer/test/multer/fake-multer.ts @@ -0,0 +1,7 @@ +export const fakeMulter = () => { + return { + array: () => {}, + fields: () => {}, + single: () => {}, + }; +}; diff --git a/packages/platform-express/test/multer/interceptors/file-fields.interceptor.spec.ts b/packages/multer/test/multer/interceptors/file-fields.interceptor.spec.ts similarity index 87% rename from packages/platform-express/test/multer/interceptors/file-fields.interceptor.spec.ts rename to packages/multer/test/multer/interceptors/file-fields.interceptor.spec.ts index 7ab6fea50df..24b83d895e6 100644 --- a/packages/platform-express/test/multer/interceptors/file-fields.interceptor.spec.ts +++ b/packages/multer/test/multer/interceptors/file-fields.interceptor.spec.ts @@ -3,7 +3,8 @@ import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-hos import { expect } from 'chai'; import { of } from 'rxjs'; import * as sinon from 'sinon'; -import { FileFieldsInterceptor } from '../../../multer/interceptors/file-fields.interceptor'; +import { FileFieldsInterceptor } from '../../../interceptors/file-fields.interceptor'; +import { fakeMulter } from '../fake-multer'; describe('FileFieldsInterceptor', () => { it('should return metatype with expected structure', async () => { @@ -29,7 +30,7 @@ describe('FileFieldsInterceptor', () => { { name: fieldName1, maxCount: maxCount1 }, { name: fieldName2, maxCount: maxCount2 }, ]; - const target = new (FileFieldsInterceptor(argument))(); + const target = new (FileFieldsInterceptor(argument))(null, fakeMulter); const callback = (req, res, next) => next(); const fieldsSpy = sinon @@ -50,7 +51,7 @@ describe('FileFieldsInterceptor', () => { { name: fieldName1, maxCount: maxCount1 }, { name: fieldName2, maxCount: maxCount2 }, ]; - const target = new (FileFieldsInterceptor(argument))(); + const target = new (FileFieldsInterceptor(argument))(null, fakeMulter); const err = {}; const callback = (req, res, next) => next(err); diff --git a/packages/platform-fastify/test/multer/interceptors/file.interceptor.spec.ts b/packages/multer/test/multer/interceptors/file.interceptor.spec.ts similarity index 84% rename from packages/platform-fastify/test/multer/interceptors/file.interceptor.spec.ts rename to packages/multer/test/multer/interceptors/file.interceptor.spec.ts index 7f4178db44e..c2cd32a1ae3 100644 --- a/packages/platform-fastify/test/multer/interceptors/file.interceptor.spec.ts +++ b/packages/multer/test/multer/interceptors/file.interceptor.spec.ts @@ -3,7 +3,8 @@ import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-hos import { expect } from 'chai'; import { of } from 'rxjs'; import * as sinon from 'sinon'; -import { FileInterceptor } from '../../../multer/interceptors/file.interceptor'; +import { FileInterceptor } from '../../../interceptors/file.interceptor'; +import { fakeMulter } from '../fake-multer'; describe('FileInterceptor', () => { it('should return metatype with expected structure', async () => { @@ -17,9 +18,11 @@ describe('FileInterceptor', () => { handle: () => of('test'), }; }); + it('should call single() with expected params', async () => { const fieldName = 'file'; - const target = new (FileInterceptor(fieldName))(); + const target = new (FileInterceptor(fieldName))(null, fakeMulter); + const callback = (req, res, next) => next(); const singleSpy = sinon .stub((target as any).multer, 'single') @@ -30,9 +33,10 @@ describe('FileInterceptor', () => { expect(singleSpy.called).to.be.true; expect(singleSpy.calledWith(fieldName)).to.be.true; }); + it('should transform exception', async () => { const fieldName = 'file'; - const target = new (FileInterceptor(fieldName))(); + const target = new (FileInterceptor(fieldName))(null, fakeMulter); const err = {}; const callback = (req, res, next) => next(err); diff --git a/packages/platform-express/test/multer/interceptors/files.interceptor.spec.ts b/packages/multer/test/multer/interceptors/files.interceptor.spec.ts similarity index 86% rename from packages/platform-express/test/multer/interceptors/files.interceptor.spec.ts rename to packages/multer/test/multer/interceptors/files.interceptor.spec.ts index c6caadd62aa..6a7c5aa355d 100644 --- a/packages/platform-express/test/multer/interceptors/files.interceptor.spec.ts +++ b/packages/multer/test/multer/interceptors/files.interceptor.spec.ts @@ -3,7 +3,8 @@ import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-hos import { expect } from 'chai'; import { of } from 'rxjs'; import * as sinon from 'sinon'; -import { FilesInterceptor } from '../../../multer/interceptors/files.interceptor'; +import { FilesInterceptor } from '../../../interceptors/files.interceptor'; +import { fakeMulter } from '../fake-multer'; describe('FilesInterceptor', () => { it('should return metatype with expected structure', async () => { @@ -20,7 +21,10 @@ describe('FilesInterceptor', () => { it('should call array() with expected params', async () => { const fieldName = 'file'; const maxCount = 10; - const target = new (FilesInterceptor(fieldName, maxCount))(); + const target = new (FilesInterceptor(fieldName, maxCount))( + null, + fakeMulter, + ); const callback = (req, res, next) => next(); const arraySpy = sinon @@ -34,7 +38,7 @@ describe('FilesInterceptor', () => { }); it('should transform exception', async () => { const fieldName = 'file'; - const target = new (FilesInterceptor(fieldName))(); + const target = new (FilesInterceptor(fieldName))(null, fakeMulter); const err = {}; const callback = (req, res, next) => next(err); diff --git a/packages/multer/test/multer/multer.module.spec.ts b/packages/multer/test/multer/multer.module.spec.ts index 11d6fdfa6cf..6f2ddc16d1d 100644 --- a/packages/multer/test/multer/multer.module.spec.ts +++ b/packages/multer/test/multer/multer.module.spec.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; -import { MULTER_MODULE_OPTIONS } from '../../constants'; +import { MULTER_MODULE_ADAPTER, MULTER_MODULE_OPTIONS } from '../../constants'; import { MulterModule } from '../../multer.module'; describe('MulterModule', () => { @@ -9,15 +9,22 @@ describe('MulterModule', () => { const options = { test: 'test', }; - const dynamicModule = MulterModule.register(options as any); - expect(dynamicModule.providers).to.have.length(1); + const multer = { test: 'Fake Multer Instance' }; + + const dynamicModule = MulterModule.register(options as any, multer); + + expect(dynamicModule.providers).to.have.length(2); expect(dynamicModule.imports).to.be.undefined; expect(dynamicModule.exports).to.include(MULTER_MODULE_OPTIONS); expect(dynamicModule.providers).to.deep.include({ provide: MULTER_MODULE_OPTIONS, useValue: options, }); + expect(dynamicModule.providers).to.deep.include({ + provide: MULTER_MODULE_ADAPTER, + useValue: multer, + }); }); }); @@ -25,12 +32,14 @@ describe('MulterModule', () => { describe('when useFactory', () => { it('should provide an options', () => { const options: any = {}; + const multer = { test: 'Fake Multer Instance' }; + const asyncOptions = { useFactory: () => options, }; - const dynamicModule = MulterModule.registerAsync(asyncOptions); + const dynamicModule = MulterModule.registerAsync(asyncOptions, multer); - expect(dynamicModule.providers).to.have.length(1); + expect(dynamicModule.providers).to.have.length(2); expect(dynamicModule.imports).to.be.undefined; expect(dynamicModule.exports).to.include(MULTER_MODULE_OPTIONS); expect(dynamicModule.providers).to.deep.include({ @@ -38,38 +47,65 @@ describe('MulterModule', () => { useFactory: asyncOptions.useFactory, inject: [], }); + expect(dynamicModule.providers).to.deep.include({ + provide: MULTER_MODULE_ADAPTER, + useValue: multer, + }); }); }); describe('when useExisting', () => { it('should provide an options', () => { + const multer = { test: 'Fake Multer Instance' }; const asyncOptions = { useExisting: Object, }; - const dynamicModule = MulterModule.registerAsync(asyncOptions as any); + const dynamicModule = MulterModule.registerAsync( + asyncOptions as any, + multer, + ); - expect(dynamicModule.providers).to.have.length(1); + expect(dynamicModule.providers).to.have.length(2); expect(dynamicModule.imports).to.be.undefined; expect(dynamicModule.exports).to.include(MULTER_MODULE_OPTIONS); + expect(dynamicModule.exports).to.include(MULTER_MODULE_ADAPTER); + expect(dynamicModule.providers).to.deep.include({ + provide: MULTER_MODULE_ADAPTER, + useValue: multer, + }); }); }); describe('when useClass', () => { it('should provide an options', () => { + const multer = { test: 'Fake Multer Instance' }; const asyncOptions = { useClass: Object, }; - const dynamicModule = MulterModule.registerAsync(asyncOptions as any); + const dynamicModule = MulterModule.registerAsync( + asyncOptions as any, + multer, + ); - expect(dynamicModule.providers).to.have.length(2); + expect(dynamicModule.providers).to.have.length(3); expect(dynamicModule.imports).to.be.undefined; expect(dynamicModule.exports).to.include(MULTER_MODULE_OPTIONS); + expect(dynamicModule.exports).to.include(MULTER_MODULE_ADAPTER); + expect(dynamicModule.providers).to.deep.include({ + provide: MULTER_MODULE_ADAPTER, + useValue: multer, + }); }); + it('provider should call "createMulterOptions"', async () => { + const multer = { test: 'Fake Multer Instance' }; const asyncOptions = { useClass: Object, }; - const dynamicModule = MulterModule.registerAsync(asyncOptions as any); + const dynamicModule = MulterModule.registerAsync( + asyncOptions as any, + multer, + ); const optionsFactory = { createMulterOptions: sinon.spy(), }; @@ -77,6 +113,10 @@ describe('MulterModule', () => { optionsFactory, ); expect(optionsFactory.createMulterOptions.called).to.be.true; + expect(dynamicModule.providers).to.deep.include({ + provide: MULTER_MODULE_ADAPTER, + useValue: multer, + }); }); }); }); diff --git a/packages/platform-express/index.ts b/packages/platform-express/index.ts index 2b00533258f..277f7c0eaf8 100644 --- a/packages/platform-express/index.ts +++ b/packages/platform-express/index.ts @@ -7,4 +7,3 @@ export * from './adapters'; export * from './interfaces'; -export * from './multer'; diff --git a/packages/platform-express/multer/index.ts b/packages/platform-express/multer/index.ts deleted file mode 100644 index 34856f47c1c..00000000000 --- a/packages/platform-express/multer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './interceptors'; diff --git a/packages/platform-express/multer/interceptors/file-fields.interceptor.ts b/packages/platform-express/multer/interceptors/file-fields.interceptor.ts deleted file mode 100644 index 4de15eb41b6..00000000000 --- a/packages/platform-express/multer/interceptors/file-fields.interceptor.ts +++ /dev/null @@ -1,64 +0,0 @@ -import * as multer from 'multer'; -import { Observable } from 'rxjs'; -import { - CallHandler, - ExecutionContext, - Inject, - mixin, - NestInterceptor, - Optional, - Type, -} from '@nestjs/common'; -import { - transformException, - MulterField, - MulterModuleOptions, - MULTER_MODULE_OPTIONS, - MulterOptions, -} from '@nestjs/multer'; - -type MulterInstance = any; - -export function FileFieldsInterceptor( - uploadFields: MulterField[], - localOptions?: MulterOptions, -): Type { - class MixinInterceptor implements NestInterceptor { - protected multer: MulterInstance; - - constructor( - @Optional() - @Inject(MULTER_MODULE_OPTIONS) - options: MulterModuleOptions = {}, - ) { - this.multer = (multer as any)({ - ...options, - ...localOptions, - }); - } - - async intercept( - context: ExecutionContext, - next: CallHandler, - ): Promise> { - const ctx = context.switchToHttp(); - - await new Promise((resolve, reject) => - this.multer.fields(uploadFields)( - ctx.getRequest(), - ctx.getResponse(), - (err: any) => { - if (err) { - const error = transformException(err); - return reject(error); - } - resolve(); - }, - ), - ); - return next.handle(); - } - } - const Interceptor = mixin(MixinInterceptor); - return Interceptor as Type; -} diff --git a/packages/platform-express/multer/interceptors/file.interceptor.ts b/packages/platform-express/multer/interceptors/file.interceptor.ts deleted file mode 100644 index f025fcaa2cd..00000000000 --- a/packages/platform-express/multer/interceptors/file.interceptor.ts +++ /dev/null @@ -1,63 +0,0 @@ -import * as multer from 'multer'; -import { Observable } from 'rxjs'; -import { - CallHandler, - ExecutionContext, - Inject, - mixin, - NestInterceptor, - Optional, - Type, -} from '@nestjs/common'; -import { - MulterOptions, - MULTER_MODULE_OPTIONS, - MulterModuleOptions, - transformException, -} from '@nestjs/multer'; - -type MulterInstance = any; - -export function FileInterceptor( - fieldName: string, - localOptions?: MulterOptions, -): Type { - class MixinInterceptor implements NestInterceptor { - protected multer: MulterInstance; - - constructor( - @Optional() - @Inject(MULTER_MODULE_OPTIONS) - options: MulterModuleOptions = {}, - ) { - this.multer = (multer as any)({ - ...options, - ...localOptions, - }); - } - - async intercept( - context: ExecutionContext, - next: CallHandler, - ): Promise> { - const ctx = context.switchToHttp(); - - await new Promise((resolve, reject) => - this.multer.single(fieldName)( - ctx.getRequest(), - ctx.getResponse(), - (err: any) => { - if (err) { - const error = transformException(err); - return reject(error); - } - resolve(); - }, - ), - ); - return next.handle(); - } - } - const Interceptor = mixin(MixinInterceptor); - return Interceptor as Type; -} diff --git a/packages/platform-express/multer/interceptors/files.interceptor.ts b/packages/platform-express/multer/interceptors/files.interceptor.ts deleted file mode 100644 index 712ea1d84d7..00000000000 --- a/packages/platform-express/multer/interceptors/files.interceptor.ts +++ /dev/null @@ -1,64 +0,0 @@ -import * as multer from 'multer'; -import { Observable } from 'rxjs'; -import { - CallHandler, - ExecutionContext, - Inject, - mixin, - NestInterceptor, - Optional, - Type, -} from '@nestjs/common'; -import { - MulterOptions, - MULTER_MODULE_OPTIONS, - MulterModuleOptions, - transformException, -} from '@nestjs/multer'; - -type MulterInstance = any; - -export function FilesInterceptor( - fieldName: string, - maxCount?: number, - localOptions?: MulterOptions, -): Type { - class MixinInterceptor implements NestInterceptor { - protected multer: MulterInstance; - - constructor( - @Optional() - @Inject(MULTER_MODULE_OPTIONS) - options: MulterModuleOptions = {}, - ) { - this.multer = (multer as any)({ - ...options, - ...localOptions, - }); - } - - async intercept( - context: ExecutionContext, - next: CallHandler, - ): Promise> { - const ctx = context.switchToHttp(); - - await new Promise((resolve, reject) => - this.multer.array(fieldName, maxCount)( - ctx.getRequest(), - ctx.getResponse(), - (err: any) => { - if (err) { - const error = transformException(err); - return reject(error); - } - resolve(); - }, - ), - ); - return next.handle(); - } - } - const Interceptor = mixin(MixinInterceptor); - return Interceptor as Type; -} diff --git a/packages/platform-express/test/multer/interceptors/file.interceptor.spec.ts b/packages/platform-express/test/multer/interceptors/file.interceptor.spec.ts deleted file mode 100644 index 7f4178db44e..00000000000 --- a/packages/platform-express/test/multer/interceptors/file.interceptor.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { CallHandler } from '@nestjs/common'; -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; -import { expect } from 'chai'; -import { of } from 'rxjs'; -import * as sinon from 'sinon'; -import { FileInterceptor } from '../../../multer/interceptors/file.interceptor'; - -describe('FileInterceptor', () => { - it('should return metatype with expected structure', async () => { - const targetClass = FileInterceptor('file'); - expect(targetClass.prototype.intercept).to.not.be.undefined; - }); - describe('intercept', () => { - let handler: CallHandler; - beforeEach(() => { - handler = { - handle: () => of('test'), - }; - }); - it('should call single() with expected params', async () => { - const fieldName = 'file'; - const target = new (FileInterceptor(fieldName))(); - const callback = (req, res, next) => next(); - const singleSpy = sinon - .stub((target as any).multer, 'single') - .returns(callback); - - await target.intercept(new ExecutionContextHost([]), handler); - - expect(singleSpy.called).to.be.true; - expect(singleSpy.calledWith(fieldName)).to.be.true; - }); - it('should transform exception', async () => { - const fieldName = 'file'; - const target = new (FileInterceptor(fieldName))(); - const err = {}; - const callback = (req, res, next) => next(err); - - (target as any).multer = { - single: () => callback, - }; - (target.intercept(new ExecutionContextHost([]), handler) as any).catch( - error => expect(error).to.not.be.undefined, - ); - }); - }); -}); diff --git a/packages/platform-fastify/index.ts b/packages/platform-fastify/index.ts index 2b00533258f..277f7c0eaf8 100644 --- a/packages/platform-fastify/index.ts +++ b/packages/platform-fastify/index.ts @@ -7,4 +7,3 @@ export * from './adapters'; export * from './interfaces'; -export * from './multer'; diff --git a/packages/platform-fastify/multer/index.ts b/packages/platform-fastify/multer/index.ts deleted file mode 100644 index 34856f47c1c..00000000000 --- a/packages/platform-fastify/multer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './interceptors'; diff --git a/packages/platform-fastify/multer/interceptors/index.ts b/packages/platform-fastify/multer/interceptors/index.ts deleted file mode 100644 index 3de0e24ce6d..00000000000 --- a/packages/platform-fastify/multer/interceptors/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './file-fields.interceptor'; -export * from './file.interceptor'; -export * from './files.interceptor'; diff --git a/packages/platform-fastify/test/multer/interceptors/file-fields.interceptor.spec.ts b/packages/platform-fastify/test/multer/interceptors/file-fields.interceptor.spec.ts deleted file mode 100644 index 7ab6fea50df..00000000000 --- a/packages/platform-fastify/test/multer/interceptors/file-fields.interceptor.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { CallHandler } from '@nestjs/common'; -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; -import { expect } from 'chai'; -import { of } from 'rxjs'; -import * as sinon from 'sinon'; -import { FileFieldsInterceptor } from '../../../multer/interceptors/file-fields.interceptor'; - -describe('FileFieldsInterceptor', () => { - it('should return metatype with expected structure', async () => { - const targetClass = FileFieldsInterceptor([ - { name: 'file', maxCount: 1 }, - { name: 'anotherFile', maxCount: 1 }, - ]); - expect(targetClass.prototype.intercept).to.not.be.undefined; - }); - describe('intercept', () => { - let handler: CallHandler; - beforeEach(() => { - handler = { - handle: () => of('test'), - }; - }); - it('should call object with expected params', async () => { - const fieldName1 = 'file'; - const maxCount1 = 1; - const fieldName2 = 'anotherFile'; - const maxCount2 = 2; - const argument = [ - { name: fieldName1, maxCount: maxCount1 }, - { name: fieldName2, maxCount: maxCount2 }, - ]; - const target = new (FileFieldsInterceptor(argument))(); - - const callback = (req, res, next) => next(); - const fieldsSpy = sinon - .stub((target as any).multer, 'fields') - .returns(callback); - - await target.intercept(new ExecutionContextHost([]), handler); - - expect(fieldsSpy.called).to.be.true; - expect(fieldsSpy.calledWith(argument)).to.be.true; - }); - it('should transform exception', async () => { - const fieldName1 = 'file'; - const maxCount1 = 1; - const fieldName2 = 'anotherFile'; - const maxCount2 = 2; - const argument = [ - { name: fieldName1, maxCount: maxCount1 }, - { name: fieldName2, maxCount: maxCount2 }, - ]; - const target = new (FileFieldsInterceptor(argument))(); - const err = {}; - const callback = (req, res, next) => next(err); - - (target as any).fields = { - array: () => callback, - }; - (target.intercept(new ExecutionContextHost([]), handler) as any).catch( - error => expect(error).to.not.be.undefined, - ); - }); - }); -}); diff --git a/packages/platform-fastify/test/multer/interceptors/files.interceptor.spec.ts b/packages/platform-fastify/test/multer/interceptors/files.interceptor.spec.ts deleted file mode 100644 index c6caadd62aa..00000000000 --- a/packages/platform-fastify/test/multer/interceptors/files.interceptor.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { CallHandler } from '@nestjs/common'; -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; -import { expect } from 'chai'; -import { of } from 'rxjs'; -import * as sinon from 'sinon'; -import { FilesInterceptor } from '../../../multer/interceptors/files.interceptor'; - -describe('FilesInterceptor', () => { - it('should return metatype with expected structure', async () => { - const targetClass = FilesInterceptor('file'); - expect(targetClass.prototype.intercept).to.not.be.undefined; - }); - describe('intercept', () => { - let handler: CallHandler; - beforeEach(() => { - handler = { - handle: () => of('test'), - }; - }); - it('should call array() with expected params', async () => { - const fieldName = 'file'; - const maxCount = 10; - const target = new (FilesInterceptor(fieldName, maxCount))(); - - const callback = (req, res, next) => next(); - const arraySpy = sinon - .stub((target as any).multer, 'array') - .returns(callback); - - await target.intercept(new ExecutionContextHost([]), handler); - - expect(arraySpy.called).to.be.true; - expect(arraySpy.calledWith(fieldName, maxCount)).to.be.true; - }); - it('should transform exception', async () => { - const fieldName = 'file'; - const target = new (FilesInterceptor(fieldName))(); - const err = {}; - const callback = (req, res, next) => next(err); - - (target as any).multer = { - array: () => callback, - }; - (target.intercept(new ExecutionContextHost([]), handler) as any).catch( - error => expect(error).to.not.be.undefined, - ); - }); - }); -}); From fcdb8458145a84708012fd82b84d73c091c9650f Mon Sep 17 00:00:00 2001 From: Gustavo Perdomo Date: Fri, 26 Apr 2019 13:42:24 -0400 Subject: [PATCH 4/5] add multer re-export --- package-lock.json | 71 +++++++++++++++++++++++++++++- package.json | 1 + packages/platform-express/index.ts | 1 + packages/platform-fastify/index.ts | 1 + 4 files changed, 72 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2fcac93105a..0d4b7c9d241 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1619,8 +1619,7 @@ "@types/mocha": { "version": "5.2.6", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.6.tgz", - "integrity": "sha512-1axi39YdtBI7z957vdqXI4Ac25e7YihYQtJa+Clnxg1zTJEaIRbndt71O3sP4GAMgiAm0pY26/b9BrY4MR/PMw==", - "dev": true + "integrity": "sha512-1axi39YdtBI7z957vdqXI4Ac25e7YihYQtJa+Clnxg1zTJEaIRbndt71O3sP4GAMgiAm0pY26/b9BrY4MR/PMw==" }, "@types/node": { "version": "10.14.5", @@ -2124,6 +2123,11 @@ "buffer-equal": "^1.0.0" } }, + "append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" + }, "append-transform": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", @@ -6108,6 +6112,69 @@ "qs": "^6.5.1" } }, + "fastify-multer": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/fastify-multer/-/fastify-multer-1.4.2.tgz", + "integrity": "sha512-e9KY/ZkSTFGIyB6u/A7DTr5zee5lYAgsvyIdMf56JtXlMQnbDhQaGDUWzyrx31+I6MhZOC6Y1angjvj1mujmcw==", + "requires": { + "@types/mocha": "^5.2.6", + "append-field": "^1.0.0", + "busboy": "^0.3.0", + "concat-stream": "^2.0.0", + "fastify-plugin": "^1.5.0", + "mkdirp": "^0.5.1", + "on-finished": "^2.3.0", + "type-is": "^1.6.16", + "xtend": "^4.0.1" + }, + "dependencies": { + "busboy": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz", + "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==", + "requires": { + "dicer": "0.3.0" + } + }, + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "dicer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", + "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", + "requires": { + "streamsearch": "0.1.2" + } + }, + "readable-stream": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", + "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "string_decoder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", + "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "fastify-multipart": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/fastify-multipart/-/fastify-multipart-0.8.1.tgz", diff --git a/package.json b/package.json index 4cd72823b97..035dd4b1204 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "fastify": "2.3.0", "fastify-cors": "2.1.2", "fastify-formbody": "3.1.0", + "fastify-multer": "^1.4.2", "fastify-multipart": "0.8.1", "graphql": "14.2.1", "grpc": "1.20.0", diff --git a/packages/platform-express/index.ts b/packages/platform-express/index.ts index 277f7c0eaf8..bd051203ac2 100644 --- a/packages/platform-express/index.ts +++ b/packages/platform-express/index.ts @@ -7,3 +7,4 @@ export * from './adapters'; export * from './interfaces'; +export { default as multerAdapter } from 'multer'; diff --git a/packages/platform-fastify/index.ts b/packages/platform-fastify/index.ts index 277f7c0eaf8..886f16d700b 100644 --- a/packages/platform-fastify/index.ts +++ b/packages/platform-fastify/index.ts @@ -7,3 +7,4 @@ export * from './adapters'; export * from './interfaces'; +export { default as multerAdapter } from 'fastify-multer'; From 9d57a311673f5431a8327cbedac157ba2b5b1917 Mon Sep 17 00:00:00 2001 From: Gustavo Perdomo Date: Sat, 27 Apr 2019 13:50:43 -0400 Subject: [PATCH 5/5] remove multer package and implement multer inside platform-fastify --- gulpfile.js | 2 - packages/multer/Readme.md | 89 ------------------- packages/multer/package.json | 19 ---- packages/multer/test/multer/fake-multer.ts | 7 -- packages/multer/tsconfig.json | 7 -- packages/multer/utils/index.ts | 1 - packages/platform-express/index.ts | 2 +- .../multer/files.constants.ts | 1 + .../{ => platform-express}/multer/index.ts | 4 +- .../interceptors/file-fields.interceptor.ts | 17 ++-- .../multer/interceptors/file.interceptor.ts | 14 +-- .../multer/interceptors/files.interceptor.ts | 14 +-- .../multer/interceptors/index.ts | 0 .../files-upload-module.interface.ts | 0 .../multer/interfaces/index.ts | 1 - .../interfaces/multer-options.interface.ts | 0 .../multer/multer.module.ts | 41 ++------- .../multer/multer/multer.constants.ts} | 3 - .../multer/multer/multer.utils.ts} | 2 +- packages/platform-express/package.json | 3 +- .../file-fields.interceptor.spec.ts | 7 +- .../interceptors/file.interceptor.spec.ts | 10 +-- .../interceptors/files.interceptor.spec.ts | 10 +-- .../test/multer}/multer/multer.module.spec.ts | 62 +++---------- .../test/multer}/multer/multer.utils.spec.ts | 6 +- packages/platform-fastify/index.ts | 2 +- .../multer/files.constants.ts | 1 + packages/platform-fastify/multer/index.ts | 3 + .../interceptors/file-fields.interceptor.ts | 64 +++++++++++++ .../multer/interceptors/file.interceptor.ts | 61 +++++++++++++ .../multer/interceptors/files.interceptor.ts | 62 +++++++++++++ .../multer/interceptors/index.ts | 3 + .../files-upload-module.interface.ts | 19 ++++ .../multer/interfaces/index.ts | 1 + .../interfaces/multer-options.interface.ts | 61 +++++++++++++ .../platform-fastify/multer/multer.module.ts | 60 +++++++++++++ .../multer/multer/multer.constants.ts | 9 ++ .../multer/multer/multer.utils.ts | 24 +++++ packages/platform-fastify/package.json | 3 +- .../file-fields.interceptor.spec.ts | 65 ++++++++++++++ .../interceptors/file.interceptor.spec.ts | 47 ++++++++++ .../interceptors/files.interceptor.spec.ts | 49 ++++++++++ .../test/multer/multer/multer.module.spec.ts | 83 +++++++++++++++++ .../test/multer/multer/multer.utils.spec.ts | 41 +++++++++ 44 files changed, 716 insertions(+), 264 deletions(-) delete mode 100644 packages/multer/Readme.md delete mode 100644 packages/multer/package.json delete mode 100644 packages/multer/test/multer/fake-multer.ts delete mode 100644 packages/multer/tsconfig.json delete mode 100644 packages/multer/utils/index.ts create mode 100644 packages/platform-express/multer/files.constants.ts rename packages/{ => platform-express}/multer/index.ts (63%) rename packages/{ => platform-express}/multer/interceptors/file-fields.interceptor.ts (78%) rename packages/{ => platform-express}/multer/interceptors/file.interceptor.ts (79%) rename packages/{ => platform-express}/multer/interceptors/files.interceptor.ts (79%) rename packages/{ => platform-express}/multer/interceptors/index.ts (100%) rename packages/{ => platform-express}/multer/interfaces/files-upload-module.interface.ts (100%) rename packages/{ => platform-express}/multer/interfaces/index.ts (52%) rename packages/{ => platform-express}/multer/interfaces/multer-options.interface.ts (100%) rename packages/{ => platform-express}/multer/multer.module.ts (53%) rename packages/{multer/constants.ts => platform-express/multer/multer/multer.constants.ts} (72%) rename packages/{multer/utils/transform-exception.util.ts => platform-express/multer/multer/multer.utils.ts} (92%) rename packages/{multer => platform-express}/test/multer/interceptors/file-fields.interceptor.spec.ts (87%) rename packages/{multer => platform-express}/test/multer/interceptors/file.interceptor.spec.ts (84%) rename packages/{multer => platform-express}/test/multer/interceptors/files.interceptor.spec.ts (86%) rename packages/{multer/test => platform-express/test/multer}/multer/multer.module.spec.ts (55%) rename packages/{multer/test => platform-express/test/multer}/multer/multer.utils.spec.ts (89%) create mode 100644 packages/platform-fastify/multer/files.constants.ts create mode 100644 packages/platform-fastify/multer/index.ts create mode 100644 packages/platform-fastify/multer/interceptors/file-fields.interceptor.ts create mode 100644 packages/platform-fastify/multer/interceptors/file.interceptor.ts create mode 100644 packages/platform-fastify/multer/interceptors/files.interceptor.ts create mode 100644 packages/platform-fastify/multer/interceptors/index.ts create mode 100644 packages/platform-fastify/multer/interfaces/files-upload-module.interface.ts create mode 100644 packages/platform-fastify/multer/interfaces/index.ts create mode 100644 packages/platform-fastify/multer/interfaces/multer-options.interface.ts create mode 100644 packages/platform-fastify/multer/multer.module.ts create mode 100644 packages/platform-fastify/multer/multer/multer.constants.ts create mode 100644 packages/platform-fastify/multer/multer/multer.utils.ts create mode 100644 packages/platform-fastify/test/multer/interceptors/file-fields.interceptor.spec.ts create mode 100644 packages/platform-fastify/test/multer/interceptors/file.interceptor.spec.ts create mode 100644 packages/platform-fastify/test/multer/interceptors/files.interceptor.spec.ts create mode 100644 packages/platform-fastify/test/multer/multer/multer.module.spec.ts create mode 100644 packages/platform-fastify/test/multer/multer/multer.utils.spec.ts diff --git a/gulpfile.js b/gulpfile.js index 44cfbe58798..ff767809956 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -9,7 +9,6 @@ const deleteEmpty = require('delete-empty'); const packages = { common: ts.createProject('packages/common/tsconfig.json'), core: ts.createProject('packages/core/tsconfig.json'), - multer: ts.createProject('packages/multer/tsconfig.json'), microservices: ts.createProject('packages/microservices/tsconfig.json'), websockets: ts.createProject('packages/websockets/tsconfig.json'), testing: ts.createProject('packages/testing/tsconfig.json'), @@ -43,7 +42,6 @@ gulp.task('copy-misc', function() { .src(['Readme.md', 'LICENSE', '.npmignore']) .pipe(gulp.dest(`${source}/common`)) .pipe(gulp.dest(`${source}/core`)) - .pipe(gulp.dest(`${source}/multer`)) .pipe(gulp.dest(`${source}/microservices`)) .pipe(gulp.dest(`${source}/websockets`)) .pipe(gulp.dest(`${source}/testing`)) diff --git a/packages/multer/Readme.md b/packages/multer/Readme.md deleted file mode 100644 index f33512e474f..00000000000 --- a/packages/multer/Readme.md +++ /dev/null @@ -1,89 +0,0 @@ -

- Nest Logo -

- -[travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master -[travis-url]: https://travis-ci.org/nestjs/nest -[linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux -[linux-url]: https://travis-ci.org/nestjs/nest - -

A progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.

-

-NPM Version -Package License -NPM Downloads -Travis -Linux -Coverage -Gitter -Discord -Backers on Open Collective -Sponsors on Open Collective - - -

- - -## Description - -Nest is a framework for building efficient, scalable Node.js server-side applications. It uses modern JavaScript, is built with TypeScript (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming). - -

Under the hood, Nest makes use of Express, but also, provides compatibility with a wide range of other libraries, like e.g. Fastify, allowing for easy use of the myriad third-party plugins which are available.

- -## Philosophy - -

In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front and backend applications, giving rise to awesome projects like Angular, React and Vue which improve developer productivity and enable the construction of fast, testable, extensible frontend applications. However, on the server-side, while there are a lot of superb libraries, helpers and tools for Node, none of them effectively solve the main problem - the architecture.

-

Nest aims to provide an application architecture out of the box which allows for effortless creation of highly testable, scalable, loosely coupled and easily maintainable applications.

- -## Getting started - -* To check out the [guide](https://docs.nestjs.com), visit [docs.nestjs.com](https://docs.nestjs.com). :books: -* 要查看中文 [指南](readme_zh.md), 请访问 [docs.nestjs.cn](https://docs.nestjs.cn). :books: - -## Consulting - -With official support, you can get expert help straight from Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://docs.nestjs.com/enterprise). - -## Support - -Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). - -#### Principal Sponsor - - - -#### Base Sponsor - -   - - -#### Silver Sponsors -   -       -   - - - -#### Sponsors - -               - -   -   - - - -## Backers - - - -## Stay in touch - -* Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) -* Website - [https://nestjs.com](https://nestjs.com/) -* Twitter - [@nestframework](https://twitter.com/nestframework) - -## License - -Nest is [MIT licensed](LICENSE). diff --git a/packages/multer/package.json b/packages/multer/package.json deleted file mode 100644 index 989ac29f379..00000000000 --- a/packages/multer/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "@nestjs/multer", - "version": "6.1.1", - "description": "...", - "author": "Kamil Mysliwiec", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/nestjs/nest" - }, - "publishConfig": { - "access": "public" - }, - "dependencies": {}, - "peerDependencies": { - "@nestjs/common": "^6.0.0", - "@nestjs/core": "^6.0.0" - } -} diff --git a/packages/multer/test/multer/fake-multer.ts b/packages/multer/test/multer/fake-multer.ts deleted file mode 100644 index 5e988f3768f..00000000000 --- a/packages/multer/test/multer/fake-multer.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const fakeMulter = () => { - return { - array: () => {}, - fields: () => {}, - single: () => {}, - }; -}; diff --git a/packages/multer/tsconfig.json b/packages/multer/tsconfig.json deleted file mode 100644 index b238080a42e..00000000000 --- a/packages/multer/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./../tsconfig.base.json", - "compilerOptions": { - "baseUrl": "./" - }, - "include": ["*.ts", "**/*.ts"] -} diff --git a/packages/multer/utils/index.ts b/packages/multer/utils/index.ts deleted file mode 100644 index 6771918e7f6..00000000000 --- a/packages/multer/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './transform-exception.util'; diff --git a/packages/platform-express/index.ts b/packages/platform-express/index.ts index bd051203ac2..2b00533258f 100644 --- a/packages/platform-express/index.ts +++ b/packages/platform-express/index.ts @@ -7,4 +7,4 @@ export * from './adapters'; export * from './interfaces'; -export { default as multerAdapter } from 'multer'; +export * from './multer'; diff --git a/packages/platform-express/multer/files.constants.ts b/packages/platform-express/multer/files.constants.ts new file mode 100644 index 00000000000..fd2ef8091cc --- /dev/null +++ b/packages/platform-express/multer/files.constants.ts @@ -0,0 +1 @@ +export const MULTER_MODULE_OPTIONS = 'MULTER_MODULE_OPTIONS'; diff --git a/packages/multer/index.ts b/packages/platform-express/multer/index.ts similarity index 63% rename from packages/multer/index.ts rename to packages/platform-express/multer/index.ts index 1c4ea39a36f..7afd4aab899 100644 --- a/packages/multer/index.ts +++ b/packages/platform-express/multer/index.ts @@ -1,5 +1,3 @@ -export * from './constants'; +export * from './interceptors'; export * from './interfaces'; export * from './multer.module'; -export * from './utils'; -export * from './interceptors'; diff --git a/packages/multer/interceptors/file-fields.interceptor.ts b/packages/platform-express/multer/interceptors/file-fields.interceptor.ts similarity index 78% rename from packages/multer/interceptors/file-fields.interceptor.ts rename to packages/platform-express/multer/interceptors/file-fields.interceptor.ts index bb6b6a4a472..535efa7429f 100644 --- a/packages/multer/interceptors/file-fields.interceptor.ts +++ b/packages/platform-express/multer/interceptors/file-fields.interceptor.ts @@ -1,7 +1,3 @@ -import { Observable } from 'rxjs'; -import { MULTER_MODULE_ADAPTER, MULTER_MODULE_OPTIONS } from '../constants'; -import { MulterField, MulterModuleOptions, MulterOptions } from '../interfaces'; -import { transformException } from '../utils'; import { CallHandler, ExecutionContext, @@ -11,6 +7,15 @@ import { Optional, Type, } from '@nestjs/common'; +import * as multer from 'multer'; +import { Observable } from 'rxjs'; +import { MULTER_MODULE_OPTIONS } from '../files.constants'; +import { MulterModuleOptions } from '../interfaces'; +import { + MulterField, + MulterOptions, +} from '../interfaces/multer-options.interface'; +import { transformException } from '../multer/multer.utils'; type MulterInstance = any; @@ -25,10 +30,8 @@ export function FileFieldsInterceptor( @Optional() @Inject(MULTER_MODULE_OPTIONS) options: MulterModuleOptions = {}, - @Inject(MULTER_MODULE_ADAPTER) - adapter: MulterInstance, ) { - this.multer = (adapter as any)({ + this.multer = (multer as any)({ ...options, ...localOptions, }); diff --git a/packages/multer/interceptors/file.interceptor.ts b/packages/platform-express/multer/interceptors/file.interceptor.ts similarity index 79% rename from packages/multer/interceptors/file.interceptor.ts rename to packages/platform-express/multer/interceptors/file.interceptor.ts index 93b56179b43..450ae2aecf1 100644 --- a/packages/multer/interceptors/file.interceptor.ts +++ b/packages/platform-express/multer/interceptors/file.interceptor.ts @@ -1,7 +1,3 @@ -import { Observable } from 'rxjs'; -import { MULTER_MODULE_ADAPTER, MULTER_MODULE_OPTIONS } from '../constants'; -import { MulterModuleOptions, MulterOptions } from '../interfaces'; -import { transformException } from '../utils'; import { CallHandler, ExecutionContext, @@ -11,6 +7,12 @@ import { Optional, Type, } from '@nestjs/common'; +import * as multer from 'multer'; +import { Observable } from 'rxjs'; +import { MULTER_MODULE_OPTIONS } from '../files.constants'; +import { MulterModuleOptions } from '../interfaces'; +import { MulterOptions } from '../interfaces/multer-options.interface'; +import { transformException } from '../multer/multer.utils'; type MulterInstance = any; @@ -25,10 +27,8 @@ export function FileInterceptor( @Optional() @Inject(MULTER_MODULE_OPTIONS) options: MulterModuleOptions = {}, - @Inject(MULTER_MODULE_ADAPTER) - adapter: MulterInstance, ) { - this.multer = (adapter as any)({ + this.multer = (multer as any)({ ...options, ...localOptions, }); diff --git a/packages/multer/interceptors/files.interceptor.ts b/packages/platform-express/multer/interceptors/files.interceptor.ts similarity index 79% rename from packages/multer/interceptors/files.interceptor.ts rename to packages/platform-express/multer/interceptors/files.interceptor.ts index 31fb2208f81..5440a944924 100644 --- a/packages/multer/interceptors/files.interceptor.ts +++ b/packages/platform-express/multer/interceptors/files.interceptor.ts @@ -1,7 +1,3 @@ -import { Observable } from 'rxjs'; -import { MULTER_MODULE_ADAPTER, MULTER_MODULE_OPTIONS } from '../constants'; -import { MulterModuleOptions, MulterOptions } from '../interfaces'; -import { transformException } from '../utils'; import { CallHandler, ExecutionContext, @@ -11,6 +7,12 @@ import { Optional, Type, } from '@nestjs/common'; +import * as multer from 'multer'; +import { Observable } from 'rxjs'; +import { MULTER_MODULE_OPTIONS } from '../files.constants'; +import { MulterModuleOptions } from '../interfaces'; +import { MulterOptions } from '../interfaces/multer-options.interface'; +import { transformException } from '../multer/multer.utils'; type MulterInstance = any; @@ -26,10 +28,8 @@ export function FilesInterceptor( @Optional() @Inject(MULTER_MODULE_OPTIONS) options: MulterModuleOptions = {}, - @Inject(MULTER_MODULE_ADAPTER) - adapter: MulterInstance, ) { - this.multer = (adapter as any)({ + this.multer = (multer as any)({ ...options, ...localOptions, }); diff --git a/packages/multer/interceptors/index.ts b/packages/platform-express/multer/interceptors/index.ts similarity index 100% rename from packages/multer/interceptors/index.ts rename to packages/platform-express/multer/interceptors/index.ts diff --git a/packages/multer/interfaces/files-upload-module.interface.ts b/packages/platform-express/multer/interfaces/files-upload-module.interface.ts similarity index 100% rename from packages/multer/interfaces/files-upload-module.interface.ts rename to packages/platform-express/multer/interfaces/files-upload-module.interface.ts diff --git a/packages/multer/interfaces/index.ts b/packages/platform-express/multer/interfaces/index.ts similarity index 52% rename from packages/multer/interfaces/index.ts rename to packages/platform-express/multer/interfaces/index.ts index fe2246c78d6..be94ec24afd 100644 --- a/packages/multer/interfaces/index.ts +++ b/packages/platform-express/multer/interfaces/index.ts @@ -1,2 +1 @@ export * from './files-upload-module.interface'; -export * from './multer-options.interface'; diff --git a/packages/multer/interfaces/multer-options.interface.ts b/packages/platform-express/multer/interfaces/multer-options.interface.ts similarity index 100% rename from packages/multer/interfaces/multer-options.interface.ts rename to packages/platform-express/multer/interfaces/multer-options.interface.ts diff --git a/packages/multer/multer.module.ts b/packages/platform-express/multer/multer.module.ts similarity index 53% rename from packages/multer/multer.module.ts rename to packages/platform-express/multer/multer.module.ts index 9aea1a74d7b..5d28a99e867 100644 --- a/packages/multer/multer.module.ts +++ b/packages/platform-express/multer/multer.module.ts @@ -1,5 +1,5 @@ -import { DynamicModule, Module, Optional, Provider } from '@nestjs/common'; -import { MULTER_MODULE_ADAPTER, MULTER_MODULE_OPTIONS } from './constants'; +import { DynamicModule, Module, Provider } from '@nestjs/common'; +import { MULTER_MODULE_OPTIONS } from './files.constants'; import { MulterModuleAsyncOptions, MulterModuleOptions, @@ -8,54 +8,31 @@ import { @Module({}) export class MulterModule { - static register( - options: MulterModuleOptions = {}, - adapter: any, - ): DynamicModule { + static register(options: MulterModuleOptions = {}): DynamicModule { return { module: MulterModule, - providers: [ - { provide: MULTER_MODULE_OPTIONS, useValue: options }, - { - provide: MULTER_MODULE_ADAPTER, - useValue: adapter, - }, - ], - exports: [MULTER_MODULE_OPTIONS, MULTER_MODULE_ADAPTER], + providers: [{ provide: MULTER_MODULE_OPTIONS, useValue: options }], + exports: [MULTER_MODULE_OPTIONS], }; } - static registerAsync( - options: MulterModuleAsyncOptions, - adapter: any, - ): DynamicModule { + static registerAsync(options: MulterModuleAsyncOptions): DynamicModule { return { module: MulterModule, imports: options.imports, - providers: this.createAsyncProviders(options, adapter), - exports: [MULTER_MODULE_OPTIONS, MULTER_MODULE_ADAPTER], + providers: this.createAsyncProviders(options), + exports: [MULTER_MODULE_OPTIONS], }; } private static createAsyncProviders( options: MulterModuleAsyncOptions, - adapter: any, ): Provider[] { if (options.useExisting || options.useFactory) { - return [ - this.createAsyncOptionsProvider(options), - { - provide: MULTER_MODULE_ADAPTER, - useValue: adapter, - }, - ]; + return [this.createAsyncOptionsProvider(options)]; } return [ this.createAsyncOptionsProvider(options), - { - provide: MULTER_MODULE_ADAPTER, - useValue: adapter, - }, { provide: options.useClass, useClass: options.useClass, diff --git a/packages/multer/constants.ts b/packages/platform-express/multer/multer/multer.constants.ts similarity index 72% rename from packages/multer/constants.ts rename to packages/platform-express/multer/multer/multer.constants.ts index 5e7d6314e09..e3c7b4557f2 100644 --- a/packages/multer/constants.ts +++ b/packages/platform-express/multer/multer/multer.constants.ts @@ -1,6 +1,3 @@ -export const MULTER_MODULE_OPTIONS = 'MULTER_MODULE_OPTIONS'; -export const MULTER_MODULE_ADAPTER = 'MULTER_MODULE_ADAPTER'; - export const multerExceptions = { LIMIT_PART_COUNT: 'Too many parts', LIMIT_FILE_SIZE: 'File too large', diff --git a/packages/multer/utils/transform-exception.util.ts b/packages/platform-express/multer/multer/multer.utils.ts similarity index 92% rename from packages/multer/utils/transform-exception.util.ts rename to packages/platform-express/multer/multer/multer.utils.ts index 1ab0e2c7d56..81e19739ca9 100644 --- a/packages/multer/utils/transform-exception.util.ts +++ b/packages/platform-express/multer/multer/multer.utils.ts @@ -1,9 +1,9 @@ -import { multerExceptions } from '../constants'; import { BadRequestException, HttpException, PayloadTooLargeException, } from '@nestjs/common'; +import { multerExceptions } from './multer.constants'; export function transformException(error: Error | undefined) { if (!error || error instanceof HttpException) { diff --git a/packages/platform-express/package.json b/packages/platform-express/package.json index b39e1171b87..de787ce5f23 100644 --- a/packages/platform-express/package.json +++ b/packages/platform-express/package.json @@ -19,7 +19,6 @@ }, "peerDependencies": { "@nestjs/common": "^6.0.0", - "@nestjs/core": "^6.0.0", - "@nestjs/multer": "^6.0.0" + "@nestjs/core": "^6.0.0" } } diff --git a/packages/multer/test/multer/interceptors/file-fields.interceptor.spec.ts b/packages/platform-express/test/multer/interceptors/file-fields.interceptor.spec.ts similarity index 87% rename from packages/multer/test/multer/interceptors/file-fields.interceptor.spec.ts rename to packages/platform-express/test/multer/interceptors/file-fields.interceptor.spec.ts index 24b83d895e6..7ab6fea50df 100644 --- a/packages/multer/test/multer/interceptors/file-fields.interceptor.spec.ts +++ b/packages/platform-express/test/multer/interceptors/file-fields.interceptor.spec.ts @@ -3,8 +3,7 @@ import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-hos import { expect } from 'chai'; import { of } from 'rxjs'; import * as sinon from 'sinon'; -import { FileFieldsInterceptor } from '../../../interceptors/file-fields.interceptor'; -import { fakeMulter } from '../fake-multer'; +import { FileFieldsInterceptor } from '../../../multer/interceptors/file-fields.interceptor'; describe('FileFieldsInterceptor', () => { it('should return metatype with expected structure', async () => { @@ -30,7 +29,7 @@ describe('FileFieldsInterceptor', () => { { name: fieldName1, maxCount: maxCount1 }, { name: fieldName2, maxCount: maxCount2 }, ]; - const target = new (FileFieldsInterceptor(argument))(null, fakeMulter); + const target = new (FileFieldsInterceptor(argument))(); const callback = (req, res, next) => next(); const fieldsSpy = sinon @@ -51,7 +50,7 @@ describe('FileFieldsInterceptor', () => { { name: fieldName1, maxCount: maxCount1 }, { name: fieldName2, maxCount: maxCount2 }, ]; - const target = new (FileFieldsInterceptor(argument))(null, fakeMulter); + const target = new (FileFieldsInterceptor(argument))(); const err = {}; const callback = (req, res, next) => next(err); diff --git a/packages/multer/test/multer/interceptors/file.interceptor.spec.ts b/packages/platform-express/test/multer/interceptors/file.interceptor.spec.ts similarity index 84% rename from packages/multer/test/multer/interceptors/file.interceptor.spec.ts rename to packages/platform-express/test/multer/interceptors/file.interceptor.spec.ts index c2cd32a1ae3..7f4178db44e 100644 --- a/packages/multer/test/multer/interceptors/file.interceptor.spec.ts +++ b/packages/platform-express/test/multer/interceptors/file.interceptor.spec.ts @@ -3,8 +3,7 @@ import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-hos import { expect } from 'chai'; import { of } from 'rxjs'; import * as sinon from 'sinon'; -import { FileInterceptor } from '../../../interceptors/file.interceptor'; -import { fakeMulter } from '../fake-multer'; +import { FileInterceptor } from '../../../multer/interceptors/file.interceptor'; describe('FileInterceptor', () => { it('should return metatype with expected structure', async () => { @@ -18,11 +17,9 @@ describe('FileInterceptor', () => { handle: () => of('test'), }; }); - it('should call single() with expected params', async () => { const fieldName = 'file'; - const target = new (FileInterceptor(fieldName))(null, fakeMulter); - + const target = new (FileInterceptor(fieldName))(); const callback = (req, res, next) => next(); const singleSpy = sinon .stub((target as any).multer, 'single') @@ -33,10 +30,9 @@ describe('FileInterceptor', () => { expect(singleSpy.called).to.be.true; expect(singleSpy.calledWith(fieldName)).to.be.true; }); - it('should transform exception', async () => { const fieldName = 'file'; - const target = new (FileInterceptor(fieldName))(null, fakeMulter); + const target = new (FileInterceptor(fieldName))(); const err = {}; const callback = (req, res, next) => next(err); diff --git a/packages/multer/test/multer/interceptors/files.interceptor.spec.ts b/packages/platform-express/test/multer/interceptors/files.interceptor.spec.ts similarity index 86% rename from packages/multer/test/multer/interceptors/files.interceptor.spec.ts rename to packages/platform-express/test/multer/interceptors/files.interceptor.spec.ts index 6a7c5aa355d..c6caadd62aa 100644 --- a/packages/multer/test/multer/interceptors/files.interceptor.spec.ts +++ b/packages/platform-express/test/multer/interceptors/files.interceptor.spec.ts @@ -3,8 +3,7 @@ import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-hos import { expect } from 'chai'; import { of } from 'rxjs'; import * as sinon from 'sinon'; -import { FilesInterceptor } from '../../../interceptors/files.interceptor'; -import { fakeMulter } from '../fake-multer'; +import { FilesInterceptor } from '../../../multer/interceptors/files.interceptor'; describe('FilesInterceptor', () => { it('should return metatype with expected structure', async () => { @@ -21,10 +20,7 @@ describe('FilesInterceptor', () => { it('should call array() with expected params', async () => { const fieldName = 'file'; const maxCount = 10; - const target = new (FilesInterceptor(fieldName, maxCount))( - null, - fakeMulter, - ); + const target = new (FilesInterceptor(fieldName, maxCount))(); const callback = (req, res, next) => next(); const arraySpy = sinon @@ -38,7 +34,7 @@ describe('FilesInterceptor', () => { }); it('should transform exception', async () => { const fieldName = 'file'; - const target = new (FilesInterceptor(fieldName))(null, fakeMulter); + const target = new (FilesInterceptor(fieldName))(); const err = {}; const callback = (req, res, next) => next(err); diff --git a/packages/multer/test/multer/multer.module.spec.ts b/packages/platform-express/test/multer/multer/multer.module.spec.ts similarity index 55% rename from packages/multer/test/multer/multer.module.spec.ts rename to packages/platform-express/test/multer/multer/multer.module.spec.ts index 6f2ddc16d1d..759110a927e 100644 --- a/packages/multer/test/multer/multer.module.spec.ts +++ b/packages/platform-express/test/multer/multer/multer.module.spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; -import { MULTER_MODULE_ADAPTER, MULTER_MODULE_OPTIONS } from '../../constants'; -import { MulterModule } from '../../multer.module'; +import { MULTER_MODULE_OPTIONS } from '../../../multer/files.constants'; +import { MulterModule } from '../../../multer/multer.module'; describe('MulterModule', () => { describe('register', () => { @@ -9,22 +9,15 @@ describe('MulterModule', () => { const options = { test: 'test', }; + const dynamicModule = MulterModule.register(options as any); - const multer = { test: 'Fake Multer Instance' }; - - const dynamicModule = MulterModule.register(options as any, multer); - - expect(dynamicModule.providers).to.have.length(2); + expect(dynamicModule.providers).to.have.length(1); expect(dynamicModule.imports).to.be.undefined; expect(dynamicModule.exports).to.include(MULTER_MODULE_OPTIONS); expect(dynamicModule.providers).to.deep.include({ provide: MULTER_MODULE_OPTIONS, useValue: options, }); - expect(dynamicModule.providers).to.deep.include({ - provide: MULTER_MODULE_ADAPTER, - useValue: multer, - }); }); }); @@ -32,14 +25,12 @@ describe('MulterModule', () => { describe('when useFactory', () => { it('should provide an options', () => { const options: any = {}; - const multer = { test: 'Fake Multer Instance' }; - const asyncOptions = { useFactory: () => options, }; - const dynamicModule = MulterModule.registerAsync(asyncOptions, multer); + const dynamicModule = MulterModule.registerAsync(asyncOptions); - expect(dynamicModule.providers).to.have.length(2); + expect(dynamicModule.providers).to.have.length(1); expect(dynamicModule.imports).to.be.undefined; expect(dynamicModule.exports).to.include(MULTER_MODULE_OPTIONS); expect(dynamicModule.providers).to.deep.include({ @@ -47,65 +38,38 @@ describe('MulterModule', () => { useFactory: asyncOptions.useFactory, inject: [], }); - expect(dynamicModule.providers).to.deep.include({ - provide: MULTER_MODULE_ADAPTER, - useValue: multer, - }); }); }); describe('when useExisting', () => { it('should provide an options', () => { - const multer = { test: 'Fake Multer Instance' }; const asyncOptions = { useExisting: Object, }; - const dynamicModule = MulterModule.registerAsync( - asyncOptions as any, - multer, - ); + const dynamicModule = MulterModule.registerAsync(asyncOptions as any); - expect(dynamicModule.providers).to.have.length(2); + expect(dynamicModule.providers).to.have.length(1); expect(dynamicModule.imports).to.be.undefined; expect(dynamicModule.exports).to.include(MULTER_MODULE_OPTIONS); - expect(dynamicModule.exports).to.include(MULTER_MODULE_ADAPTER); - expect(dynamicModule.providers).to.deep.include({ - provide: MULTER_MODULE_ADAPTER, - useValue: multer, - }); }); }); describe('when useClass', () => { it('should provide an options', () => { - const multer = { test: 'Fake Multer Instance' }; const asyncOptions = { useClass: Object, }; - const dynamicModule = MulterModule.registerAsync( - asyncOptions as any, - multer, - ); + const dynamicModule = MulterModule.registerAsync(asyncOptions as any); - expect(dynamicModule.providers).to.have.length(3); + expect(dynamicModule.providers).to.have.length(2); expect(dynamicModule.imports).to.be.undefined; expect(dynamicModule.exports).to.include(MULTER_MODULE_OPTIONS); - expect(dynamicModule.exports).to.include(MULTER_MODULE_ADAPTER); - expect(dynamicModule.providers).to.deep.include({ - provide: MULTER_MODULE_ADAPTER, - useValue: multer, - }); }); - it('provider should call "createMulterOptions"', async () => { - const multer = { test: 'Fake Multer Instance' }; const asyncOptions = { useClass: Object, }; - const dynamicModule = MulterModule.registerAsync( - asyncOptions as any, - multer, - ); + const dynamicModule = MulterModule.registerAsync(asyncOptions as any); const optionsFactory = { createMulterOptions: sinon.spy(), }; @@ -113,10 +77,6 @@ describe('MulterModule', () => { optionsFactory, ); expect(optionsFactory.createMulterOptions.called).to.be.true; - expect(dynamicModule.providers).to.deep.include({ - provide: MULTER_MODULE_ADAPTER, - useValue: multer, - }); }); }); }); diff --git a/packages/multer/test/multer/multer.utils.spec.ts b/packages/platform-express/test/multer/multer/multer.utils.spec.ts similarity index 89% rename from packages/multer/test/multer/multer.utils.spec.ts rename to packages/platform-express/test/multer/multer/multer.utils.spec.ts index ddd2cd9da66..7ad1eec9069 100644 --- a/packages/multer/test/multer/multer.utils.spec.ts +++ b/packages/platform-express/test/multer/multer/multer.utils.spec.ts @@ -1,11 +1,11 @@ -import { expect } from 'chai'; -import { multerExceptions } from '../../constants'; -import { transformException } from '../../utils'; import { BadRequestException, HttpException, PayloadTooLargeException, } from '@nestjs/common'; +import { expect } from 'chai'; +import { multerExceptions } from '../../../multer/multer/multer.constants'; +import { transformException } from '../../../multer/multer/multer.utils'; describe('transformException', () => { describe('if error does not exist', () => { diff --git a/packages/platform-fastify/index.ts b/packages/platform-fastify/index.ts index 886f16d700b..2b00533258f 100644 --- a/packages/platform-fastify/index.ts +++ b/packages/platform-fastify/index.ts @@ -7,4 +7,4 @@ export * from './adapters'; export * from './interfaces'; -export { default as multerAdapter } from 'fastify-multer'; +export * from './multer'; diff --git a/packages/platform-fastify/multer/files.constants.ts b/packages/platform-fastify/multer/files.constants.ts new file mode 100644 index 00000000000..fd2ef8091cc --- /dev/null +++ b/packages/platform-fastify/multer/files.constants.ts @@ -0,0 +1 @@ +export const MULTER_MODULE_OPTIONS = 'MULTER_MODULE_OPTIONS'; diff --git a/packages/platform-fastify/multer/index.ts b/packages/platform-fastify/multer/index.ts new file mode 100644 index 00000000000..7afd4aab899 --- /dev/null +++ b/packages/platform-fastify/multer/index.ts @@ -0,0 +1,3 @@ +export * from './interceptors'; +export * from './interfaces'; +export * from './multer.module'; diff --git a/packages/platform-fastify/multer/interceptors/file-fields.interceptor.ts b/packages/platform-fastify/multer/interceptors/file-fields.interceptor.ts new file mode 100644 index 00000000000..0a523f74187 --- /dev/null +++ b/packages/platform-fastify/multer/interceptors/file-fields.interceptor.ts @@ -0,0 +1,64 @@ +import * as multer from 'fastify-multer'; +import { Observable } from 'rxjs'; +import { MULTER_MODULE_OPTIONS } from '../files.constants'; +import { MulterModuleOptions } from '../interfaces'; +import { transformException } from '../multer/multer.utils'; +import { + CallHandler, + ExecutionContext, + Inject, + mixin, + NestInterceptor, + Optional, + Type, +} from '@nestjs/common'; +import { + MulterField, + MulterOptions, +} from '../interfaces/multer-options.interface'; + +type MulterInstance = any; + +export function FileFieldsInterceptor( + uploadFields: MulterField[], + localOptions?: MulterOptions, +): Type { + class MixinInterceptor implements NestInterceptor { + protected multer: MulterInstance; + + constructor( + @Optional() + @Inject(MULTER_MODULE_OPTIONS) + options: MulterModuleOptions = {}, + ) { + this.multer = (multer as any)({ + ...options, + ...localOptions, + }); + } + + async intercept( + context: ExecutionContext, + next: CallHandler, + ): Promise> { + const ctx = context.switchToHttp(); + + await new Promise((resolve, reject) => + this.multer.fields(uploadFields)( + ctx.getRequest(), + ctx.getResponse(), + (err: any) => { + if (err) { + const error = transformException(err); + return reject(error); + } + resolve(); + }, + ), + ); + return next.handle(); + } + } + const Interceptor = mixin(MixinInterceptor); + return Interceptor as Type; +} diff --git a/packages/platform-fastify/multer/interceptors/file.interceptor.ts b/packages/platform-fastify/multer/interceptors/file.interceptor.ts new file mode 100644 index 00000000000..651ae9d28d7 --- /dev/null +++ b/packages/platform-fastify/multer/interceptors/file.interceptor.ts @@ -0,0 +1,61 @@ +import * as multer from 'fastify-multer'; +import { Observable } from 'rxjs'; +import { MULTER_MODULE_OPTIONS } from '../files.constants'; +import { MulterModuleOptions } from '../interfaces'; +import { MulterOptions } from '../interfaces/multer-options.interface'; +import { transformException } from '../multer/multer.utils'; +import { + CallHandler, + ExecutionContext, + Inject, + mixin, + NestInterceptor, + Optional, + Type, +} from '@nestjs/common'; + +type MulterInstance = any; + +export function FileInterceptor( + fieldName: string, + localOptions?: MulterOptions, +): Type { + class MixinInterceptor implements NestInterceptor { + protected multer: MulterInstance; + + constructor( + @Optional() + @Inject(MULTER_MODULE_OPTIONS) + options: MulterModuleOptions = {}, + ) { + this.multer = (multer as any)({ + ...options, + ...localOptions, + }); + } + + async intercept( + context: ExecutionContext, + next: CallHandler, + ): Promise> { + const ctx = context.switchToHttp(); + + await new Promise((resolve, reject) => + this.multer.single(fieldName)( + ctx.getRequest(), + ctx.getResponse(), + (err: any) => { + if (err) { + const error = transformException(err); + return reject(error); + } + resolve(); + }, + ), + ); + return next.handle(); + } + } + const Interceptor = mixin(MixinInterceptor); + return Interceptor as Type; +} diff --git a/packages/platform-fastify/multer/interceptors/files.interceptor.ts b/packages/platform-fastify/multer/interceptors/files.interceptor.ts new file mode 100644 index 00000000000..6b99794b1a0 --- /dev/null +++ b/packages/platform-fastify/multer/interceptors/files.interceptor.ts @@ -0,0 +1,62 @@ +import * as multer from 'fastify-multer'; +import { Observable } from 'rxjs'; +import { MULTER_MODULE_OPTIONS } from '../files.constants'; +import { MulterModuleOptions } from '../interfaces'; +import { MulterOptions } from '../interfaces/multer-options.interface'; +import { transformException } from '../multer/multer.utils'; +import { + CallHandler, + ExecutionContext, + Inject, + mixin, + NestInterceptor, + Optional, + Type, +} from '@nestjs/common'; + +type MulterInstance = any; + +export function FilesInterceptor( + fieldName: string, + maxCount?: number, + localOptions?: MulterOptions, +): Type { + class MixinInterceptor implements NestInterceptor { + protected multer: MulterInstance; + + constructor( + @Optional() + @Inject(MULTER_MODULE_OPTIONS) + options: MulterModuleOptions = {}, + ) { + this.multer = (multer as any)({ + ...options, + ...localOptions, + }); + } + + async intercept( + context: ExecutionContext, + next: CallHandler, + ): Promise> { + const ctx = context.switchToHttp(); + + await new Promise((resolve, reject) => + this.multer.array(fieldName, maxCount)( + ctx.getRequest(), + ctx.getResponse(), + (err: any) => { + if (err) { + const error = transformException(err); + return reject(error); + } + resolve(); + }, + ), + ); + return next.handle(); + } + } + const Interceptor = mixin(MixinInterceptor); + return Interceptor as Type; +} diff --git a/packages/platform-fastify/multer/interceptors/index.ts b/packages/platform-fastify/multer/interceptors/index.ts new file mode 100644 index 00000000000..3de0e24ce6d --- /dev/null +++ b/packages/platform-fastify/multer/interceptors/index.ts @@ -0,0 +1,3 @@ +export * from './file-fields.interceptor'; +export * from './file.interceptor'; +export * from './files.interceptor'; diff --git a/packages/platform-fastify/multer/interfaces/files-upload-module.interface.ts b/packages/platform-fastify/multer/interfaces/files-upload-module.interface.ts new file mode 100644 index 00000000000..832e8ed22a5 --- /dev/null +++ b/packages/platform-fastify/multer/interfaces/files-upload-module.interface.ts @@ -0,0 +1,19 @@ +import { Type } from '@nestjs/common'; +import { ModuleMetadata } from '@nestjs/common/interfaces'; +import { MulterOptions } from '../interfaces/multer-options.interface'; + +export interface MulterModuleOptions extends MulterOptions {} + +export interface MulterOptionsFactory { + createMulterOptions(): Promise | MulterModuleOptions; +} + +export interface MulterModuleAsyncOptions + extends Pick { + useExisting?: Type; + useClass?: Type; + useFactory?: ( + ...args: any[] + ) => Promise | MulterModuleOptions; + inject?: any[]; +} diff --git a/packages/platform-fastify/multer/interfaces/index.ts b/packages/platform-fastify/multer/interfaces/index.ts new file mode 100644 index 00000000000..be94ec24afd --- /dev/null +++ b/packages/platform-fastify/multer/interfaces/index.ts @@ -0,0 +1 @@ +export * from './files-upload-module.interface'; diff --git a/packages/platform-fastify/multer/interfaces/multer-options.interface.ts b/packages/platform-fastify/multer/interfaces/multer-options.interface.ts new file mode 100644 index 00000000000..0b8e80e38a3 --- /dev/null +++ b/packages/platform-fastify/multer/interfaces/multer-options.interface.ts @@ -0,0 +1,61 @@ +/** + * @see https://github.com/expressjs/multer + */ +export interface MulterOptions { + dest?: string; + /** The storage engine to use for uploaded files. */ + storage?: any; + /** + * An object specifying the size limits of the following optional properties. This object is passed to busboy + * directly, and the details of properties can be found on https://github.com/mscdex/busboy#busboy-methods + */ + limits?: { + /** Max field name size (Default: 100 bytes) */ + fieldNameSize?: number; + /** Max field value size (Default: 1MB) */ + fieldSize?: number; + /** Max number of non- file fields (Default: Infinity) */ + fields?: number; + /** For multipart forms, the max file size (in bytes)(Default: Infinity) */ + fileSize?: number; + /** For multipart forms, the max number of file fields (Default: Infinity) */ + files?: number; + /** For multipart forms, the max number of parts (fields + files)(Default: Infinity) */ + parts?: number; + /** For multipart forms, the max number of header key=> value pairs to parse Default: 2000(same as node's http). */ + headerPairs?: number; + /** Keep the full path of files instead of just the base name (Default: false) */ + preservePath?: boolean; + }; + fileFilter?( + req: any, + file: { + /** Field name specified in the form */ + fieldname: string; + /** Name of the file on the user's computer */ + originalname: string; + /** Encoding type of the file */ + encoding: string; + /** Mime type of the file */ + mimetype: string; + /** Size of the file in bytes */ + size: number; + /** The folder to which the file has been saved (DiskStorage) */ + destination: string; + /** The name of the file within the destination (DiskStorage) */ + filename: string; + /** Location of the uploaded file (DiskStorage) */ + path: string; + /** A Buffer of the entire file (MemoryStorage) */ + buffer: Buffer; + }, + callback: (error: Error | null, acceptFile: boolean) => void, + ): void; +} + +export interface MulterField { + /** The field name. */ + name: string; + /** Optional maximum number of files per field to accept. */ + maxCount?: number; +} diff --git a/packages/platform-fastify/multer/multer.module.ts b/packages/platform-fastify/multer/multer.module.ts new file mode 100644 index 00000000000..5d28a99e867 --- /dev/null +++ b/packages/platform-fastify/multer/multer.module.ts @@ -0,0 +1,60 @@ +import { DynamicModule, Module, Provider } from '@nestjs/common'; +import { MULTER_MODULE_OPTIONS } from './files.constants'; +import { + MulterModuleAsyncOptions, + MulterModuleOptions, + MulterOptionsFactory, +} from './interfaces/files-upload-module.interface'; + +@Module({}) +export class MulterModule { + static register(options: MulterModuleOptions = {}): DynamicModule { + return { + module: MulterModule, + providers: [{ provide: MULTER_MODULE_OPTIONS, useValue: options }], + exports: [MULTER_MODULE_OPTIONS], + }; + } + + static registerAsync(options: MulterModuleAsyncOptions): DynamicModule { + return { + module: MulterModule, + imports: options.imports, + providers: this.createAsyncProviders(options), + exports: [MULTER_MODULE_OPTIONS], + }; + } + + private static createAsyncProviders( + options: MulterModuleAsyncOptions, + ): Provider[] { + if (options.useExisting || options.useFactory) { + return [this.createAsyncOptionsProvider(options)]; + } + return [ + this.createAsyncOptionsProvider(options), + { + provide: options.useClass, + useClass: options.useClass, + }, + ]; + } + + private static createAsyncOptionsProvider( + options: MulterModuleAsyncOptions, + ): Provider { + if (options.useFactory) { + return { + provide: MULTER_MODULE_OPTIONS, + useFactory: options.useFactory, + inject: options.inject || [], + }; + } + return { + provide: MULTER_MODULE_OPTIONS, + useFactory: async (optionsFactory: MulterOptionsFactory) => + optionsFactory.createMulterOptions(), + inject: [options.useExisting || options.useClass], + }; + } +} diff --git a/packages/platform-fastify/multer/multer/multer.constants.ts b/packages/platform-fastify/multer/multer/multer.constants.ts new file mode 100644 index 00000000000..e3c7b4557f2 --- /dev/null +++ b/packages/platform-fastify/multer/multer/multer.constants.ts @@ -0,0 +1,9 @@ +export const multerExceptions = { + LIMIT_PART_COUNT: 'Too many parts', + LIMIT_FILE_SIZE: 'File too large', + LIMIT_FILE_COUNT: 'Too many files', + LIMIT_FIELD_KEY: 'Field name too long', + LIMIT_FIELD_VALUE: 'Field value too long', + LIMIT_FIELD_COUNT: 'Too many fields', + LIMIT_UNEXPECTED_FILE: 'Unexpected field', +}; diff --git a/packages/platform-fastify/multer/multer/multer.utils.ts b/packages/platform-fastify/multer/multer/multer.utils.ts new file mode 100644 index 00000000000..81e19739ca9 --- /dev/null +++ b/packages/platform-fastify/multer/multer/multer.utils.ts @@ -0,0 +1,24 @@ +import { + BadRequestException, + HttpException, + PayloadTooLargeException, +} from '@nestjs/common'; +import { multerExceptions } from './multer.constants'; + +export function transformException(error: Error | undefined) { + if (!error || error instanceof HttpException) { + return error; + } + switch (error.message) { + case multerExceptions.LIMIT_FILE_SIZE: + return new PayloadTooLargeException(error.message); + case multerExceptions.LIMIT_FILE_COUNT: + case multerExceptions.LIMIT_FIELD_KEY: + case multerExceptions.LIMIT_FIELD_VALUE: + case multerExceptions.LIMIT_FIELD_COUNT: + case multerExceptions.LIMIT_UNEXPECTED_FILE: + case multerExceptions.LIMIT_PART_COUNT: + return new BadRequestException(error.message); + } + return error; +} diff --git a/packages/platform-fastify/package.json b/packages/platform-fastify/package.json index 0d8654a9619..13fd6a18bd8 100644 --- a/packages/platform-fastify/package.json +++ b/packages/platform-fastify/package.json @@ -21,7 +21,6 @@ }, "peerDependencies": { "@nestjs/common": "^6.0.0", - "@nestjs/core": "^6.0.0", - "@nestjs/multer": "^6.0.0" + "@nestjs/core": "^6.0.0" } } diff --git a/packages/platform-fastify/test/multer/interceptors/file-fields.interceptor.spec.ts b/packages/platform-fastify/test/multer/interceptors/file-fields.interceptor.spec.ts new file mode 100644 index 00000000000..7ab6fea50df --- /dev/null +++ b/packages/platform-fastify/test/multer/interceptors/file-fields.interceptor.spec.ts @@ -0,0 +1,65 @@ +import { CallHandler } from '@nestjs/common'; +import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; +import { expect } from 'chai'; +import { of } from 'rxjs'; +import * as sinon from 'sinon'; +import { FileFieldsInterceptor } from '../../../multer/interceptors/file-fields.interceptor'; + +describe('FileFieldsInterceptor', () => { + it('should return metatype with expected structure', async () => { + const targetClass = FileFieldsInterceptor([ + { name: 'file', maxCount: 1 }, + { name: 'anotherFile', maxCount: 1 }, + ]); + expect(targetClass.prototype.intercept).to.not.be.undefined; + }); + describe('intercept', () => { + let handler: CallHandler; + beforeEach(() => { + handler = { + handle: () => of('test'), + }; + }); + it('should call object with expected params', async () => { + const fieldName1 = 'file'; + const maxCount1 = 1; + const fieldName2 = 'anotherFile'; + const maxCount2 = 2; + const argument = [ + { name: fieldName1, maxCount: maxCount1 }, + { name: fieldName2, maxCount: maxCount2 }, + ]; + const target = new (FileFieldsInterceptor(argument))(); + + const callback = (req, res, next) => next(); + const fieldsSpy = sinon + .stub((target as any).multer, 'fields') + .returns(callback); + + await target.intercept(new ExecutionContextHost([]), handler); + + expect(fieldsSpy.called).to.be.true; + expect(fieldsSpy.calledWith(argument)).to.be.true; + }); + it('should transform exception', async () => { + const fieldName1 = 'file'; + const maxCount1 = 1; + const fieldName2 = 'anotherFile'; + const maxCount2 = 2; + const argument = [ + { name: fieldName1, maxCount: maxCount1 }, + { name: fieldName2, maxCount: maxCount2 }, + ]; + const target = new (FileFieldsInterceptor(argument))(); + const err = {}; + const callback = (req, res, next) => next(err); + + (target as any).fields = { + array: () => callback, + }; + (target.intercept(new ExecutionContextHost([]), handler) as any).catch( + error => expect(error).to.not.be.undefined, + ); + }); + }); +}); diff --git a/packages/platform-fastify/test/multer/interceptors/file.interceptor.spec.ts b/packages/platform-fastify/test/multer/interceptors/file.interceptor.spec.ts new file mode 100644 index 00000000000..7f4178db44e --- /dev/null +++ b/packages/platform-fastify/test/multer/interceptors/file.interceptor.spec.ts @@ -0,0 +1,47 @@ +import { CallHandler } from '@nestjs/common'; +import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; +import { expect } from 'chai'; +import { of } from 'rxjs'; +import * as sinon from 'sinon'; +import { FileInterceptor } from '../../../multer/interceptors/file.interceptor'; + +describe('FileInterceptor', () => { + it('should return metatype with expected structure', async () => { + const targetClass = FileInterceptor('file'); + expect(targetClass.prototype.intercept).to.not.be.undefined; + }); + describe('intercept', () => { + let handler: CallHandler; + beforeEach(() => { + handler = { + handle: () => of('test'), + }; + }); + it('should call single() with expected params', async () => { + const fieldName = 'file'; + const target = new (FileInterceptor(fieldName))(); + const callback = (req, res, next) => next(); + const singleSpy = sinon + .stub((target as any).multer, 'single') + .returns(callback); + + await target.intercept(new ExecutionContextHost([]), handler); + + expect(singleSpy.called).to.be.true; + expect(singleSpy.calledWith(fieldName)).to.be.true; + }); + it('should transform exception', async () => { + const fieldName = 'file'; + const target = new (FileInterceptor(fieldName))(); + const err = {}; + const callback = (req, res, next) => next(err); + + (target as any).multer = { + single: () => callback, + }; + (target.intercept(new ExecutionContextHost([]), handler) as any).catch( + error => expect(error).to.not.be.undefined, + ); + }); + }); +}); diff --git a/packages/platform-fastify/test/multer/interceptors/files.interceptor.spec.ts b/packages/platform-fastify/test/multer/interceptors/files.interceptor.spec.ts new file mode 100644 index 00000000000..c6caadd62aa --- /dev/null +++ b/packages/platform-fastify/test/multer/interceptors/files.interceptor.spec.ts @@ -0,0 +1,49 @@ +import { CallHandler } from '@nestjs/common'; +import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; +import { expect } from 'chai'; +import { of } from 'rxjs'; +import * as sinon from 'sinon'; +import { FilesInterceptor } from '../../../multer/interceptors/files.interceptor'; + +describe('FilesInterceptor', () => { + it('should return metatype with expected structure', async () => { + const targetClass = FilesInterceptor('file'); + expect(targetClass.prototype.intercept).to.not.be.undefined; + }); + describe('intercept', () => { + let handler: CallHandler; + beforeEach(() => { + handler = { + handle: () => of('test'), + }; + }); + it('should call array() with expected params', async () => { + const fieldName = 'file'; + const maxCount = 10; + const target = new (FilesInterceptor(fieldName, maxCount))(); + + const callback = (req, res, next) => next(); + const arraySpy = sinon + .stub((target as any).multer, 'array') + .returns(callback); + + await target.intercept(new ExecutionContextHost([]), handler); + + expect(arraySpy.called).to.be.true; + expect(arraySpy.calledWith(fieldName, maxCount)).to.be.true; + }); + it('should transform exception', async () => { + const fieldName = 'file'; + const target = new (FilesInterceptor(fieldName))(); + const err = {}; + const callback = (req, res, next) => next(err); + + (target as any).multer = { + array: () => callback, + }; + (target.intercept(new ExecutionContextHost([]), handler) as any).catch( + error => expect(error).to.not.be.undefined, + ); + }); + }); +}); diff --git a/packages/platform-fastify/test/multer/multer/multer.module.spec.ts b/packages/platform-fastify/test/multer/multer/multer.module.spec.ts new file mode 100644 index 00000000000..759110a927e --- /dev/null +++ b/packages/platform-fastify/test/multer/multer/multer.module.spec.ts @@ -0,0 +1,83 @@ +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import { MULTER_MODULE_OPTIONS } from '../../../multer/files.constants'; +import { MulterModule } from '../../../multer/multer.module'; + +describe('MulterModule', () => { + describe('register', () => { + it('should provide an options', () => { + const options = { + test: 'test', + }; + const dynamicModule = MulterModule.register(options as any); + + expect(dynamicModule.providers).to.have.length(1); + expect(dynamicModule.imports).to.be.undefined; + expect(dynamicModule.exports).to.include(MULTER_MODULE_OPTIONS); + expect(dynamicModule.providers).to.deep.include({ + provide: MULTER_MODULE_OPTIONS, + useValue: options, + }); + }); + }); + + describe('register async', () => { + describe('when useFactory', () => { + it('should provide an options', () => { + const options: any = {}; + const asyncOptions = { + useFactory: () => options, + }; + const dynamicModule = MulterModule.registerAsync(asyncOptions); + + expect(dynamicModule.providers).to.have.length(1); + expect(dynamicModule.imports).to.be.undefined; + expect(dynamicModule.exports).to.include(MULTER_MODULE_OPTIONS); + expect(dynamicModule.providers).to.deep.include({ + provide: MULTER_MODULE_OPTIONS, + useFactory: asyncOptions.useFactory, + inject: [], + }); + }); + }); + + describe('when useExisting', () => { + it('should provide an options', () => { + const asyncOptions = { + useExisting: Object, + }; + const dynamicModule = MulterModule.registerAsync(asyncOptions as any); + + expect(dynamicModule.providers).to.have.length(1); + expect(dynamicModule.imports).to.be.undefined; + expect(dynamicModule.exports).to.include(MULTER_MODULE_OPTIONS); + }); + }); + + describe('when useClass', () => { + it('should provide an options', () => { + const asyncOptions = { + useClass: Object, + }; + const dynamicModule = MulterModule.registerAsync(asyncOptions as any); + + expect(dynamicModule.providers).to.have.length(2); + expect(dynamicModule.imports).to.be.undefined; + expect(dynamicModule.exports).to.include(MULTER_MODULE_OPTIONS); + }); + it('provider should call "createMulterOptions"', async () => { + const asyncOptions = { + useClass: Object, + }; + const dynamicModule = MulterModule.registerAsync(asyncOptions as any); + const optionsFactory = { + createMulterOptions: sinon.spy(), + }; + await ((dynamicModule.providers[0] as any).useFactory as any)( + optionsFactory, + ); + expect(optionsFactory.createMulterOptions.called).to.be.true; + }); + }); + }); +}); diff --git a/packages/platform-fastify/test/multer/multer/multer.utils.spec.ts b/packages/platform-fastify/test/multer/multer/multer.utils.spec.ts new file mode 100644 index 00000000000..7ad1eec9069 --- /dev/null +++ b/packages/platform-fastify/test/multer/multer/multer.utils.spec.ts @@ -0,0 +1,41 @@ +import { + BadRequestException, + HttpException, + PayloadTooLargeException, +} from '@nestjs/common'; +import { expect } from 'chai'; +import { multerExceptions } from '../../../multer/multer/multer.constants'; +import { transformException } from '../../../multer/multer/multer.utils'; + +describe('transformException', () => { + describe('if error does not exist', () => { + it('behave as identity', () => { + const err = undefined; + expect(transformException(err)).to.be.eq(err); + }); + }); + describe('if error is instance of HttpException', () => { + it('behave as identity', () => { + const err = new HttpException('response', 500); + expect(transformException(err)).to.be.eq(err); + }); + }); + describe('if error exists and is not instance of HttpException', () => { + describe('and is LIMIT_FILE_SIZE exception', () => { + it('should return "PayloadTooLargeException"', () => { + const err = { message: multerExceptions.LIMIT_FILE_SIZE }; + expect(transformException(err as any)).to.be.instanceof( + PayloadTooLargeException, + ); + }); + }); + describe('and is multer exception but not a LIMIT_FILE_SIZE', () => { + it('should return "BadRequestException"', () => { + const err = { message: multerExceptions.LIMIT_FIELD_KEY }; + expect(transformException(err as any)).to.be.instanceof( + BadRequestException, + ); + }); + }); + }); +});