Skip to content

Commit c541886

Browse files
Merge pull request #2840 from lucas-gregoire/feat/allow-to-disable-swagger-ui
feat: allow to disable Swagger UI
2 parents 91a7c76 + d56087c commit c541886

File tree

4 files changed

+248
-113
lines changed

4 files changed

+248
-113
lines changed

e2e/express.e2e-spec.ts

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ describe('Express Swagger', () => {
8686
);
8787
SwaggerModule.setup(SWAGGER_RELATIVE_URL, app, swaggerDocument, {
8888
// to showcase that in new implementation u can use custom swagger-ui path. Useful when using e.g. webpack
89-
customSwaggerUiPath: path.resolve(`./node_modules/swagger-ui-dist`),
89+
customSwaggerUiPath: path.resolve(`./node_modules/swagger-ui-dist`)
9090
});
9191

9292
await app.init();
@@ -114,6 +114,55 @@ describe('Express Swagger', () => {
114114
});
115115
});
116116

117+
describe('disabled Swagger UI but served JSON/YAML definitions', () => {
118+
const SWAGGER_RELATIVE_URL = '/apidoc';
119+
120+
beforeEach(async () => {
121+
const swaggerDocument = SwaggerModule.createDocument(
122+
app,
123+
builder.build()
124+
);
125+
SwaggerModule.setup(SWAGGER_RELATIVE_URL, app, swaggerDocument, {
126+
swaggerUiEnabled: false
127+
});
128+
129+
await app.init();
130+
});
131+
132+
afterEach(async () => {
133+
await app.close();
134+
});
135+
136+
it('should serve the JSON definition file', async () => {
137+
const response = await request(app.getHttpServer()).get(
138+
`${SWAGGER_RELATIVE_URL}-json`
139+
);
140+
141+
expect(response.status).toEqual(200);
142+
expect(Object.keys(response.body).length).toBeGreaterThan(0);
143+
});
144+
145+
it('should serve the YAML definition file', async () => {
146+
const response = await request(app.getHttpServer()).get(
147+
`${SWAGGER_RELATIVE_URL}-yaml`
148+
);
149+
150+
expect(response.status).toEqual(200);
151+
expect(response.text.length).toBeGreaterThan(0);
152+
});
153+
154+
it.each([
155+
'/apidoc',
156+
'/apidoc/',
157+
'/apidoc/swagger-ui-bundle.js',
158+
'/apidoc/swagger-ui-init.js'
159+
])('should not serve "%s"', async (file) => {
160+
const response = await request(app.getHttpServer()).get(file);
161+
162+
expect(response.status).toEqual(404);
163+
});
164+
});
165+
117166
describe('custom documents endpoints', () => {
118167
const JSON_CUSTOM_URL = '/apidoc-json';
119168
const YAML_CUSTOM_URL = '/apidoc-yaml';
@@ -154,10 +203,10 @@ describe('Express Swagger', () => {
154203
`${JSON_CUSTOM_URL}?description=My%20custom%20description`
155204
);
156205

157-
expect(response.body.info.description).toBe("My custom description");
206+
expect(response.body.info.description).toBe('My custom description');
158207
});
159208

160-
it('yaml document should be server in the custom url', async () => {
209+
it('yaml document should be served in the custom url', async () => {
161210
const response = await request(app.getHttpServer()).get(YAML_CUSTOM_URL);
162211

163212
expect(response.status).toEqual(200);
@@ -168,7 +217,7 @@ describe('Express Swagger', () => {
168217
const response = await request(app.getHttpServer()).get(
169218
`${YAML_CUSTOM_URL}?description=My%20custom%20description`
170219
);
171-
expect(response.text).toContain("My custom description");
220+
expect(response.text).toContain('My custom description');
172221
});
173222
});
174223

@@ -244,13 +293,17 @@ describe('Express Swagger', () => {
244293
customfavIcon: CUSTOM_FAVICON,
245294
customSiteTitle: CUSTOM_SITE_TITLE,
246295
customCssUrl: CUSTOM_CSS_URL,
247-
patchDocumentOnRequest<ExpressRequest, ExpressResponse> (req, res, document) {
296+
patchDocumentOnRequest<ExpressRequest, ExpressResponse>(
297+
req,
298+
res,
299+
document
300+
) {
248301
return {
249302
...document,
250303
info: {
251304
description: req.query.description
252305
}
253-
}
306+
};
254307
}
255308
});
256309

@@ -313,23 +366,29 @@ describe('Express Swagger', () => {
313366
);
314367

315368
SwaggerModule.setup('/:customer/', app, swaggerDocument, {
316-
patchDocumentOnRequest<ExpressRequest, ExpressResponse> (req, res, document) {
369+
patchDocumentOnRequest<ExpressRequest, ExpressResponse>(
370+
req,
371+
res,
372+
document
373+
) {
317374
return {
318375
...document,
319376
info: {
320377
description: `${req.params.customer}'s API documentation`
321378
}
322-
}
379+
};
323380
}
324381
});
325382

326383
await app.init();
327384

328-
const response: Response = await request(app.getHttpServer()).get('/customer-1/swagger-ui-init.js');
385+
const response: Response = await request(app.getHttpServer()).get(
386+
'/customer-1/swagger-ui-init.js'
387+
);
329388

330389
await app.close();
331390
expect(response.text).toContain("customer-1's API documentation");
332-
})
391+
});
333392

334393
afterEach(async () => {
335394
await app.close();
Lines changed: 90 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,107 @@
11
import { SwaggerUiOptions } from './swagger-ui-options.interface';
2-
import { SwaggerDocumentOptions } from './swagger-document-options.interface';
32
import { OpenAPIObject } from './open-api-spec.interface';
43

54
export interface SwaggerCustomOptions {
5+
/**
6+
* If `true`, Swagger resources paths will be prefixed by the global prefix set through `setGlobalPrefix()`.
7+
* Default: `false`.
8+
* @see https://docs.nestjs.com/faq/global-prefix
9+
*/
610
useGlobalPrefix?: boolean;
11+
12+
/**
13+
* If `false`, only API definitions (JSON and YAML) will be served (on `/{path}-json` and `/{path}-yaml`).
14+
* This is particularly useful if you are already hosting a Swagger UI somewhere else and just want to serve API definitions.
15+
* Default: `true`.
16+
*/
17+
swaggerUiEnabled?: boolean;
18+
19+
/**
20+
* Url point the API definition to load in Swagger UI.
21+
*/
22+
swaggerUrl?: string;
23+
24+
/**
25+
* Path of the JSON API definition to serve.
26+
* Default: `{{path}}-json`.
27+
*/
28+
jsonDocumentUrl?: string;
29+
30+
/**
31+
* Path of the YAML API definition to serve.
32+
* Default: `{{path}}-json`.
33+
*/
34+
yamlDocumentUrl?: string;
35+
36+
/**
37+
* Hook allowing to alter the OpenAPI document before being served.
38+
* It's called after the document is generated and before it is served as JSON & YAML.
39+
*/
40+
patchDocumentOnRequest?: <TRequest = any, TResponse = any>(
41+
req: TRequest,
42+
res: TResponse,
43+
document: OpenAPIObject
44+
) => OpenAPIObject;
45+
46+
/**
47+
* If `true`, the selector of OpenAPI definitions is displayed in the Swagger UI interface.
48+
* Default: `false`.
49+
*/
750
explorer?: boolean;
51+
52+
/**
53+
* Additional Swagger UI options
54+
*/
855
swaggerOptions?: SwaggerUiOptions;
56+
57+
/**
58+
* Custom CSS styles to inject in Swagger UI page.
59+
*/
960
customCss?: string;
61+
62+
/**
63+
* URL(s) of a custom CSS stylesheet to load in Swagger UI page.
64+
*/
1065
customCssUrl?: string | string[];
66+
67+
/**
68+
* URL(s) of custom JavaScript files to load in Swagger UI page.
69+
*/
1170
customJs?: string | string[];
71+
72+
/**
73+
* Custom JavaScript scripts to load in Swagger UI page.
74+
*/
1275
customJsStr?: string | string[];
76+
77+
/**
78+
* Custom favicon for Swagger UI page.
79+
*/
1380
customfavIcon?: string;
14-
customSwaggerUiPath?: string;
15-
swaggerUrl?: string;
81+
82+
/**
83+
* Custom title for Swagger UI page.
84+
*/
1685
customSiteTitle?: string;
86+
87+
/**
88+
* File system path (ex: ./node_modules/swagger-ui-dist) containing static Swagger UI assets.
89+
*/
90+
customSwaggerUiPath?: string;
91+
92+
/**
93+
* @deprecated This property has no effect.
94+
*/
1795
validatorUrl?: string;
96+
97+
/**
98+
* @deprecated This property has no effect.
99+
*/
18100
url?: string;
101+
102+
/**
103+
* @deprecated This property has no effect.
104+
*/
19105
urls?: Record<'url' | 'name', string>[];
20-
jsonDocumentUrl?: string;
21-
yamlDocumentUrl?: string;
22-
patchDocumentOnRequest?: <TRequest = any, TResponse = any> (req: TRequest, res: TResponse, document: OpenAPIObject) => OpenAPIObject;
106+
23107
}

0 commit comments

Comments
 (0)