3

I have a User resource:

I want to define a PATCH /users/{uid} so that the client can update image, bio or both.

An example valid request body would be:

{
  "image": "filename.jpg",
  "bio": "My biography"
}

If the image property is sent alone the existing bio property will remain the same on the server and only the image will be updated. If both are sent (as above) both will change.

In short:

  • an empty request body {} is disallowed.

  • {"image": "new.jpg"}, {"bio": "new bio" or {"image": "new.jpg", "bio": "new bio" is allowed.

This is what I have so far. I'm using the anyOf object with two separate type: objects within. I've tried this with Swagger hub using the virtserver, but the virtual server always seems to return 200 OK and passes back the example data regardless of whatever is passed, so I have no way to know.

Does my definition do what I intended? If not, what the best practice?

openapi: 3.0.0
    ...
    
    patch:
      summary: update a user
      parameters:
        - in: path
          name: uid
          description: user id
          schema:
            type: string
          required: true
      requestBody:
        description: Update a user's profile
        content:
          application/json:
            schema:
              type: object
              anyOf:
                - type: object
                  properties:
                    image:
                      type: string
                - type: object
                  properties:
                    bio:
                      type: string
              additionalProperties: false
        required: true
      responses:
        '200':
          description: Successfully updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
namwa
  • 45
  • 4

1 Answers1

3

You can use minProperties: 1:

      requestBody:
        description: Update a user's profile
        content:
          application/json:
            schema:
              type: object
              properties:
                image:
                  type: string
                bio:
                  type: string
              minProperties: 1   # <-----------
              additionalProperties: false

or anyOf + required:

              type: object
              properties:
                image:
                  type: string
                bio:
                  type: string
              anyOf:   # <-----------
                - required: [image]
                - required: [bio]
              additionalProperties: false

Your original example defines an empty object {} because:

  1. Without required or minProperties defined, all properties are optional.
  2. More importantly, additionalProperties: false only knows about the properties defined directly alongside it and has no visibility into subschemas. So, in this example it disallows ALL properties.

As for:

I've tried this with SwaggerHub using the VirtServer, but the virtual server always seems to return 200 OK and passes back the example data regardless of whatever is passed.

This is because SwaggerHub mocks do not validate inputs and always return a static response based on the response schema.

From SwaggerHub documentation:

Note that the mock does not support business logic, that is, it cannot send specific responses based on the input.

...

The mock generates static responses for each API operation based on its responses and the response media types defined in the spec.

If an operation has multiple response codes, the mock returns the response with the lowest status code. For example, if an operation has responses 201, 202 and 400, the mock returns the 201 response.

Helen
  • 87,344
  • 17
  • 243
  • 314