Re-using model with different required properties

NotNone picture NotNone · Nov 28, 2016 · Viewed 8.2k times · Source

I have a path that uses complex models with almost identical properties for each http method. The problem is that I want to define some required properties for the request of PUT and POST, while no properties are required in GET response (as the server always returns all properties and it's mentioned elsewhere in the documentation).

I created a simple cat API to demonstrate what I've tried. The idea is that for GET response the response model doesn't have anything marked as required, but the request of PUT must have a name for the cat.

swagger: "2.0"

info:
  title: "Cat API"
  version: 1.0.0

paths:
  /cats/{id}:
    parameters:
      - name: id
        in: path
        required: true
        type: integer
    get:
      responses:
        200:
          description: Return a cat
          schema:
            $ref: "#/definitions/GetCat"
    put:
      parameters:
        - name: cat
          in: body
          required: true
          schema:
            $ref: "#/definitions/PutCat"
      responses:
        204:
          description: Cat edited

definitions:
  Cat:
    type: object
    properties:
      name:
        type: string
  GetCat:
    allOf:
      - $ref: "#/definitions/Cat"
    properties:
      id:
        type: integer
  PutCat:
    type: object
    required:
      - name
    properties:
      $ref: "#/definitions/Cat/properties"

Swagger Editor says that this is a valid specification, but name is set as required for both GET and PUT. The same goes with Swagger UI.

I also tried the following version of PutCat:

PutCat:
  type: object
  required:
    - name
  allOf:
    - $ref: "#/definitions/Cat"

But now everything is optional.

I can't figure this out. Is there a way to do this properly?

EDIT:

As Helen correctly mentioned, I can use readOnly to solve this particular case with GET and PUT.

But let's say I add breed property which must be provided (in addition to the name property) for PUT. Then I add PATCH method, which can be used to update either breed or name while the other remains unchanged, and I want to set neither of those as required.

Answer

Helen picture Helen · Nov 28, 2016

In your example, you can use a single model for both GET and POST/PUT, with properties only used in the GET response marked as readOnly. From the spec:

readOnly

Declares the property as "read only". This means that it MAY be sent as part of a response but MUST NOT be sent as part of the request. Properties marked as readOnly being true SHOULD NOT be in the required list of the defined schema. Default value is false.

The spec would look like:

    get:
      responses:
        200:
          description: Return a cat
          schema:
            $ref: "#/definitions/Cat"
    put:
      parameters:
        - name: cat
          in: body
          required: true
          schema:
            $ref: "#/definitions/Cat"
      responses:
        204:
          description: Cat edited

definitions:
  Cat:
    properties:
      id:
        type: integer
        readOnly: true
      name:
        type: string
      breed:
        type: string
    required:
      - name
      - breed

This means you must PUT the name and breed:

{
  "name": "Puss in Boots",
  "breed": "whatever"
}

and GET /cats/{id} must return the name and breed and may also return the id:

{
  "name": "Puss in Boots",
  "breed": "whatever",
  "id": 5
}