Skip to content

Commit

Permalink
Merge pull request #2392 from h4ad-forks/perf/lazy-initialization
Browse files Browse the repository at this point in the history
perf: added possibility to lazy initialize the document
  • Loading branch information
kamilmysliwiec authored Jun 12, 2023
2 parents f567413 + e876270 commit 9682d79
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 28 deletions.
2 changes: 2 additions & 0 deletions lib/interfaces/swagger-custom-options.interface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { SwaggerUiOptions } from './swagger-ui-options.interface';
import { SwaggerDocumentOptions } from './swagger-document-options.interface';
import { OpenAPIObject } from './open-api-spec.interface';

export interface SwaggerCustomOptions {
useGlobalPrefix?: boolean;
Expand Down
114 changes: 86 additions & 28 deletions lib/swagger-module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { INestApplication } from '@nestjs/common';
import { HttpServer } from '@nestjs/common/interfaces/http/http-server.interface';
import { isFunction } from '@nestjs/common/utils/shared.utils';
import { NestExpressApplication } from '@nestjs/platform-express';
import { NestFastifyApplication } from '@nestjs/platform-fastify';
import * as jsyaml from 'js-yaml';
Expand Down Expand Up @@ -65,21 +66,39 @@ export class SwaggerModule {
finalPath: string,
urlLastSubdirectory: string,
httpAdapter: HttpServer,
swaggerInitJS: string,
documentOrFactory: OpenAPIObject | (() => OpenAPIObject),
options: {
html: string;
yamlDocument: string;
jsonDocument: string;
jsonDocumentUrl: string;
yamlDocumentUrl: string;
}
swaggerOptions: SwaggerCustomOptions
},
) {
let document: OpenAPIObject;

const lazyBuildDocument = () => {
return typeof documentOrFactory === 'function' ? documentOrFactory() : documentOrFactory;
}

const baseUrlForSwaggerUI = normalizeRelPath(`./${urlLastSubdirectory}/`);

let html: string;
let swaggerInitJS: string;

httpAdapter.get(
normalizeRelPath(`${finalPath}/swagger-ui-init.js`),
(req, res) => {
res.type('application/javascript');

if (!document) {
document = lazyBuildDocument();
}

if (!swaggerInitJS) {
swaggerInitJS = buildSwaggerInitJS(document, options);
}

res.send(swaggerInitJS);
}
},
);

/**
Expand All @@ -89,12 +108,21 @@ export class SwaggerModule {
try {
httpAdapter.get(
normalizeRelPath(
`${finalPath}/${urlLastSubdirectory}/swagger-ui-init.js`
`${finalPath}/${urlLastSubdirectory}/swagger-ui-init.js`,
),
(req, res) => {
res.type('application/javascript');

if (!document) {
document = lazyBuildDocument();
}

if (!swaggerInitJS) {
swaggerInitJS = buildSwaggerInitJS(document, options);
}

res.send(swaggerInitJS);
}
},
);
} catch (err) {
/**
Expand All @@ -105,14 +133,32 @@ export class SwaggerModule {

httpAdapter.get(finalPath, (req, res) => {
res.type('text/html');
res.send(options.html);

if (!document) {
document = lazyBuildDocument();
}

if (!html) {
html = buildSwaggerHTML(baseUrlForSwaggerUI, document, options);
}

res.send(html);
});

// fastify doesn't resolve 'routePath/' -> 'routePath', that's why we handle it manually
try {
httpAdapter.get(normalizeRelPath(`${finalPath}/`), (req, res) => {
res.type('text/html');
res.send(options.html);

if (!document) {
document = lazyBuildDocument();
}

if (!html) {
html = buildSwaggerHTML(baseUrlForSwaggerUI, document, options);
}

res.send(html);
});
} catch (err) {
/**
Expand All @@ -123,33 +169,51 @@ export class SwaggerModule {
*/
}

let yamlDocument: string;
let jsonDocument: string;

httpAdapter.get(normalizeRelPath(options.jsonDocumentUrl), (req, res) => {
res.type('application/json');
res.send(options.jsonDocument);

if (!document) {
document = lazyBuildDocument();
}

if (!jsonDocument) {
jsonDocument = JSON.stringify(document);
}

res.send(jsonDocument);
});

httpAdapter.get(normalizeRelPath(options.yamlDocumentUrl), (req, res) => {
res.type('text/yaml');
res.send(options.yamlDocument);

if (!document) {
document = lazyBuildDocument();
}

if (!yamlDocument) {
yamlDocument = jsyaml.dump(document, { skipInvalid: true });
}

res.send(yamlDocument);
});
}

public static setup(
path: string,
app: INestApplication,
document: OpenAPIObject,
documentOrFactory: OpenAPIObject | (() => OpenAPIObject),
options?: SwaggerCustomOptions
) {
const globalPrefix = getGlobalPrefix(app);
const finalPath = validatePath(
options?.useGlobalPrefix && validateGlobalPrefix(globalPrefix)
? `${globalPrefix}${validatePath(path)}`
: path
: path,
);
const urlLastSubdirectory = finalPath.split('/').slice(-1).pop();

const yamlDocument = jsyaml.dump(document, { skipInvalid: true });
const jsonDocument = JSON.stringify(document);
const urlLastSubdirectory = finalPath.split('/').slice(-1).pop() || '';

const validatedGlobalPrefix =
options?.useGlobalPrefix && validateGlobalPrefix(globalPrefix)
Expand All @@ -164,24 +228,18 @@ export class SwaggerModule {
? `${validatedGlobalPrefix}${validatePath(options.yamlDocumentUrl)}`
: `${finalPath}-yaml`;

const baseUrlForSwaggerUI = normalizeRelPath(`./${urlLastSubdirectory}/`);

const html = buildSwaggerHTML(baseUrlForSwaggerUI, document, options);
const swaggerInitJS = buildSwaggerInitJS(document, options);
const httpAdapter = app.getHttpAdapter();

SwaggerModule.serveDocuments(
finalPath,
urlLastSubdirectory,
httpAdapter,
swaggerInitJS,
documentOrFactory,
{
html,
yamlDocument,
jsonDocument,
jsonDocumentUrl: finalJSONDocumentPath,
yamlDocumentUrl: finalYAMLDocumentPath
}
yamlDocumentUrl: finalYAMLDocumentPath,
swaggerOptions: options || {},
},
);

SwaggerModule.serveStatic(finalPath, app);
Expand Down

0 comments on commit 9682d79

Please sign in to comment.