diff --git a/lib/services/schema-object-factory.ts b/lib/services/schema-object-factory.ts index 4a10643a7..27dd5feb8 100644 --- a/lib/services/schema-object-factory.ts +++ b/lib/services/schema-object-factory.ts @@ -167,14 +167,24 @@ export class SchemaObjectFactory { const schemaCombinators = ['oneOf', 'anyOf', 'allOf']; let keyOfCombinators = ''; - if (schemaCombinators.some((_key) => { keyOfCombinators = _key; return _key in property; })) { - if (((property as SchemaObjectMetadata)?.type === 'array' || (property as SchemaObjectMetadata).isArray) && keyOfCombinators) { - (property as SchemaObjectMetadata).items = {}; - (property as SchemaObjectMetadata).items[keyOfCombinators] = property[keyOfCombinators]; - delete property[keyOfCombinators]; - } else { - delete (property as SchemaObjectMetadata).type; - } + if ( + schemaCombinators.some((_key) => { + keyOfCombinators = _key; + return _key in property; + }) + ) { + if ( + ((property as SchemaObjectMetadata)?.type === 'array' || + (property as SchemaObjectMetadata).isArray) && + keyOfCombinators + ) { + (property as SchemaObjectMetadata).items = {}; + (property as SchemaObjectMetadata).items[keyOfCombinators] = + property[keyOfCombinators]; + delete property[keyOfCombinators]; + } else { + delete (property as SchemaObjectMetadata).type; + } } return property as ParameterObject; }); @@ -203,7 +213,8 @@ export class SchemaObjectFactory { if (!propertiesWithType) { return ''; } - const extensionProperties = Reflect.getMetadata(DECORATORS.API_EXTENSION, type) || {}; + const extensionProperties = + Reflect.getMetadata(DECORATORS.API_EXTENSION, type) || {}; const typeDefinition: SchemaObject = { type: 'object', properties: mapValues(keyBy(propertiesWithType, 'name'), (property) => @@ -273,7 +284,10 @@ export class SchemaObjectFactory { : undefined; schemas[enumName] = { - type: param.schema?.['type'] ?? 'string', + type: + (param.isArray + ? param.schema?.['items']?.['type'] + : param.schema?.['type']) ?? 'string', enum: _enum }; } @@ -301,15 +315,17 @@ export class SchemaObjectFactory { const $ref = getSchemaPath(enumName); // Allow given fields to be part of the referenced enum schema - const additionalParams = ['description', 'deprecated', 'default'] - const additionalFields = additionalParams.reduce((acc, param) => - ({...acc, ...(metadata[param] && { [param]: metadata[param] })}), {}); - - const enumType: string = ( - metadata.isArray - ? metadata.items['type'] - : metadata.type - ) ?? 'string'; + const additionalParams = ['description', 'deprecated', 'default']; + const additionalFields = additionalParams.reduce( + (acc, param) => ({ + ...acc, + ...(metadata[param] && { [param]: metadata[param] }) + }), + {} + ); + + const enumType: string = + (metadata.isArray ? metadata.items['type'] : metadata.type) ?? 'string'; schemas[enumName] = { type: enumType, diff --git a/test/explorer/swagger-explorer.spec.ts b/test/explorer/swagger-explorer.spec.ts index f6e41951c..df0995694 100644 --- a/test/explorer/swagger-explorer.spec.ts +++ b/test/explorer/swagger-explorer.spec.ts @@ -965,7 +965,7 @@ describe('SwaggerExplorer', () => { }) @ApiQuery({ name: 'order', enum: QueryEnum }) @ApiQuery({ name: 'page', enum: ['d', 'e', 'f'], isArray: true }) - find(): Promise { + find(@Param('objectId') objectId: ParamEnum): Promise { return Promise.resolve([]); } } @@ -979,7 +979,7 @@ describe('SwaggerExplorer', () => { }) @ApiQuery({ name: 'order', enum: QueryEnum }) @ApiQuery({ name: 'page', enum: ['d', 'e', 'f'] }) - find(): Promise { + find(@Param('objectId') objectId: ParamEnum, @Query('order') order: QueryEnum, @Query('page') page: 'd' | 'e' | 'f'): Promise { return Promise.resolve([]); } } @@ -999,7 +999,7 @@ describe('SwaggerExplorer', () => { enumName: 'QueryEnum', isArray: true }) - findBar(): Promise { + findBar(@Param('objectId') objectId: ParamEnum, @Query('order') order: QueryEnum, @Query('page') page: QueryEnum[]): Promise { return Promise.resolve(null); } } @@ -1012,7 +1012,7 @@ describe('SwaggerExplorer', () => { enum: [1, 2, 3], enumName: 'NumberEnum' }) - findBar(): Promise { + findBar(@Param('objectId') objectId: number): Promise { return Promise.resolve(null); } } @@ -1037,6 +1037,15 @@ describe('SwaggerExplorer', () => { '/globalPrefix/v3/modulePath/foos/{objectId}' ); expect(routes[0].root!.parameters).toEqual([ + { + in: 'path', + name: 'objectId', + required: true, + schema: { + type: 'string', + enum: ['a', 'b', 'c'] + } + }, { in: 'query', name: 'page', @@ -1057,15 +1066,6 @@ describe('SwaggerExplorer', () => { type: 'number', enum: [1, 2, 3] } - }, - { - in: 'path', - name: 'objectId', - required: true, - schema: { - type: 'string', - enum: ['a', 'b', 'c'] - } } ]); }); @@ -1083,12 +1083,12 @@ describe('SwaggerExplorer', () => { expect(routes[0].root!.parameters).toEqual([ { - in: 'query', - name: 'page', + in: 'path', + name: 'objectId', required: true, schema: { type: 'string', - enum: ['d', 'e', 'f'] + enum: ['a', 'b', 'c'] } }, { @@ -1101,12 +1101,12 @@ describe('SwaggerExplorer', () => { } }, { - in: 'path', - name: 'objectId', + in: 'query', + name: 'page', required: true, schema: { type: 'string', - enum: ['a', 'b', 'c'] + enum: ['d', 'e', 'f'] } } ]); @@ -1126,14 +1126,11 @@ describe('SwaggerExplorer', () => { expect(routes[0].root!.parameters).toEqual([ { - in: 'query', - name: 'page', + in: 'path', + name: 'objectId', required: true, schema: { - type: 'array', - items: { - $ref: '#/components/schemas/QueryEnum' - } + $ref: '#/components/schemas/ParamEnum' } }, { @@ -1145,11 +1142,14 @@ describe('SwaggerExplorer', () => { } }, { - in: 'path', - name: 'objectId', + in: 'query', + name: 'page', required: true, schema: { - $ref: '#/components/schemas/ParamEnum' + type: 'array', + items: { + $ref: '#/components/schemas/QueryEnum' + } } } ]); diff --git a/test/services/schema-object-factory.spec.ts b/test/services/schema-object-factory.spec.ts index 531ab1290..d3c316ad7 100644 --- a/test/services/schema-object-factory.spec.ts +++ b/test/services/schema-object-factory.spec.ts @@ -1,9 +1,13 @@ import { ApiExtension, ApiProperty } from '../../lib/decorators'; -import { SchemasObject } from '../../lib/interfaces/open-api-spec.interface'; +import { + BaseParameterObject, + SchemasObject +} from '../../lib/interfaces/open-api-spec.interface'; import { ModelPropertiesAccessor } from '../../lib/services/model-properties-accessor'; import { SchemaObjectFactory } from '../../lib/services/schema-object-factory'; import { SwaggerTypesMapper } from '../../lib/services/swagger-types-mapper'; import { CreateUserDto } from './fixtures/create-user.dto'; +import { ParamWithTypeMetadata } from '../../lib/services/parameter-metadata-accessor'; describe('SchemaObjectFactory', () => { let modelPropertiesAccessor: ModelPropertiesAccessor; @@ -353,4 +357,44 @@ describe('SchemaObjectFactory', () => { expect(schemas).toEqual({ MyEnum: { enum: [1, 2, 3], type: 'number' } }); }); }); + + describe('createEnumParam', () => { + it('should create an enum schema definition', () => { + const params: ParamWithTypeMetadata & BaseParameterObject = { + required: true, + isArray: false, + enumName: 'MyEnum', + enum: ['a', 'b', 'c'] + }; + const schemas = {}; + schemaObjectFactory.createEnumParam(params, schemas); + + expect(schemas['MyEnum']).toEqual({ + enum: ['a', 'b', 'c'], + type: 'string' + }); + }); + + it('should create an enum schema definition for an array', () => { + const params: ParamWithTypeMetadata & BaseParameterObject = { + required: true, + isArray: true, + enumName: 'MyEnum', + schema: { + type: 'array', + items: { + type: 'string', + enum: ['a', 'b', 'c'] + } + } + }; + const schemas = {}; + schemaObjectFactory.createEnumParam(params, schemas); + + expect(schemas['MyEnum']).toEqual({ + enum: ['a', 'b', 'c'], + type: 'string' + }); + }); + }); });