Repository: nestjs/swagger (ts)

Issue:
Incorrect Open-API definition when using @All(...) route decorator
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Current behavior

When using the @All(...) route decorator in a controller and then generating the `swagger.json` from that, it will result in a definition like this:

```typescript
@Controller('proxy')
export class ProxyController {

  @All('*')
  public async proxy(...) {...}
}
```

```json
"/proxy/*": {
      "all": {...}
}
```

When opening this in the [Swagger Editor](https://editor.swagger.io/) it shows an error like:

> Structural error at paths./proxy/*
should NOT have additional properties
additionalProperty: all

According to the [Open-API specification](https://spec.openapis.org/oas/latest.html#path-item-object) `all` is indeed not allowed there, only `get`, `post`, ...




### Minimum reproduction code

https://github.com/timoklingenhoefer/swagger_bug

### Steps to reproduce

1. create new app
2. create controller like the one above
3. create swagger.json by adding this to the `bootstrap` function
```typescript
    const app = await NestFactory.create(ApplicationModul);
    const document = SwaggerModule.createDocument(app, new DocumentBuilder().build()););

    fs.writeFileSync('swagger.json', JSON.stringify(document, null, 2));
```
4. open `swagger.json` in the swagger editor
5. see error

### Expected behavior

Not sure how a correct definition would look like. 

Maybe copy-pasting the content of what would be the `all` block to `get`, `post`, ... blocks.

### Package version

6.1.3  (latest)

### NestJS version

9.1.6   (latest)

### Node.js version

_No response_

### In which operating systems have you tested?

- [ ] macOS
- [X] Windows
- [X] Linux

### Other

_No response_

Pull request:
fix(lib): fixed incorrect handling of @All decorator
## 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". -->
- [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?
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->

As NestJS defines `All` as a new request-method to handle endpoints, that should be called by every method, a bug gets introduced when this `all` pseudo-method gets used in the Open-API definitions. `All` is not a valid request-method in the Open-API specification and therefore routes with this mehtod do not get recognised by tools using the Open-API spec.

Issue Number: fixes #2143 


## What is the new behavior?
This PR adds a workaround when the metadata contains the `all`-method type.

When the request-method is `all`, the explorer no longer generates a route with the `all` method, but instead generates multiple routes, one for every request-method, with the content of what would previously have been in the `all` route. 

## 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. -->


Patch:
diff --git a/lib/swagger-explorer.ts b/lib/swagger-explorer.ts
index 1ee4a74b..2e052031 100644
--- a/lib/swagger-explorer.ts
+++ b/lib/swagger-explorer.ts
@@ -287,19 +287,33 @@ export class SwaggerExplorer {
       },
       requestMethod
     );
-    return allRoutePaths.map((routePath) => {
-      const fullPath = this.validateRoutePath(routePath);
-      const apiExtension = Reflect.getMetadata(
-        DECORATORS.API_EXTENSION,
-        method
-      );
-      return {
-        method: RequestMethod[requestMethod].toLowerCase(),
-        path: fullPath === '' ? '/' : fullPath,
-        operationId: this.getOperationId(instance, method),
-        ...apiExtension
-      };
-    });
+    return flatten(
+      allRoutePaths.map((routePath) => {
+        const fullPath = this.validateRoutePath(routePath);
+        const apiExtension = Reflect.getMetadata(
+          DECORATORS.API_EXTENSION,
+          method
+        );
+        if (requestMethod === RequestMethod.ALL) {
+          // apply workaround for invalid "ALL" Method
+          const validMethods = Object.values(RequestMethod).filter(
+            (meth) => meth !== 'ALL' && typeof meth === 'string'
+          ) as string[];
+          return validMethods.map((meth) => ({
+            method: meth.toLowerCase(),
+            path: fullPath === '' ? '/' : fullPath,
+            operationId: `${this.getOperationId(instance, method)}_${meth.toLowerCase()}`,
+            ...apiExtension
+          }));
+        }
+        return {
+          method: RequestMethod[requestMethod].toLowerCase(),
+          path: fullPath === '' ? '/' : fullPath,
+          operationId: this.getOperationId(instance, method),
+          ...apiExtension
+        };
+      })
+    );
   }
 
   private getOperationId(instance: object, method: Function): string {


Test patch:
diff --git a/test/explorer/swagger-explorer.spec.ts b/test/explorer/swagger-explorer.spec.ts
index 4d2ebdf0..84645560 100644
--- a/test/explorer/swagger-explorer.spec.ts
+++ b/test/explorer/swagger-explorer.spec.ts
@@ -1,4 +1,5 @@
 import {
+  All,
   Body,
   Controller,
   Get,
@@ -1465,7 +1466,38 @@ describe('SwaggerExplorer', () => {
       });
     });
   });
+  
+  describe('when @All(...) is used', () => {
 
+    @Controller('')
+    class AllController {
+
+      @All('*')
+      all(): Promise<void> {
+        return Promise.resolve();
+      }
+    }
+
+    it('should create route for every method', () => {
+      const explorer = new SwaggerExplorer(schemaObjectFactory);
+      const routes = explorer.exploreController(
+        {
+          instance: new AllController(),
+          metatype: AllController
+        } as InstanceWrapper<AllController>,
+        new ApplicationConfig(),
+        'modulePath',
+        'globalPrefix'
+      );
+
+      expect(routes.length).toEqual(7);
+      expect(['get', 'post', 'put', 'delete', 'patch', 'options', 'head'].every((method) => routes.find((route) => route.root.method === method))).toBe(true);
+      expect(routes.find((route) => route.root.method === 'all')).toBe(undefined);
+      // check if all routes are equal except for method
+      expect(routes.filter((v, i, a) => a.findIndex(v2 => ['path', 'parameter'].every(k => v2[k] === v[k])) === i).length).toEqual(1);
+    });
+  });
+  
   describe('when global paramters are defined', () => {
     class Foo {}