Skip to content

Commit

Permalink
chore(): merge conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilmysliwiec committed Jul 8, 2022
2 parents 984469f + 4cc6728 commit 9f59393
Show file tree
Hide file tree
Showing 18 changed files with 2,073 additions and 1,976 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';`);
});
});
});
139 changes: 139 additions & 0 deletions e2e/manual-e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
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, Logger } from '@nestjs/common';
import {
ExpressAdapter,
NestExpressApplication
} from '@nestjs/platform-express';
import { join } from 'path';

const port = 4001;
const host = 'localhost';
const docRelPath = '/api-docs';

const USE_FASTIFY = true;
const ENABLE_BASIC_AUTH = true;

const adapter = USE_FASTIFY ? new FastifyAdapter() : new ExpressAdapter();
const publicFolderPath = join(__dirname, '../../e2e', 'public');

async function bootstrap() {
const app = await NestFactory.create<INestApplication>(
ApplicationModule,
adapter
);
const httpAdapter = app.getHttpAdapter();

ENABLE_BASIC_AUTH &&
httpAdapter.use('/api-docs', (req, res, next) => {
function parse(input: string): { name: string; pass: string } {
const [, encodedPart] = input.split(' ');

const buff = Buffer.from(encodedPart, 'base64');
const text = buff.toString('ascii');
const [name, pass] = text.split(':');

return { name, pass };
}

function unauthorizedResponse(): void {
if (USE_FASTIFY) {
res.statusCode = 401;
res.setHeader('WWW-Authenticate', 'Basic');
} else {
res.status(401);
res.set('WWW-Authenticate', 'Basic');
}

next();
}

if (!req.headers.authorization) {
return unauthorizedResponse();
}

const credentials = parse(req.headers.authorization);

if (
!credentials ||
credentials?.name !== 'admin' ||
credentials?.pass !== 'admin'
) {
return unauthorizedResponse();
}

next();
});

app.setGlobalPrefix('/api/v1');

const swaggerSettings = 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: [] })
.build();

const document = SwaggerModule.createDocument(app, swaggerSettings, {
deepScanRoutes: true,
ignoreGlobalPrefix: false,
extraModels: [] // add DTOs that are not explicitly registered here (like PaginatedDto, etc)
});

SwaggerModule.setup(docRelPath, app, document, {
customSiteTitle: 'Demo API - Swagger UI 1',
swaggerOptions: {
persistAuthorization: true,
defaultModelsExpandDepth: -1,
syntaxHighlight: {
activate: true,
theme: 'tomorrow-night'
},
tryItOutEnabled: true
},
customfavIcon: '/public/favicon.ico',
customCssUrl: '/public/theme.css' // to showcase that in new implementation u can use custom css with fastify
});

SwaggerModule.setup('/swagger-docs', app, document, {
customSiteTitle: 'Demo API - Swagger UI 2',
swaggerOptions: {
persistAuthorization: true,
defaultModelsExpandDepth: -1
}
});

USE_FASTIFY
? (app as NestFastifyApplication).useStaticAssets({
root: publicFolderPath,
prefix: `/public`,
decorateReply: false
})
: (app as NestExpressApplication).useStaticAssets(publicFolderPath, {
prefix: '/public'
});

await app.listen(port, host);
const baseUrl = `http://${host}:${port}`;
const startMessage = `Server started at ${baseUrl}; SwaggerUI at ${
baseUrl + docRelPath
};`;

Logger.log(startMessage);
}

bootstrap();
Binary file added e2e/public/favicon.ico
Binary file not shown.
Binary file added e2e/public/logo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions e2e/public/theme.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.swagger-ui .btn.authorize {
line-height: 1;
display: inline;
color: #336E7B;
border-color: #336E7B;
}
.swagger-ui .btn.authorize svg {
fill: #ef0505;
}


.swagger-ui body {
margin: 0;
background: #fafafa
}

img[alt="Swagger UI"] {
display: block;
-moz-box-sizing: border-box;
box-sizing: border-box;
content: url('/public/logo.png');
max-width: 100%;
max-height: 100%;
}
5 changes: 4 additions & 1 deletion e2e/validate-schema.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { ApplicationModule } from './src/app.module';
import { Cat } from './src/cats/classes/cat.class';
import { TagDto } from './src/cats/dto/tag.dto';
import { OpenAPIV3 } from 'openapi-types';

describe('Validate OpenAPI schema', () => {
let app: INestApplication;
Expand Down Expand Up @@ -108,7 +109,9 @@ describe('Validate OpenAPI schema', () => {
}
});

let api = await SwaggerParser.validate(document as any);
let api = (await SwaggerParser.validate(
document as any
)) as OpenAPIV3.Document;
console.log('API name: %s, Version: %s', api.info.title, api.info.version);
expect(api.components.schemas).toHaveProperty('Person');
expect(api.components.schemas).toHaveProperty('Cat');
Expand Down

0 comments on commit 9f59393

Please sign in to comment.