Skip to content

Commit

Permalink
feat(): routing platform independence (polishing)
Browse files Browse the repository at this point in the history
  • Loading branch information
flamewow committed Apr 11, 2022
1 parent ed777f3 commit 2913391
Show file tree
Hide file tree
Showing 8 changed files with 337 additions and 443 deletions.
70 changes: 70 additions & 0 deletions e2e/express.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { NestFactory } from '@nestjs/core';
import * as SwaggerParser from 'swagger-parser';
import { DocumentBuilder, SwaggerModule } from '../lib';
import { ApplicationModule } from './src/app.module';
import {
ExpressAdapter,
NestExpressApplication
} from '@nestjs/platform-express';

describe('Express Swagger', () => {
let app: NestExpressApplication;
let builder: DocumentBuilder;

beforeEach(async () => {
app = await NestFactory.create<NestExpressApplication>(
ApplicationModule,
new ExpressAdapter(),
{ logger: false }
);

builder = new DocumentBuilder()
.setTitle('Cats example')
.setDescription('The cats API description')
.setVersion('1.0')
.addTag('cats')
.addBasicAuth()
.addBearerAuth()
.addOAuth2()
.addApiKey()
.addApiKey({ type: 'apiKey' }, 'key1')
.addApiKey({ type: 'apiKey' }, 'key2')
.addCookieAuth()
.addSecurityRequirements('bearer')
.addSecurityRequirements({ basic: [], cookie: [] });
});

it('should produce a valid OpenAPI 3.0 schema', async () => {
const document = SwaggerModule.createDocument(app, builder.build());
const doc = JSON.stringify(document, null, 2);

try {
let api = await SwaggerParser.validate(document as any);
console.log(
'API name: %s, Version: %s',
api.info.title,
api.info.version
);
expect(api.info.title).toEqual('Cats example');
} catch (err) {
console.log(doc);
expect(err).toBeUndefined();
}
});

it('should fix colons in url', async () => {
const document = SwaggerModule.createDocument(app, builder.build());
expect(document.paths['/express:colon:another/{prop}']).toBeDefined();
});

it('should setup multiple routes', async () => {
const document1 = SwaggerModule.createDocument(app, builder.build());
SwaggerModule.setup('/swagger1', app, document1);

const document2 = SwaggerModule.createDocument(app, builder.build());
SwaggerModule.setup('/swagger2', app, document2);

await app.init();
expect(app.getHttpAdapter().getInstance()).toBeDefined();
});
});
123 changes: 0 additions & 123 deletions e2e/fastify.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,28 +57,6 @@ describe('Fastify Swagger', () => {
expect(document.paths['/fastify:colon:another/{prop}']).toBeDefined();
});

it('should pass uiConfig options to fastify-swagger', async () => {
const document1 = SwaggerModule.createDocument(app, builder.build());
const uiConfig = {
displayOperationId: true,
persistAuthorization: true
};
const options = { uiConfig };
SwaggerModule.setup('/swagger1', app, document1, options);

const instance = await app.getHttpAdapter().getInstance().ready();

instance.ready(async () => {
const response = await instance.inject({
method: 'GET',
url: '/swagger1/uiConfig'
});

expect(response.statusCode).toEqual(200);
expect(JSON.parse(response.body)).toEqual(uiConfig);
});
});

it('should setup multiple routes', async () => {
const document1 = SwaggerModule.createDocument(app, builder.build());
SwaggerModule.setup('/swagger1', app, document1);
Expand All @@ -92,105 +70,4 @@ describe('Fastify Swagger', () => {
app.getHttpAdapter().getInstance().ready()
).resolves.toBeDefined();
});

it('should pass initOAuth options to fastify-swagger', async () => {
const document1 = SwaggerModule.createDocument(app, builder.build());
const initOAuth = {
scopes: ['openid', 'profile', 'email', 'offline_access']
};
const options = { initOAuth };
SwaggerModule.setup('/swagger1', app, document1, options);

const instance = await app.getHttpAdapter().getInstance().ready();

instance.ready(async () => {
const response = await instance.inject({
method: 'GET',
url: '/swagger1/initOAuth'
});

expect(response.statusCode).toEqual(200);
expect(JSON.parse(response.body)).toEqual(initOAuth);
});
});

it('should pass staticCSP = undefined options to fastify-swagger', async () => {
const document1 = SwaggerModule.createDocument(app, builder.build());
SwaggerModule.setup('swagger1', app, document1);

const instance = await app.getHttpAdapter().getInstance().ready();

instance.ready(async () => {
const response = await instance.inject({
method: 'GET',
url: '/swagger1/json'
});

expect(response.statusCode).toEqual(200);
expect(response.headers['content-security-policy']).toBeUndefined();
});
});

it('should pass staticCSP = true options to fastify-swagger', async () => {
const document1 = SwaggerModule.createDocument(app, builder.build());
const options = { staticCSP: true };
SwaggerModule.setup('/swagger1', app, document1, options);

const instance = await app.getHttpAdapter().getInstance().ready();

instance.ready(async () => {
const response = await instance.inject({
method: 'GET',
url: '/swagger1/json'
});

expect(response.statusCode).toEqual(200);
expect(response.headers['content-security-policy']).toContain(`script-src 'self' 'sha256`);
expect(response.headers['content-security-policy']).toContain(`style-src 'self' https: 'sha256`);
});
});

it('should pass staticCSP = false options to fastify-swagger', async () => {
const document1 = SwaggerModule.createDocument(app, builder.build());
const options = { staticCSP: false };
SwaggerModule.setup('/swagger1', app, document1, options);

const instance = await app.getHttpAdapter().getInstance().ready();

instance.ready(async () => {
const response = await instance.inject({
method: 'GET',
url: '/swagger1/json'
});

expect(response.statusCode).toEqual(200);
expect(response.headers['content-security-policy']).toBeUndefined();
});
});

it('should pass transformStaticCSP = function options to fastify-swagger', async () => {
const document1 = SwaggerModule.createDocument(app, builder.build());
const checkParam = jest.fn((param: string) => param);
const options = {
staticCSP: `default-src 'self';`,
transformStaticCSP: (header: string) => {
checkParam(header);
return `default-src 'self'; script-src 'self';`
}
};
SwaggerModule.setup('/swagger1', app, document1, options);

const instance = await app.getHttpAdapter().getInstance().ready();

instance.ready(async () => {
const response = await instance.inject({
method: 'GET',
url: '/swagger1/json'
});

expect(checkParam).toBeCalledWith(`default-src 'self';`);
expect(response.statusCode).toEqual(200);
expect(response.headers['content-security-policy']).toEqual(`default-src 'self'; script-src 'self';`);
});
});
});
7 changes: 3 additions & 4 deletions e2e/manual-e2e.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import 'source-map-support/register';

/* eslint-disable @typescript-eslint/no-unused-vars */
import { NestFactory } from '@nestjs/core';
import { ApplicationModule } from './src/app.module';
import { DocumentBuilder, SwaggerModule } from '../lib';
import {
FastifyAdapter,
NestFastifyApplication
} from '@nestjs/platform-fastify';
import { INestApplication } from '@nestjs/common';
import { INestApplication, Logger } from '@nestjs/common';
import { ExpressAdapter } from '@nestjs/platform-express';

const port = 4001;
Expand Down Expand Up @@ -67,7 +66,7 @@ async function bootstrap() {
baseUrl + docRelPath
};`;

console.log(startMessage);
Logger.log(startMessage);
}

bootstrap();
48 changes: 1 addition & 47 deletions lib/interfaces/swagger-custom-options.interface.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
interface CommonSwaggerCustomOptions {
export interface SwaggerCustomOptions {
useGlobalPrefix?: boolean;
}

export interface ExpressSwaggerCustomOptions
extends CommonSwaggerCustomOptions {
explorer?: boolean;
swaggerOptions?: Record<string, any>;
customCss?: string;
Expand All @@ -16,45 +12,3 @@ export interface ExpressSwaggerCustomOptions
url?: string;
urls?: Record<'url' | 'name', string>[];
}

export interface FastifySwaggerCustomOptions
extends CommonSwaggerCustomOptions {
uiConfig?: Partial<{
deepLinking: boolean;
displayOperationId: boolean;
defaultModelsExpandDepth: number;
defaultModelExpandDepth: number;
defaultModelRendering: string;
displayRequestDuration: boolean;
docExpansion: string;
filter: boolean | string;
layout: string;
maxDisplayedTags: number;
showExtensions: boolean;
showCommonExtensions: boolean;
useUnsafeMarkdown: boolean;
syntaxHighlight:
| {
activate?: boolean;
theme?: string;
}
| false;
tryItOutEnabled: boolean;
validatorUrl: string | null;
persistAuthorization: boolean;
tagsSorter: string;
operationsSorter: string;
queryConfigEnabled: boolean;
}>;
initOAuth?: Record<string, any>;
staticCSP?: boolean | string | Record<string, string | string[]>;
transformStaticCSP?: (header: string) => string;
uiHooks?: {
onRequest?: Function;
preHandler?: Function;
};
}

export type SwaggerCustomOptions =
| FastifySwaggerCustomOptions
| ExpressSwaggerCustomOptions;

0 comments on commit 2913391

Please sign in to comment.