Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: If a name is defined, should be used the defined name #564

Merged
merged 10 commits into from
Jun 4, 2024
152 changes: 152 additions & 0 deletions spec/exclude-swagger/exclude-swagger-resonse-name.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { Controller, Module, type INestApplication } from '@nestjs/common';
import { OmitType, PickType } from '@nestjs/swagger';
import { Test } from '@nestjs/testing';
import { TypeOrmModule } from '@nestjs/typeorm';

import { Crud } from '../../src/lib/crud.decorator';
import { CrudController } from '../../src/lib/interface';
import { BaseEntity } from '../base/base.entity';
import { BaseService } from '../base/base.service';
import { TestHelper } from '../test.helper';

import type { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
import type { DenormalizedDoc } from '@nestjs/swagger/dist/interfaces/denormalized-doc.interface';
import type { TestingModule } from '@nestjs/testing';

class OmitTypeDto extends OmitType(BaseEntity, ['description']) {}
class PickTypeDto extends PickType(BaseEntity, ['name']) {}

@Crud({
entity: BaseEntity,
routes: {
recover: { swagger: { hide: true } },
readOne: { swagger: { response: OmitTypeDto } },
create: { swagger: { body: PickTypeDto } },
update: { swagger: { response: OmitTypeDto } },
},
})
@Controller('exclude-swagger')
export class ExcludeSwaggerController implements CrudController<BaseEntity> {
constructor(public readonly crudService: BaseService) {}
}

@Module({
imports: [TypeOrmModule.forFeature([BaseEntity])],
controllers: [ExcludeSwaggerController],
providers: [BaseService],
})
export class ExcludeSwaggerModule {}

describe('exclude swagger > defined name', () => {
let app: INestApplication;
let controller: ExcludeSwaggerController;
let routeSet: Record<string, DenormalizedDoc>;

beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [ExcludeSwaggerModule, TestHelper.getTypeOrmMysqlModule([BaseEntity])],
}).compile();

app = moduleFixture.createNestApplication();
controller = moduleFixture.get<ExcludeSwaggerController>(ExcludeSwaggerController);

await app.init();

routeSet = TestHelper.getSwaggerExplorer({
instance: controller,
metatype: ExcludeSwaggerController,
} as InstanceWrapper<ExcludeSwaggerController>);
});

afterAll(async () => {
await TestHelper.dropTypeOrmEntityTables();
await app?.close();
});

it('should not generate recover route in swagger', async () => {
const recover = 'post /exclude-swagger/{id}/recover';
expect(routeSet[recover]).toBeUndefined();
});

it('Should be changed swagger readOne response interface', () => {
const readOne = 'get /exclude-swagger/{id}';
expect(routeSet[readOne].responses).toEqual({
'200': {
content: {
'application/json': {
schema: { $ref: '#/components/schemas/OmitTypeDto' },
},
},
description: 'Fetch one entity from Base table',
},
'400': {
description: 'Entity that does not exist',
},
'422': {
description: 'Invalid field',
},
});
expect(routeSet[readOne].root?.method).toEqual('get');
expect(routeSet[readOne].root?.summary).toEqual("Read one from 'Base' Table");
expect(routeSet[readOne].root?.description).toEqual("Fetch one entity in 'Base' Table");
});

it('Should be changed swagger update response interface', () => {
const update = 'patch /exclude-swagger/{id}';
expect(routeSet[update].responses).toEqual({
'200': {
content: {
'application/json': {
schema: { $ref: '#/components/schemas/OmitTypeDto' },
},
},
description: 'Updated ok',
},
'404': {
description: 'Not found entity',
},
'422': {
description: 'Invalid field',
},
});
expect(routeSet[update].root?.method).toEqual('patch');
expect(routeSet[update].root?.summary).toEqual("update one in 'Base' Table");
expect(routeSet[update].root?.description).toEqual("Update on entity in 'Base' Table");
});

it('Should be changed swagger Create request body interface', () => {
const create = 'post /exclude-swagger';
expect(routeSet[create].root).toEqual({
method: 'post',
path: '/exclude-swagger',
operationId: 'reservedCreate',
summary: "create one to 'Base' Table",
description: "Create an entity in 'Base' Table",
parameters: [],
requestBody: {
description: 'CreateBaseDto',
required: true,
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/PickTypeDto',
anyOf: [
{
$ref: '#/components/schemas/PickTypeDto',
},
{
items: {
$ref: '#/components/schemas/PickTypeDto',
},
type: 'array',
},
],
type: 'object',
},
},
},
},
});
});
});
8 changes: 5 additions & 3 deletions src/lib/crud.route.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,9 +352,11 @@ export class CrudRouteFactory {
}
if (CRUD_POLICY[method].useBody) {
const bodyType = (() => {
const routeConfig = this.crudOptions.routes?.[method];
if (routeConfig?.swagger?.body) {
return this.generalTypeGuard(routeConfig.swagger.body, method, 'body');
const customBody = this.crudOptions.routes?.[method]?.swagger?.body;
if (customBody) {
return ['PickTypeClass', 'OmitTypeClass'].includes(customBody.name)
? this.generalTypeGuard(customBody, method, 'body')
: customBody;
}
if (method === Method.SEARCH) {
return RequestSearchDto;
Expand Down
Loading