From 16d1ea9ab04b49ef78a7eb28881e60caaea096b0 Mon Sep 17 00:00:00 2001
From: Artur Kraft <artur.kraft@nesto-software.de>
Date: Sat, 9 Mar 2024 16:41:37 +0100
Subject: [PATCH 1/4] add params and queries to routes

---
 test/explorer/swagger-explorer.spec.ts | 56 +++++++++++++-------------
 1 file changed, 28 insertions(+), 28 deletions(-)

diff --git a/test/explorer/swagger-explorer.spec.ts b/test/explorer/swagger-explorer.spec.ts
index 2870f7923..123e1b90d 100644
--- a/test/explorer/swagger-explorer.spec.ts
+++ b/test/explorer/swagger-explorer.spec.ts
@@ -938,7 +938,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([]);
       }
     }
@@ -952,7 +952,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([]);
       }
     }
@@ -972,7 +972,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);
       }
     }
@@ -985,7 +985,7 @@ describe('SwaggerExplorer', () => {
         enum: [1, 2, 3],
         enumName: 'NumberEnum'
       })
-      findBar(): Promise<Foo> {
+      findBar(@Param('objectId') objectId: number): Promise<Foo> {
         return Promise.resolve(null);
       }
     }
@@ -1010,6 +1010,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',
@@ -1030,15 +1039,6 @@ describe('SwaggerExplorer', () => {
             type: 'number',
             enum: [1, 2, 3]
           }
-        },
-        {
-          in: 'path',
-          name: 'objectId',
-          required: true,
-          schema: {
-            type: 'string',
-            enum: ['a', 'b', 'c']
-          }
         }
       ]);
     });
@@ -1056,12 +1056,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']
           }
         },
         {
@@ -1074,12 +1074,12 @@ describe('SwaggerExplorer', () => {
           }
         },
         {
-          in: 'path',
-          name: 'objectId',
+          in: 'query',
+          name: 'page',
           required: true,
           schema: {
             type: 'string',
-            enum: ['a', 'b', 'c']
+            enum: ['d', 'e', 'f']
           }
         }
       ]);
@@ -1099,14 +1099,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'
           }
         },
         {
@@ -1118,11 +1115,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'
+            }
           }
         }
       ]);

From 7230915e627a7a96110ec3dd8ee6d84bc70bb54f Mon Sep 17 00:00:00 2001
From: Artur Kraft <artur.kraft@nesto-software.de>
Date: Sat, 9 Mar 2024 16:41:50 +0100
Subject: [PATCH 2/4] if enum is of type array, use the item type

---
 lib/services/schema-object-factory.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/services/schema-object-factory.ts b/lib/services/schema-object-factory.ts
index 8c72bd989..2abd16eca 100644
--- a/lib/services/schema-object-factory.ts
+++ b/lib/services/schema-object-factory.ts
@@ -273,7 +273,7 @@ export class SchemaObjectFactory {
         : undefined;
 
       schemas[enumName] = {
-        type: param.schema?.['type'] ?? 'string',
+        type: (param.isArray ? param.schema?.['items']?.['type'] : param.schema?.['type']) ?? 'string',
         enum: _enum
       };
     }

From 11f6ac584040257dc2e208ed5911ea57e3a47eed Mon Sep 17 00:00:00 2001
From: Artur Kraft <artur.kraft@nesto-software.de>
Date: Sat, 9 Mar 2024 16:42:09 +0100
Subject: [PATCH 3/4] add tests for enum schema types

---
 test/services/schema-object-factory.spec.ts | 43 ++++++++++++++++++++-
 1 file changed, 42 insertions(+), 1 deletion(-)

diff --git a/test/services/schema-object-factory.spec.ts b/test/services/schema-object-factory.spec.ts
index 2a7546734..d1923400f 100644
--- a/test/services/schema-object-factory.spec.ts
+++ b/test/services/schema-object-factory.spec.ts
@@ -1,9 +1,10 @@
 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;
@@ -328,4 +329,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'
+      })
+    })
+  })
 });

From 5ab17a90bb5ae70fe03344ec93d0b1bd6eec2e42 Mon Sep 17 00:00:00 2001
From: Artur Kraft <artur.kraft@nesto-software.de>
Date: Sat, 9 Mar 2024 16:43:23 +0100
Subject: [PATCH 4/4] prettier

---
 lib/services/schema-object-factory.ts       | 54 +++++++++++++--------
 test/services/schema-object-factory.spec.ts | 23 +++++----
 2 files changed, 48 insertions(+), 29 deletions(-)

diff --git a/lib/services/schema-object-factory.ts b/lib/services/schema-object-factory.ts
index 2abd16eca..a5bb2be78 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.isArray ? param.schema?.['items']?.['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/services/schema-object-factory.spec.ts b/test/services/schema-object-factory.spec.ts
index d1923400f..3f4514bcb 100644
--- a/test/services/schema-object-factory.spec.ts
+++ b/test/services/schema-object-factory.spec.ts
@@ -1,5 +1,8 @@
 import { ApiExtension, ApiProperty } from '../../lib/decorators';
-import { BaseParameterObject, 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';
@@ -337,15 +340,15 @@ describe('SchemaObjectFactory', () => {
         isArray: false,
         enumName: 'MyEnum',
         enum: ['a', 'b', 'c']
-      }
+      };
       const schemas = {};
-      schemaObjectFactory.createEnumParam(params, 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 = {
@@ -359,14 +362,14 @@ describe('SchemaObjectFactory', () => {
             enum: ['a', 'b', 'c']
           }
         }
-      }
+      };
       const schemas = {};
-      schemaObjectFactory.createEnumParam(params, schemas)
+      schemaObjectFactory.createEnumParam(params, schemas);
 
       expect(schemas['MyEnum']).toEqual({
         enum: ['a', 'b', 'c'],
         type: 'string'
-      })
-    })
-  })
+      });
+    });
+  });
 });