Re-using model with different required properties
Asked Answered
I

2

43

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 its 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.

Isoclinal answered 28/11, 2016 at 8:13 Comment(0)
S
37

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
}
Stemma answered 28/11, 2016 at 12:1 Comment(5)
Thanks, that solves the issue in the example. However, I was a bit careless with my example and updated the question with the scenario of adding a PATCH method for partial updates that doesn't required anything.Isoclinal
Thank you, but now it looks like both name and breed are required for PATCH as well (there's asterisk after both in Swagger Editor). This is the same problem as in the original question. I believe there is no solution for this as of now.Isoclinal
@Stemma But the allOf keyword will also appear in the formatted structure and it is ugly. Is there any way to prevent it from appearing in the structure?Kreindler
The syntax with $ref underneath properties doesn't appear to work with Open API 3.0.Fourgon
@ChrisHaines: Yeah that example is not valid... I'll remove it. Unfortunately, there's no elegant way to handle GET/POST/PATCH differences. :(Stemma
G
11

I've had a similar problem. Turned out that my indentation was wrong. The following syntax should work:

PutCat:
  allOf:
    - $ref: "#/definitions/Cat"
    - type: object
      required: [name]
Gala answered 21/3, 2018 at 15:15 Comment(1)
This is the best solution to hide some fields from put which are only required for post. By adding a properties: there on the level of "type", and just the property name with a readOnly: true.Tramline

© 2022 - 2024 — McMap. All rights reserved.