Skip to content

Commit

Permalink
feat(@nestjs/swagger): Support extensions in Document and Schema objects
Browse files Browse the repository at this point in the history
  • Loading branch information
drewish committed Oct 31, 2023
1 parent e618e03 commit 693889f
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 4 deletions.
4 changes: 4 additions & 0 deletions e2e/api-spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,10 @@
]
}
},
"x-tags": [
"foo",
"bar"
],
"required": [
"name",
"age",
Expand Down
3 changes: 2 additions & 1 deletion e2e/src/cats/dto/create-cat.dto.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { ApiExtraModels, ApiProperty } from '../../../../lib';
import { ApiExtension, ApiExtraModels, ApiProperty } from '../../../../lib';
import { ExtraModel } from './extra-model.dto';
import { LettersEnum } from './pagination-query.dto';
import { TagDto } from './tag.dto';

@ApiExtraModels(ExtraModel)
@ApiExtension('x-tags', ['foo', 'bar'])
export class CreateCatDto {
@ApiProperty()
readonly name: string;
Expand Down
14 changes: 13 additions & 1 deletion lib/document-builder.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Logger } from '@nestjs/common';
import { isString, isUndefined, negate, pickBy } from 'lodash';
import { clone, isString, isUndefined, negate, pickBy } from 'lodash';
import { buildDocumentBase } from './fixtures/document.base';
import { OpenAPIObject } from './interfaces';
import {
Expand Down Expand Up @@ -85,6 +85,18 @@ export class DocumentBuilder {
return this;
}

public addExtension(extensionKey: string, extensionProperties: any): this {
if (!extensionKey.startsWith('x-')) {
throw new Error(
'Extension key is not prefixed. Please ensure you prefix it with `x-`.'
);
}

this.document[extensionKey] = clone(extensionProperties);

return this;
}

public addSecurity(name: string, options: SecuritySchemeObject): this {
this.document.components.securitySchemes = {
...(this.document.components.securitySchemes || {}),
Expand Down
4 changes: 3 additions & 1 deletion lib/services/schema-object-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,13 @@ export class SchemaObjectFactory {
if (!propertiesWithType) {
return '';
}
const extensionProperties = Reflect.getMetadata(DECORATORS.API_EXTENSION, type) || [];
const typeDefinition: SchemaObject = {
type: 'object',
properties: mapValues(keyBy(propertiesWithType, 'name'), (property) =>
omit(property, ['name', 'isArray', 'required', 'enumName'])
) as Record<string, SchemaObject | ReferenceObject>
) as Record<string, SchemaObject | ReferenceObject>,
...extensionProperties
};
const typeDefinitionRequiredFields = propertiesWithType
.filter((property) => property.required != false)
Expand Down
16 changes: 15 additions & 1 deletion test/services/schema-object-factory.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ApiProperty } from '../../lib/decorators';
import { ApiExtension, ApiProperty } from '../../lib/decorators';
import { SchemasObject } from '../../lib/interfaces/open-api-spec.interface';
import { ModelPropertiesAccessor } from '../../lib/services/model-properties-accessor';
import { SchemaObjectFactory } from '../../lib/services/schema-object-factory';
Expand Down Expand Up @@ -297,6 +297,20 @@ describe('SchemaObjectFactory', () => {
properties: { name: { type: 'string', minLength: 1 } }
});
});

it('should include extension properties', () => {
@ApiExtension('x-test', 'value')
class CreatUserDto {
@ApiProperty({ minLength: 0, required: true })
name: string;
}

const schemas: Record<string, SchemasObject> = {};

schemaObjectFactory.exploreModelSchema(CreatUserDto, schemas);

expect(schemas[CreatUserDto.name]['x-test']).toEqual('value');
});
});

describe('createEnumSchemaType', () => {
Expand Down

0 comments on commit 693889f

Please sign in to comment.