diff --git a/e2e/express.e2e-spec.ts b/e2e/express.e2e-spec.ts index ca205e23d..25d489f75 100644 --- a/e2e/express.e2e-spec.ts +++ b/e2e/express.e2e-spec.ts @@ -113,7 +113,14 @@ describe('Express Swagger', () => { ); SwaggerModule.setup('api', app, swaggerDocument, { jsonDocumentUrl: JSON_CUSTOM_URL, - yamlDocumentUrl: YAML_CUSTOM_URL + yamlDocumentUrl: YAML_CUSTOM_URL, + patchDocument: (req, res, document) => ({ + ...document, + info: { + ...document.info, + description: (req as Record).query.description + } + }) }); await app.init(); @@ -130,12 +137,24 @@ describe('Express Swagger', () => { expect(Object.keys(response.body).length).toBeGreaterThan(0); }); + it('patched JSON document should be served', async () => { + const response = await request(app.getHttpServer()).get(`${JSON_CUSTOM_URL}?description=My%20custom%20description`); + + expect(response.body.info.description).toBe("My custom description") + }) + it('yaml document should be server in the custom url', async () => { const response = await request(app.getHttpServer()).get(YAML_CUSTOM_URL); expect(response.status).toEqual(200); expect(response.text.length).toBeGreaterThan(0); }); + + it('patched YAML document should be served', async () => { + const response = await request(app.getHttpServer()).get(`${YAML_CUSTOM_URL}?description=My%20custom%20description`); + + expect(response.text).toContain("My custom description") + }) }); describe('custom documents endpoints with global prefix', () => { @@ -209,7 +228,15 @@ describe('Express Swagger', () => { customJsStr: CUSTOM_JS_STR, customfavIcon: CUSTOM_FAVICON, customSiteTitle: CUSTOM_SITE_TITLE, - customCssUrl: CUSTOM_CSS_URL + customCssUrl: CUSTOM_CSS_URL, + patchDocument (req, res, document) { + return { + ...document, + info: { + description: req.query.description + } + } + } }); await app.init(); @@ -249,6 +276,46 @@ describe('Express Swagger', () => { ); }); + it('should patch the OpenAPI document', async () => { + const response: Response = await request(app.getHttpServer()).get('/swagger-ui-init.js?description=Custom%20Swagger%20description%20passed%20by%20query%20param') + expect(response.text).toContain( + `"description": "Custom Swagger description passed by query param"` + ) + }) + + it('should patch the OpenAPI document based on path param of the swagger prefix', async () => { + const app = await NestFactory.create( + ApplicationModule, + new ExpressAdapter(), + { logger: false } + ); + + app.setGlobalPrefix("/:customer/") + + const swaggerDocument = SwaggerModule.createDocument( + app, + builder.build() + ); + + SwaggerModule.setup('/:customer/', app, swaggerDocument, { + patchDocument (req, res, document) { + return { + ...document, + info: { + description: `${req.params.customer}'s API documentation` + } + } + } + }); + + await app.init(); + + const response: Response = await request(app.getHttpServer()).get('/customer-1/swagger-ui-init.js') + + await app.close() + expect(response.text).toContain("customer-1's API documentation") + }) + afterEach(async () => { await app.close(); }); diff --git a/e2e/fastify.e2e-spec.ts b/e2e/fastify.e2e-spec.ts index b40c8eca5..1e90f4a0f 100644 --- a/e2e/fastify.e2e-spec.ts +++ b/e2e/fastify.e2e-spec.ts @@ -214,7 +214,14 @@ describe('Fastify Swagger', () => { customJsStr: CUSTOM_JS_STR, customfavIcon: CUSTOM_FAVICON, customSiteTitle: CUSTOM_SITE_TITLE, - customCssUrl: CUSTOM_CSS_URL + customCssUrl: CUSTOM_CSS_URL, + patchDocument: (req, res, document) => ({ + ...document, + info: { + ...document.info, + description: (req as Record).query.description + } + }) }); await app.init(); @@ -267,6 +274,47 @@ describe('Fastify Swagger', () => { ); }); + it('should patch the OpenAPI document', async function () { + const response: Response = await request(app.getHttpServer()).get( + "/custom/swagger-ui-init.js?description=Custom%20Swagger%20description%20passed%20by%20query%20param" + ) + expect(response.text).toContain( + `"description": "Custom Swagger description passed by query param"` + ) + }) + + it('should patch the OpenAPI document based on path param of the swagger prefix', async () => { + const app = await NestFactory.create( + ApplicationModule, + new FastifyAdapter(), + { logger: false } + ); + + const swaggerDocument = SwaggerModule.createDocument( + app, + builder.build() + ); + + SwaggerModule.setup('/:tenantId/', app, swaggerDocument, { + patchDocument (req, res, document) { + return { + ...document, + info: { + description: `${req.params.tenantId}'s API documentation` + } + } + } + }); + + await app.init(); + await app.getHttpAdapter().getInstance().ready(); + + const response: Response = await request(app.getHttpServer()).get('/tenant-1/swagger-ui-init.js') + + await app.close() + expect(response.text).toContain("tenant-1's API documentation") + }) + afterEach(async () => { await app.close(); }); diff --git a/e2e/manual-e2e.ts b/e2e/manual-e2e.ts index 32ff1dd20..f5d43b75d 100644 --- a/e2e/manual-e2e.ts +++ b/e2e/manual-e2e.ts @@ -123,6 +123,16 @@ async function bootstrap() { } }); + SwaggerModule.setup("/:tenantId/api-docs", app, document, { + patchDocument: (req, res, document1) => ({ + ...document1, + info: { + ...document1.info, + title: `${(req as Record).params.tenantId}'s API document` + } + }) + }) + USE_FASTIFY ? (app as NestFastifyApplication).useStaticAssets({ root: publicFolderPath, diff --git a/lib/interfaces/swagger-custom-options.interface.ts b/lib/interfaces/swagger-custom-options.interface.ts index 5853e5fa1..a35d805a5 100644 --- a/lib/interfaces/swagger-custom-options.interface.ts +++ b/lib/interfaces/swagger-custom-options.interface.ts @@ -18,4 +18,5 @@ export interface SwaggerCustomOptions { urls?: Record<'url' | 'name', string>[]; jsonDocumentUrl?: string; yamlDocumentUrl?: string; + patchDocument?: (req: TRequest, res: TResponse, document: OpenAPIObject) => OpenAPIObject; } diff --git a/lib/swagger-module.ts b/lib/swagger-module.ts index f5138bed3..5f6377039 100644 --- a/lib/swagger-module.ts +++ b/lib/swagger-module.ts @@ -102,6 +102,10 @@ export class SwaggerModule { if (!document) { document = lazyBuildDocument(); + + if (options.swaggerOptions.patchDocument) { + document = options.swaggerOptions.patchDocument(req, res, document) + } } if (!swaggerInitJS) { @@ -126,6 +130,10 @@ export class SwaggerModule { if (!document) { document = lazyBuildDocument(); + + if (options.swaggerOptions.patchDocument) { + document = options.swaggerOptions.patchDocument(req, res, document); + } } if (!swaggerInitJS) { @@ -150,6 +158,10 @@ export class SwaggerModule { if (!document) { document = lazyBuildDocument(); + + if (options.swaggerOptions.patchDocument) { + document = options.swaggerOptions.patchDocument(req, res, document) + } } if (!html) { @@ -199,6 +211,10 @@ export class SwaggerModule { if (!document) { document = lazyBuildDocument(); + + if (options.swaggerOptions.patchDocument) { + document = options.swaggerOptions.patchDocument(req, res, document) + } } if (!jsonDocument) { @@ -213,6 +229,10 @@ export class SwaggerModule { if (!document) { document = lazyBuildDocument(); + + if (options.swaggerOptions.patchDocument) { + document = options.swaggerOptions.patchDocument(req, res, document) + } } if (!yamlDocument) {