lukeautry / tsoa

Build OpenAPI-compliant REST APIs using TypeScript and Node
MIT License
3.33k stars 481 forks source link

fix: Store all Response examples by name #1571

Closed MiguelSavignano closed 4 months ago

MiguelSavignano commented 4 months ago

Allow use multiple times the @Response decorator with the same status and different examples.

expected result:

Controller with @Response status 400 and 2 response examples.

  @Response<ErrorResponseModel>('400', 'Bad Request', { status: 400, message: 'reason 1' })
  @Response<ErrorResponseModel>('400', 'Bad Request', { status: 400, message: 'reason 2' })
  @Response<ErrorResponseModel>('401', 'Unauthorized')
  @Response<ErrorResponseModel>('default', 'Unexpected error', { status: 500, message: 'Something went wrong!' })
  @Get('MultiResponse')
  public async multiResponse(): Promise<TestModel> {
    return new ModelService().getModel();
  }

Swagger.json:

"responses": {
  "400": {
    "description": "Bad Request",
    "content": {
      "application/json": {
        "schema": {},
        "examples": {
          "Example 1": {
            "value": {
              "status": 400,
              "message": "reason 1"
            }
          },
          "Example 2": {
            "value": {
              "status": 400,
              "message": "reason 2"
            }
          }
        }
      }
    }
  }
}

All Submissions:

Closing issues

Closes #1570

If this is a new feature submission:

Potential Problems With The Approach

Test plan

WoH commented 4 months ago

LGTM, thanks!

mishelen commented 3 months ago

hey @WoH I'm using tsoa@6.2.0 for openapi3.x and seems that this PR is messing up some data

Здымак экрана ад 2024-04-14 16-48-26

then in the spec it overwrites MIME types:

Здымак экрана ад 2024-04-14 16-50-11

So, there is only available single type text/plain, obviously, as generated section for 400 looks this way, so it also breaks schema for response!

{
  "400": {
    "description": "Bad Request",
    "content": {
      "text/plain": {
        "schema": {
          "type": "string"
        },
        "examples": {
          "Example 1": {
            "value": {
              "message": "Validation Error",
              "details": {
                "body.sessionId": {
                  "message": "'sessionId' is required"
                }
              }
            }
          },
          "Example 2": {
            "value": "Unsupported data provided"
          },
          "Example 3": {
            "value": "Session already exists"
          }
        }
      }
    }
  }
}

Update: When I remove produces param, then all responses are marked as application/json So in that cases it ignores generic type provided to decorator:

  @Response<ValidationError>(400, ReasonPhrases.BAD_REQUEST, ValidationErrorSample)
  @Response<string>(400, ReasonPhrases.BAD_REQUEST, 'Unsupported data provided')
  @Response<string>(400, ReasonPhrases.BAD_REQUEST, 'Session already exists')

Produced spec has also broken schema -- just string:

{
  "400": {
    "description": "Bad Request",
    "content": {
      "application/json": {
        "schema": {
          // it's not just a string
          "type": "string"
        },
        "examples": {
          "Example 1": {
            "value": {
              "message": "Validation Error",
              "details": {
                "fieldName": {
                  "message": "Description of the specific error"
                }
              }
            }
          },
          "Example 2": {
            "value": "Unsupported data provided"
          },
          "Example 3": {
            "value": "Session already exists"
          }
        }
      }
    }
  }
}

And then if I remove generic type and add produces --produces is ignored at all.

Ideally I want to get MIME type from Response generic type, to not provide produces, while produces should be used as last-ditch effort when defining MIME type.

Possible problem: