Repository: nestjs/swagger
Pull request #536 (2020-01-30 09:17:44)

feat(api-extension): add support for open api extensions via decorator (#195)
This commit adds support for Open API Extensions via a @ApiExtension decorator. 
Usage: 
```
@ApiExtension(<extensionName>, <extensionPropertyObject>)
```
Example:
```
@ApiExtension('x-foo', { hello: 'world' })
```

## 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)
- [x] 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". -->
```
[ ] Bugfix
[x] 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?
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->

Issue Number: Solves #195 


## What is the new behavior?
```
@ApiExtension('x-foo', { hello: 'world' })
```
Adds an extension to the Swagger Specification: 
<img width="688" alt="Screenshot 2020-01-30 at 08 36 45" src="https://user-images.githubusercontent.com/1887572/73435816-44893780-4341-11ea-920e-62269038eb65.png">

## 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/constants.ts b/lib/constants.ts
index e85b7230..e928001a 100644
--- a/lib/constants.ts
+++ b/lib/constants.ts
@@ -11,5 +11,6 @@ export const DECORATORS = {
   API_MODEL_PROPERTIES_ARRAY: `${DECORATORS_PREFIX}/apiModelPropertiesArray`,
   API_SECURITY: `${DECORATORS_PREFIX}/apiSecurity`,
   API_EXCLUDE_ENDPOINT: `${DECORATORS_PREFIX}/apiExcludeEndpoint`,
-  API_EXTRA_MODELS: `${DECORATORS_PREFIX}/apiExtraModels`
+  API_EXTRA_MODELS: `${DECORATORS_PREFIX}/apiExtraModels`,
+  API_EXTENSION: `${DECORATORS_PREFIX}/apiExtension`
 };
diff --git a/lib/decorators/api-extension.decorator.ts b/lib/decorators/api-extension.decorator.ts
new file mode 100644
index 00000000..9926009e
--- /dev/null
+++ b/lib/decorators/api-extension.decorator.ts
@@ -0,0 +1,18 @@
+import { DECORATORS } from '../constants';
+import { createMixedDecorator } from './helpers';
+
+export function ApiExtension(extensionKey: string, extensionProperties: any) {
+  if (!extensionKey.startsWith('x-')) {
+    throw new Error(
+      'Extension key is not prefixed. Please ensure you prefix it with `x-`.'
+    );
+  }
+
+  const extensionObject = {
+    [extensionKey]: {
+      ...extensionProperties
+    }
+  };
+
+  return createMixedDecorator(DECORATORS.API_EXTENSION, extensionObject);
+}
diff --git a/lib/decorators/api-security.decorator.ts b/lib/decorators/api-security.decorator.ts
index f93725b1..3bc61c12 100644
--- a/lib/decorators/api-security.decorator.ts
+++ b/lib/decorators/api-security.decorator.ts
@@ -12,7 +12,7 @@ export function ApiSecurity(
     target: object,
     key?: string | symbol,
     descriptor?: TypedPropertyDescriptor<any>
-  ): any => {
+  ) => {
     if (descriptor) {
       metadata = extendMetadata(
         metadata,
@@ -24,7 +24,7 @@ export function ApiSecurity(
         metadata,
         descriptor.value
       );
-      return descriptor;
+      return descriptor.value;
     }
     metadata = extendMetadata(metadata, DECORATORS.API_SECURITY, target);
     Reflect.defineMetadata(DECORATORS.API_SECURITY, metadata, target);
diff --git a/lib/decorators/index.ts b/lib/decorators/index.ts
index 67961fce..e528ec18 100644
--- a/lib/decorators/index.ts
+++ b/lib/decorators/index.ts
@@ -20,3 +20,4 @@ export * from './api-query.decorator';
 export * from './api-response.decorator';
 export * from './api-security.decorator';
 export * from './api-use-tags.decorator';
+export * from './api-extension.decorator';
diff --git a/lib/plugin/utils/plugin-utils.ts b/lib/plugin/utils/plugin-utils.ts
index 0eab5bed..2950c612 100644
--- a/lib/plugin/utils/plugin-utils.ts
+++ b/lib/plugin/utils/plugin-utils.ts
@@ -7,7 +7,6 @@ import {
   getTypeArguments,
   isArray,
   isBoolean,
-  isEnum,
   isInterface,
   isNumber,
   isString
@@ -70,12 +69,6 @@ export function getTypeReferenceAsString(
     ) {
       return 'Object';
     }
-    if (isEnum(type)) {
-      return undefined;
-    }
-    if (type.aliasSymbol) {
-      return 'Object';
-    }
     return undefined;
   } catch {
     return undefined;
diff --git a/lib/plugin/visitors/model-class.visitor.ts b/lib/plugin/visitors/model-class.visitor.ts
index cd7953d1..c30909b9 100644
--- a/lib/plugin/visitors/model-class.visitor.ts
+++ b/lib/plugin/visitors/model-class.visitor.ts
@@ -259,13 +259,10 @@ export class ModelClassVisitor extends AbstractFileVisitor {
     if (hasPropertyKey(key, existingProperties)) {
       return undefined;
     }
-    let initializer = node.initializer;
+    const initializer = node.initializer;
     if (!initializer) {
       return undefined;
     }
-    if (ts.isAsExpression(initializer)) {
-      initializer = initializer.expression;
-    }
     return ts.createPropertyAssignment(
       key,
       ts.createIdentifier(initializer.getText())
diff --git a/lib/services/model-properties-accessor.ts b/lib/services/model-properties-accessor.ts
index 3a7bd31a..bebbf759 100644
--- a/lib/services/model-properties-accessor.ts
+++ b/lib/services/model-properties-accessor.ts
@@ -20,23 +20,16 @@ export class ModelPropertiesAccessor {
   }
 
   applyMetadataFactory(prototype: Type<unknown>) {
-    const classPrototype = prototype;
-    do {
-      if (!prototype.constructor) {
-        return;
-      }
-      if (!prototype.constructor[METADATA_FACTORY_NAME]) {
-        continue;
-      }
-      const metadata = prototype.constructor[METADATA_FACTORY_NAME]();
-      const properties = Object.keys(metadata);
-      properties.forEach(key => {
-        createApiPropertyDecorator(metadata[key], false)(classPrototype, key);
-      });
-    } while (
-      (prototype = Reflect.getPrototypeOf(prototype) as Type<any>) &&
-      prototype !== Object.prototype &&
-      prototype
-    );
+    if (!prototype.constructor) {
+      return;
+    }
+    if (!prototype.constructor[METADATA_FACTORY_NAME]) {
+      return;
+    }
+    const metadata = prototype.constructor[METADATA_FACTORY_NAME]();
+    const properties = Object.keys(metadata);
+    properties.forEach(key => {
+      createApiPropertyDecorator(metadata[key], false)(prototype, key);
+    });
   }
 }
diff --git a/lib/swagger-explorer.ts b/lib/swagger-explorer.ts
index 3be43876..aadbd397 100644
--- a/lib/swagger-explorer.ts
+++ b/lib/swagger-explorer.ts
@@ -202,10 +202,12 @@ export class SwaggerExplorer {
       method
     ) as RequestMethod;
     const fullPath = globalPath + this.validateRoutePath(routePath);
+    const apiExtension = Reflect.getMetadata(DECORATORS.API_EXTENSION, method);
     return {
       method: RequestMethod[requestMethod].toLowerCase(),
       path: fullPath === '' ? '/' : fullPath,
-      operationId: this.getOperationId(instance, method)
+      operationId: this.getOperationId(instance, method),
+      ...apiExtension
     };
   }
 
diff --git a/package-lock.json b/package-lock.json
index 939b9d0f..4e337d4f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "@nestjs/swagger",
-  "version": "4.2.3",
+  "version": "4.2.1",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -1196,67 +1196,14 @@
       }
     },
     "@jest/types": {
-      "version": "25.1.0",
-      "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz",
-      "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==",
+      "version": "24.9.0",
+      "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz",
+      "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==",
       "dev": true,
       "requires": {
         "@types/istanbul-lib-coverage": "^2.0.0",
         "@types/istanbul-reports": "^1.1.1",
-        "@types/yargs": "^15.0.0",
-        "chalk": "^3.0.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "4.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
-          "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
-          "dev": true,
-          "requires": {
-            "@types/color-name": "^1.1.1",
-            "color-convert": "^2.0.1"
-          }
-        },
-        "chalk": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
-          "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^4.1.0",
-            "supports-color": "^7.1.0"
-          }
-        },
-        "color-convert": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-          "dev": true,
-          "requires": {
-            "color-name": "~1.1.4"
-          }
-        },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-          "dev": true
-        },
-        "has-flag": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "7.1.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
-          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^4.0.0"
-          }
-        }
+        "@types/yargs": "^13.0.0"
       }
     },
     "@marionebl/sander": {
@@ -1282,9 +1229,9 @@
       }
     },
     "@nestjs/common": {
-      "version": "6.11.4",
-      "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-6.11.4.tgz",
-      "integrity": "sha512-NOtDTUcCSoJAYN+4gvROkMkcDTdRQ+z39ngQQKYNMeXx2SOmTKXG6TaRVbiNN8Q1drri3ooqwG70PWiz/EQbPQ==",
+      "version": "6.11.1",
+      "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-6.11.1.tgz",
+      "integrity": "sha512-ozraRypa0uA0I15lFkCuEC4BaJmruGetDW2VXuJ0hifSl98sHoj+XeZmW5QfNTl8ouPEJ46jc+HtBtyZ1IeR+A==",
       "dev": true,
       "requires": {
         "axios": "0.19.2",
@@ -1302,9 +1249,9 @@
       }
     },
     "@nestjs/core": {
-      "version": "6.11.4",
-      "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-6.11.4.tgz",
-      "integrity": "sha512-l/2dlVLV9MTjtcza7Lk6iqfp5MR+BGt0yGc1wLWKbjibEckgtngQmbGSSbtXMjK/3lTvIX3VYle2k/2DV1vkfg==",
+      "version": "6.11.1",
+      "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-6.11.1.tgz",
+      "integrity": "sha512-pznpX6CzMkMeIilVDntEqmOJLwRA+GACBwD3GW1AJVaIe3xx2QlsClByiGz04vxKADUJvSC+EqJpUbx7ROTAHg==",
       "dev": true,
       "requires": {
         "@nuxtjs/opencollective": "0.2.2",
@@ -1325,9 +1272,9 @@
       }
     },
     "@nestjs/platform-express": {
-      "version": "6.11.4",
-      "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-6.11.4.tgz",
-      "integrity": "sha512-kDCd4U3J2hxzhPr7L4SSjf+pTYt/Pu+CHm+lHCBcRAUuxChPvFgdZkzPBqJtfe5cUPZz+LdWqB1Q8ck8+qy3Ig==",
+      "version": "6.11.1",
+      "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-6.11.1.tgz",
+      "integrity": "sha512-drU+T4uHpyysIBlDSu3qrgRCCRq/h0VYYM0wvJll2Ki++b7xcIoasZSU+cYKurdXRi7x6NNdFHbo8nozXoi7iQ==",
       "dev": true,
       "requires": {
         "body-parser": "1.19.0",
@@ -1338,9 +1285,9 @@
       }
     },
     "@nestjs/platform-fastify": {
-      "version": "6.11.4",
-      "resolved": "https://registry.npmjs.org/@nestjs/platform-fastify/-/platform-fastify-6.11.4.tgz",
-      "integrity": "sha512-CWVLKSiLixawMYr9OCvNfU3R8cwvDvqvZTdl3udE5xfnsYAMsTB5jF/8LE3CP70ADuukzEeLDgiLII1plO0uxg==",
+      "version": "6.11.1",
+      "resolved": "https://registry.npmjs.org/@nestjs/platform-fastify/-/platform-fastify-6.11.1.tgz",
+      "integrity": "sha512-LtQ5Aoxbo+R6SzLESvE9R3E44w4dtX6TT3E/mG2/255yRoFNp5twOKbv/6w8Xt8vW6sFjV1wdEe5Kpt/+a1nww==",
       "dev": true,
       "requires": {
         "fastify": "2.11.0",
@@ -1611,13 +1558,12 @@
       }
     },
     "@types/jest": {
-      "version": "25.1.1",
-      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.1.1.tgz",
-      "integrity": "sha512-bKSZJYZJLzwaoVYNN4W3A0RvKNYsrLm5tsuXaMlfYDxKf4gY2sFrMYneCugNQWGg1gjPW+FHBwNrwPzEi4sIsw==",
+      "version": "24.9.1",
+      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.9.1.tgz",
+      "integrity": "sha512-Fb38HkXSVA4L8fGKEZ6le5bB8r6MRWlOCZbVuWZcmOMSCd2wCYOwN1ibj8daIoV9naq7aaOZjrLCoCMptKU/4Q==",
       "dev": true,
       "requires": {
-        "jest-diff": "^25.1.0",
-        "pretty-format": "^25.1.0"
+        "jest-diff": "^24.3.0"
       }
     },
     "@types/lodash": {
@@ -1657,9 +1603,9 @@
       "dev": true
     },
     "@types/yargs": {
-      "version": "15.0.2",
-      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.2.tgz",
-      "integrity": "sha512-hFkuAp58M2xOc1QgJhkFrLMnqa8KWTFRTnzrI1zlEcOfg3DZ0eH3aPAo/N6QlVVu8E4KS4xD1jtEG3rdQYFmIg==",
+      "version": "13.0.6",
+      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.6.tgz",
+      "integrity": "sha512-IkltIncDQWv6fcAvnHtJ6KtkmY/vtR3bViOaCzpj/A3yNhlfZAgxNe6AEQD1cQrkYD+YsKVo08DSxvNKEsD7BA==",
       "dev": true,
       "requires": {
         "@types/yargs-parser": "*"
@@ -3320,9 +3266,9 @@
       }
     },
     "diff-sequences": {
-      "version": "25.1.0",
-      "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.1.0.tgz",
-      "integrity": "sha512-nFIfVk5B/NStCsJ+zaPO4vYuLjlzQ6uFvPxzYyHlejNZ/UGa7G/n7peOXVrVNvRuyfstt+mZQYGpjxg9Z6N8Kw==",
+      "version": "24.9.0",
+      "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz",
+      "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==",
       "dev": true
     },
     "dir-glob": {
@@ -5760,67 +5706,15 @@
       }
     },
     "jest-diff": {
-      "version": "25.1.0",
-      "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.1.0.tgz",
-      "integrity": "sha512-nepXgajT+h017APJTreSieh4zCqnSHEJ1iT8HDlewu630lSJ4Kjjr9KNzm+kzGwwcpsDE6Snx1GJGzzsefaEHw==",
+      "version": "24.9.0",
+      "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz",
+      "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==",
       "dev": true,
       "requires": {
-        "chalk": "^3.0.0",
-        "diff-sequences": "^25.1.0",
-        "jest-get-type": "^25.1.0",
-        "pretty-format": "^25.1.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "4.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
-          "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
-          "dev": true,
-          "requires": {
-            "@types/color-name": "^1.1.1",
-            "color-convert": "^2.0.1"
-          }
-        },
-        "chalk": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
-          "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^4.1.0",
-            "supports-color": "^7.1.0"
-          }
-        },
-        "color-convert": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-          "dev": true,
-          "requires": {
-            "color-name": "~1.1.4"
-          }
-        },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-          "dev": true
-        },
-        "has-flag": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "7.1.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
-          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^4.0.0"
-          }
-        }
+        "chalk": "^2.0.1",
+        "diff-sequences": "^24.9.0",
+        "jest-get-type": "^24.9.0",
+        "pretty-format": "^24.9.0"
       }
     },
     "jest-docblock": {
@@ -6122,9 +6016,9 @@
       }
     },
     "jest-get-type": {
-      "version": "25.1.0",
-      "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz",
-      "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==",
+      "version": "24.9.0",
+      "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz",
+      "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==",
       "dev": true
     },
     "jest-haste-map": {
@@ -7848,9 +7742,9 @@
       "dev": true
     },
     "lint-staged": {
-      "version": "10.0.5",
-      "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.0.5.tgz",
-      "integrity": "sha512-Y6V53LnwGxxUD0CCPTCGDlQx2l3RkW0gebgc5XBxKWEf1r0K1b1imedA1owQF7c3+lC9fp7AjWMrRiBsHSCmNQ==",
+      "version": "10.0.3",
+      "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.0.3.tgz",
+      "integrity": "sha512-N071+M0yxKX0u67UOB8emFms29ekzrKoX1UlPcLS615O/rhx3+xRk5so8uU4tjdg4yNsD3ntdPSsuZnUPzblLA==",
       "dev": true,
       "requires": {
         "chalk": "^3.0.0",
@@ -9306,46 +9200,21 @@
       "dev": true
     },
     "pretty-format": {
-      "version": "25.1.0",
-      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz",
-      "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==",
+      "version": "24.9.0",
+      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz",
+      "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==",
       "dev": true,
       "requires": {
-        "@jest/types": "^25.1.0",
-        "ansi-regex": "^5.0.0",
-        "ansi-styles": "^4.0.0",
-        "react-is": "^16.12.0"
+        "@jest/types": "^24.9.0",
+        "ansi-regex": "^4.0.0",
+        "ansi-styles": "^3.2.0",
+        "react-is": "^16.8.4"
       },
       "dependencies": {
         "ansi-regex": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
-          "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
-          "dev": true
-        },
-        "ansi-styles": {
-          "version": "4.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
-          "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
-          "dev": true,
-          "requires": {
-            "@types/color-name": "^1.1.1",
-            "color-convert": "^2.0.1"
-          }
-        },
-        "color-convert": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-          "dev": true,
-          "requires": {
-            "color-name": "~1.1.4"
-          }
-        },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
           "dev": true
         }
       }
diff --git a/package.json b/package.json
index cbe7ad0b..a9d3407c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nestjs/swagger",
-  "version": "4.2.3",
+  "version": "4.2.1",
   "description": "Nest - modern, fast, powerful node.js web framework (@swagger)",
   "author": "Kamil Mysliwiec",
   "license": "MIT",
@@ -25,18 +25,18 @@
   "devDependencies": {
     "@commitlint/cli": "8.3.5",
     "@commitlint/config-angular": "8.3.4",
-    "@nestjs/common": "6.11.4",
-    "@nestjs/core": "6.11.4",
-    "@nestjs/platform-express": "6.11.4",
-    "@nestjs/platform-fastify": "6.11.4",
-    "@types/jest": "25.1.1",
+    "@nestjs/common": "6.11.1",
+    "@nestjs/core": "6.11.1",
+    "@nestjs/platform-express": "6.11.1",
+    "@nestjs/platform-fastify": "6.11.1",
+    "@types/jest": "24.9.1",
     "@types/lodash": "4.14.149",
     "@types/node": "11.15.0",
     "express": "4.17.1",
     "fastify-swagger": "2.5.0",
     "husky": "4.2.1",
     "jest": "25.1.0",
-    "lint-staged": "10.0.5",
+    "lint-staged": "10.0.3",
     "prettier": "1.19.1",
     "reflect-metadata": "0.1.13",
     "release-it": "12.4.3",


Test patch:
diff --git a/e2e/api-spec.json b/e2e/api-spec.json
index ae35f968..8be0dc8c 100644
--- a/e2e/api-spec.json
+++ b/e2e/api-spec.json
@@ -224,6 +224,9 @@
     "/api/cats": {
       "post": {
         "operationId": "CatsController_create",
+        "x-foo": {
+          "test": "bar "
+        },
         "summary": "Create cat",
         "parameters": [],
         "requestBody": {
diff --git a/e2e/src/cats/cats.controller.ts b/e2e/src/cats/cats.controller.ts
index 19f7e08e..6d189548 100644
--- a/e2e/src/cats/cats.controller.ts
+++ b/e2e/src/cats/cats.controller.ts
@@ -5,6 +5,7 @@ import {
   ApiOperation,
   ApiResponse,
   ApiSecurity,
+  ApiExtension,
   ApiTags
 } from '../../../lib';
 import { CatsService } from './cats.service';
@@ -28,6 +29,7 @@ export class CatsController {
     type: () => Cat
   })
   @ApiResponse({ status: 403, description: 'Forbidden.' })
+  @ApiExtension('x-foo', { test: 'bar ' })
   async create(@Body() createCatDto: CreateCatDto): Promise<Cat> {
     return this.catsService.create(createCatDto);
   }
diff --git a/test/plugin/fixtures/create-cat-alt.dto.ts b/test/plugin/fixtures/create-cat-alt.dto.ts
index 9b6b8989..3a07b141 100644
--- a/test/plugin/fixtures/create-cat-alt.dto.ts
+++ b/test/plugin/fixtures/create-cat-alt.dto.ts
@@ -10,11 +10,6 @@ interface Node {
     id: number;
 }
 
-type AliasedType = {
-    type: string;
-};
-type NumberAlias = number;
-
 export class CreateCatDto2 {
   name: string;
   age: number = 3;
@@ -22,16 +17,12 @@ export class CreateCatDto2 {
   status: Status = Status.ENABLED;
   readonly breed?: string;
   nodes: Node[];
-  alias: AliasedType;
-  numberAlias: NumberAlias;
   nested: {
       first: string,
       second: number,
       status: Status,
       tags: string[],
       nodes: Node[]
-      alias: AliasedType,
-      numberAlias: NumberAlias,
   }
 }
 `;
@@ -47,7 +38,7 @@ export class CreateCatDto2 {
         this.status = Status.ENABLED;
     }
     static _OPENAPI_METADATA_FACTORY() {
-        return { name: { required: true, type: () => String }, age: { required: true, type: () => Number, default: 3 }, tags: { required: true, type: () => [String] }, status: { required: true, default: Status.ENABLED, enum: Status }, breed: { required: false, type: () => String }, nodes: { required: true, type: () => [Object] }, alias: { required: true, type: () => Object }, numberAlias: { required: true, type: () => Number }, nested: { required: true, type: () => ({ first: { required: true, type: () => String }, second: { required: true, type: () => Number }, status: { required: true, enum: Status }, tags: { required: true, type: () => [String] }, nodes: { required: true, type: () => [Object] }, alias: { required: true, type: () => Object }, numberAlias: { required: true, type: () => Number } }) } };
+        return { name: { required: true, type: () => String }, age: { required: true, type: () => Number, default: 3 }, tags: { required: true, type: () => [String] }, status: { required: true, default: Status.ENABLED, enum: Status }, breed: { required: false, type: () => String }, nodes: { required: true, type: () => [Object] }, nested: { required: true, type: () => ({ first: { required: true, type: () => String }, second: { required: true, type: () => Number }, status: { required: true, enum: Status }, tags: { required: true, type: () => [String] }, nodes: { required: true, type: () => [Object] } }) } };
     }
 }
 `;