Repository: nestjs/swagger
Pull request #1279 (2021-03-31 11:28:29)

fix(#1148): prevent decorator properties from leaking into swagger doc
## PR Checklist
Please check if your PR fulfills the following requirements:

- [X] The commit message follows our guidelines: https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md
- [X] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been added / updated (for bug fixes / features)


## PR Type
What kind of change does this PR introduce?

<!-- Please check the one that applies to this PR using "x". -->
```
[X] Bugfix
[ ] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Other... Please describe:
```

## What is the current behavior?
The `enumName` property from the `@ApiParam` decorator is leaking into the Swagger document when an enum is used as a URL parameter. We currently strip the `type` property from the parameter if a schema definition exists but this does not take other decorator properties into account.

Issue Number: #1148 

## What is the new behavior?
The `keysToRemove` array has been moved so all methods can access it. If a schema definition exists, we now omit any key featured in the `keysToRemove` array from the param to avoid polluting the swagger document with invalid properties.

## Does this PR introduce a breaking change?
```
[ ] Yes
[X] No
```

<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->


## Other information


Patch:
diff --git a/lib/services/swagger-types-mapper.ts b/lib/services/swagger-types-mapper.ts
index b8b42b1d..e4954547 100644
--- a/lib/services/swagger-types-mapper.ts
+++ b/lib/services/swagger-types-mapper.ts
@@ -8,12 +8,22 @@ import {
 import { ParamWithTypeMetadata } from './parameter-metadata-accessor';
 
 export class SwaggerTypesMapper {
+  private readonly keysToRemove: Array<keyof ApiPropertyOptions | '$ref'> = [
+    'type',
+    'isArray',
+    'enum',
+    'enumName',
+    'items',
+    '$ref',
+    ...this.getSchemaOptionsKeys()
+  ]
+
   mapParamTypes(
     parameters: Array<ParamWithTypeMetadata | BaseParameterObject>
   ) {
     return parameters.map((param) => {
       if (this.hasSchemaDefinition(param as BaseParameterObject)) {
-        return this.omitParamType(param);
+        return this.omitParamKeys(param);
       }
       const { type } = param as ParamWithTypeMetadata;
       const typeName =
@@ -29,27 +39,19 @@ export class SwaggerTypesMapper {
         isUndefined
       );
 
-      const keysToRemove: Array<keyof ApiPropertyOptions | '$ref'> = [
-        'type',
-        'isArray',
-        'enum',
-        'items',
-        '$ref',
-        ...this.getSchemaOptionsKeys()
-      ];
       if (this.isEnumArrayType(paramWithTypeMetadata)) {
         return this.mapEnumArrayType(
           paramWithTypeMetadata as ParamWithTypeMetadata,
-          keysToRemove
+          this.keysToRemove
         );
       } else if (paramWithTypeMetadata.isArray) {
         return this.mapArrayType(
           paramWithTypeMetadata as ParamWithTypeMetadata,
-          keysToRemove
+          this.keysToRemove
         );
       }
       return {
-        ...omit(param, keysToRemove),
+        ...omit(param, this.keysToRemove),
         schema: omitBy(
           {
             ...this.getSchemaOptions(param),
@@ -131,8 +133,8 @@ export class SwaggerTypesMapper {
     return !!param.schema;
   }
 
-  private omitParamType(param: ParamWithTypeMetadata | BaseParameterObject) {
-    return omit(param, 'type');
+  private omitParamKeys(param: ParamWithTypeMetadata | BaseParameterObject) {
+    return omit(param, this.keysToRemove);
   }
 
   private getSchemaOptionsKeys(): Array<keyof SchemaObject> {


Test patch:
diff --git a/e2e/api-spec.json b/e2e/api-spec.json
index 476c0dcb..734639f1 100644
--- a/e2e/api-spec.json
+++ b/e2e/api-spec.json
@@ -761,6 +761,56 @@
           }
         ]
       }
+    },
+    "/api/cats/with-enum/{type}": {
+      "get": {
+        "operationId": "CatsController_getWithEnumParam",
+        "parameters": [
+          {
+            "name": "header",
+            "in": "header",
+            "description": "Test",
+            "required": false,
+            "schema": {
+              "default": "test",
+              "type": "string"
+            }
+          },
+          {
+            "name": "type",
+            "required": true,
+            "in": "path",
+            "schema": {
+              "type": "string",
+              "enum": [
+                "A",
+                "B",
+                "C"
+              ]
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": ""
+          }
+        },
+        "tags": [
+          "cats"
+        ],
+        "security": [
+          {
+            "key2": [],
+            "key1": []
+          },
+          {
+            "bearer": []
+          },
+          {
+            "basic": []
+          }
+        ]
+      }
     }
   }
 }
\ No newline at end of file
diff --git a/e2e/src/cats/cats.controller.ts b/e2e/src/cats/cats.controller.ts
index c60ddc60..9f7e8b79 100644
--- a/e2e/src/cats/cats.controller.ts
+++ b/e2e/src/cats/cats.controller.ts
@@ -8,12 +8,13 @@ import {
   ApiQuery,
   ApiResponse,
   ApiSecurity,
-  ApiTags
+  ApiTags,
+  ApiParam
 } from '../../../lib';
 import { CatsService } from './cats.service';
 import { Cat } from './classes/cat.class';
 import { CreateCatDto } from './dto/create-cat.dto';
-import { PaginationQuery } from './dto/pagination-query.dto';
+import { PaginationQuery, LettersEnum } from './dto/pagination-query.dto';
 
 @ApiSecurity('basic')
 @ApiBearerAuth()
@@ -87,4 +88,13 @@ export class CatsController {
 
   @Get('site*')
   getSite() {}
+
+
+  @Get("with-enum/:type")
+  @ApiParam({
+    name: "type",
+    enum: LettersEnum,
+    enumName: "Letter"
+  })
+  getWithEnumParam(@Param('type') type: LettersEnum) {}
 }