open-api-spex / open_api_spex

Open API Specifications for Elixir Plug applications
Mozilla Public License 2.0
681 stars 177 forks source link

Multiple responses for one response code #577

Open jimpanse42 opened 7 months ago

jimpanse42 commented 7 months ago

Hey,

for OpenAPI 3.x specs there is the possibility to declare multiple responses for one specific response code, as per example here:

openapi: 3.0.0
...
paths:
  /path:
    get:
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/ResponseOne'
                  - $ref: '#/components/schemas/ResponseTwo'

Would it be possible to extend the controller response definition to be able to pass multiple response schemas aswell? For cases where an additional query parameter changes the returned object or adds fields to it.

E.g.

  operation :test,
    summary: "Test",
    request_body: {"url", "application/json"},
    responses: %{
      200 => [{"Response one", "application/json", nil}, {"Response two", "application/json", nil}]

Thanks in advance

Cervajz commented 5 months ago

Hello,

I'd like to also know if this is possible or planned?

Thank you

zorbash commented 5 months ago

@jimpanse42 You example:

paths:
  /path:
    get:
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/ResponseOne'
                  - $ref: '#/components/schemas/ResponseTwo'

Does not specify multiple responses for the the same response code, but multiple response body schemas. If both '#/components/schemas/ResponseOne' and '#/components/schemas/ResponseOne' are response definitions then the spec won't do what you expect it to.

I gave it a go with the following:

openapi: 3.0.2
info:
  title: Some Service
  description: Some description
  version: 1.0.0
paths:
  /users:
    get:
      summary: Gets a list of users.
      responses:
        '401':
          $ref: '#/components/responses/Unauthorized'
        '200':
          description: ok
          content:
            application/json:
              schema:
                $ref: '#/components/responses/Unauthorized'

# Descriptions of common components
components:
  responses:
    NotFound:
      description: The specified resource was not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Unauthorized
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    # Schema for error response body
    Error:
      type: object
      properties:
        code:
          type: string
        message:
          type: string
      required:
        - code
        - message

And you will notice the GET /users -> 200 is not associated with the schema as one might expect.

Screenshot 2024-02-07 at 13 02 46

However if what you're trying to achieve is associating the response of multiple media types with schemas this can be done with something like:

  operation :update,
    summary: "Update user",
    parameters: [
      Operation.parameter(:id, :path, :integer, "User ID", example: 1001)
    ],
    request_body:
      Operation.request_body(
        "User params",
        %{
          "application/json" => [example: "json example request"],
          "application/text" => [example: "text example request"]
        },
        DslController.UserParams
      ),
    responses: [
      not_found:
        Operation.response(
          "User not found response",
          %{
            "application/json" => [schema: DslController.UserResponse],
            "application/text" => [schema: DslController.UserResponse]
          },
          nil,
          headers: %{
            "content-type" => %OpenApiSpex.Header{
              description: "Type of the content for the response",
              example: "content-type: application/json; charset=utf-8"
            }
          }
        )
    ],
    tags: ["custom"],
    security: [%{"two" => ["another"]}]

and to associate a single media type with multiple schemas you can point to an oneOf / anyOf schema.