Skip to content

Commit

Permalink
Merge pull request #2870 from arkraft/fix/array-enums-queries-or-params
Browse files Browse the repository at this point in the history
fix(swagger): Query Enums with isArray and enumName have type 'array' in schema
  • Loading branch information
kamilmysliwiec authored Jul 1, 2024
2 parents 6c5a7f6 + d74136b commit d3bad9d
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 48 deletions.
54 changes: 35 additions & 19 deletions lib/services/schema-object-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
});
Expand Down Expand Up @@ -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) =>
Expand Down Expand Up @@ -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
};
}
Expand Down Expand Up @@ -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,
Expand Down
56 changes: 28 additions & 28 deletions test/explorer/swagger-explorer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -965,7 +965,7 @@ describe('SwaggerExplorer', () => {
})
@ApiQuery({ name: 'order', enum: QueryEnum })
@ApiQuery({ name: 'page', enum: ['d', 'e', 'f'], isArray: true })
find(): Promise<Foo[]> {
find(@Param('objectId') objectId: ParamEnum): Promise<Foo[]> {
return Promise.resolve([]);
}
}
Expand All @@ -979,7 +979,7 @@ describe('SwaggerExplorer', () => {
})
@ApiQuery({ name: 'order', enum: QueryEnum })
@ApiQuery({ name: 'page', enum: ['d', 'e', 'f'] })
find(): Promise<Foo[]> {
find(@Param('objectId') objectId: ParamEnum, @Query('order') order: QueryEnum, @Query('page') page: 'd' | 'e' | 'f'): Promise<Foo[]> {
return Promise.resolve([]);
}
}
Expand All @@ -999,7 +999,7 @@ describe('SwaggerExplorer', () => {
enumName: 'QueryEnum',
isArray: true
})
findBar(): Promise<Foo> {
findBar(@Param('objectId') objectId: ParamEnum, @Query('order') order: QueryEnum, @Query('page') page: QueryEnum[]): Promise<Foo> {
return Promise.resolve(null);
}
}
Expand All @@ -1012,7 +1012,7 @@ describe('SwaggerExplorer', () => {
enum: [1, 2, 3],
enumName: 'NumberEnum'
})
findBar(): Promise<Foo> {
findBar(@Param('objectId') objectId: number): Promise<Foo> {
return Promise.resolve(null);
}
}
Expand All @@ -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',
Expand All @@ -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']
}
}
]);
});
Expand All @@ -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']
}
},
{
Expand All @@ -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']
}
}
]);
Expand All @@ -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'
}
},
{
Expand All @@ -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'
}
}
}
]);
Expand Down
46 changes: 45 additions & 1 deletion test/services/schema-object-factory.spec.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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'
});
});
});
});

0 comments on commit d3bad9d

Please sign in to comment.