From ba761c1488a869db28842711fe997a924d35f07a Mon Sep 17 00:00:00 2001 From: Patrick Acioli Date: Wed, 17 Jul 2024 20:42:55 -0300 Subject: [PATCH] fix(fastify-adapter): middleware not executed when root path is excluded --- .../e2e/middleware-fastify.spec.ts | 131 ++++++++++++++++++ .../adapters/fastify-adapter.ts | 12 ++ 2 files changed, 143 insertions(+) diff --git a/integration/hello-world/e2e/middleware-fastify.spec.ts b/integration/hello-world/e2e/middleware-fastify.spec.ts index 36a6d64e024..a0d9d9f211e 100644 --- a/integration/hello-world/e2e/middleware-fastify.spec.ts +++ b/integration/hello-world/e2e/middleware-fastify.spec.ts @@ -16,6 +16,8 @@ import { import { Test } from '@nestjs/testing'; import { expect } from 'chai'; import { AppModule } from '../src/app.module'; +import * as request from 'supertest'; +import { FastifyRequest } from 'fastify'; describe('Middleware (FastifyAdapter)', () => { let app: NestFastifyApplication; @@ -398,4 +400,133 @@ describe('Middleware (FastifyAdapter)', () => { await app.close(); }); }); + + describe('should have data attached in middleware', () => { + @Controller() + class DataController { + @Get('data') + async data(@Req() req: FastifyRequest['raw']) { + return { + success: true, + extras: req?.['raw']?.extras, + pong: req?.['raw']?.headers?.ping, + }; + } + @Get('pong') + async pong(@Req() req: FastifyRequest['raw']) { + return { success: true, pong: req?.['raw']?.headers?.ping }; + } + + @Get('') + async rootPath(@Req() req: FastifyRequest['raw']) { + return { success: true, root: true }; + } + } + + @Module({ + controllers: [DataController], + }) + class DataModule implements NestModule { + configure(consumer: MiddlewareConsumer) { + consumer + .apply((req, res, next) => { + req.extras = { data: 'Data attached in middleware' }; + req.headers['ping'] = 'pong'; + next(); + }) + .forRoutes('*'); + } + } + + beforeEach(async () => { + app = ( + await Test.createTestingModule({ + imports: [DataModule], + }).compile() + ).createNestApplication(new FastifyAdapter()); + }); + + it(`GET forRoutes('*') with global prefix`, async () => { + app.setGlobalPrefix('/api'); + await app.init(); + await app.getHttpAdapter().getInstance().ready(); + return app + .inject({ + method: 'GET', + url: '/api/pong', + }) + .then(({ payload }) => + expect(payload).to.be.eql( + JSON.stringify({ + success: true, + pong: 'pong', + }), + ), + ); + }); + + it(`GET forRoutes('*') without prefix config`, async () => { + await app.init(); + await app.getHttpAdapter().getInstance().ready(); + return app + .inject({ + method: 'GET', + url: '/pong', + }) + .then(({ payload }) => + expect(payload).to.be.eql( + JSON.stringify({ + success: true, + pong: 'pong', + }), + ), + ); + }); + + it(`GET forRoutes('*') with global prefix and exclude patterns`, async () => { + app.setGlobalPrefix('/api', { exclude: ['/'] }); + await app.init(); + await app.getHttpAdapter().getInstance().ready(); + + await request(app.getHttpServer()) + .get('/') + .expect(200, { success: true, root: true }); + }); + + it(`GET forRoutes('*') with global prefix and global prefix options`, async () => { + app.setGlobalPrefix('/api', { exclude: ['/'] }); + await app.init(); + await app.getHttpAdapter().getInstance().ready(); + + await request(app.getHttpServer()) + .get('/api/data') + .expect(200, { + success: true, + extras: { data: 'Data attached in middleware' }, + pong: 'pong', + }); + await request(app.getHttpServer()) + .get('/') + .expect(200, { success: true, root: true }); + }); + + it(`GET forRoutes('*') with global prefix that not starts with /`, async () => { + app.setGlobalPrefix('api'); + await app.init(); + await app.getHttpAdapter().getInstance().ready(); + + await request(app.getHttpServer()) + .get('/api/data') + .expect(200, { + success: true, + extras: { data: 'Data attached in middleware' }, + pong: 'pong', + }); + await request(app.getHttpServer()).get('/').expect(404); + }); + + afterEach(async () => { + await app.close(); + }); + }); }); diff --git a/packages/platform-fastify/adapters/fastify-adapter.ts b/packages/platform-fastify/adapters/fastify-adapter.ts index 837eb09e0c5..9843dd34b0c 100644 --- a/packages/platform-fastify/adapters/fastify-adapter.ts +++ b/packages/platform-fastify/adapters/fastify-adapter.ts @@ -131,6 +131,7 @@ export class FastifyAdapter< > = FastifyInstance, > extends AbstractHttpAdapter { protected readonly instance: TInstance; + protected _pathPrefix?: string; private _isParserRegistered: boolean; private isMiddieRegistered: boolean; @@ -521,6 +522,11 @@ export class FastifyAdapter< this.registerJsonContentParser(rawBody); this._isParserRegistered = true; + this._pathPrefix = prefix + ? !prefix.startsWith('/') + ? `/${prefix}` + : prefix + : undefined; } public useBodyParser( @@ -577,6 +583,12 @@ export class FastifyAdapter< // Fallback to "(.*)" to support plugins like GraphQL normalizedPath = normalizedPath === '/(.*)' ? '(.*)' : normalizedPath; + // Normalize the path to support the prefix if it set in application + normalizedPath = + this._pathPrefix && !normalizedPath.startsWith(this._pathPrefix) + ? `${this._pathPrefix}${normalizedPath}(.*)` + : normalizedPath; + let re = pathToRegexp(normalizedPath); re = hasEndOfStringCharacter ? new RegExp(re.source + '$', re.flags) : re;