Forum Discussion

Southclaws's avatar
Southclaws
New Member
3 years ago

Valid to allow allOf entries to conflict and rely on the latest entry to override (with example)

Hey all! I build fairly large APIs but always try to keep the schemas 1. reusable and 2. logically separated.

 

One requirement I often run into is the ability for the representation of a field to differ slightly to the mutable version. One example would be prices or monetary values in general.

 

When doing patch calls, I want to accept numbers but when getting the data, I want to return strings (or, in my case, a more complex object that contains a currency, a pre-formatted representation, the raw/inaccurate Float32/Number value, etc)

 

So what I've settled on is something like this:

 

edit: here's a link because formatting is broken: https://hastebin.com/isawawocuy.yaml

 

openapi: 3.0.0

info:

  title: "Example of allOf key precedence"

  version: "0.0.0"

  contact: {}

paths: {}

components:

  schemas:

    ProductImmutable:

      type: object

      properties:

        price:

          type: string

    ProductMutable:

      type: object

      properties:

        price:

          type: number

    Product:

      type: object

      allOf:

        - { $ref: "#/components/schemas/ProductMutable" }

        - { $ref: "#/components/schemas/ProductImmutable" }
 
(this forum software is a bit broken so pasting a bit of code broke everything... where's the markdown!?)
 
Now this works, my code generates as expected. My PATCH endpoint can use *just* the mutable schema and the GET endpoint can use the full "Product" schema and show everything at once. All the validators and UIs render this correctly:
 

 

As desired, the most recent entry into the `allOf` list overrides earlier conflicting keys.

 
But what I want to know is, is this a valid and acceptable way to do this? It's really concise and I'd hate to have to define these schemas twice (one for the patch and one for the get) since the whole point of this is to reduce duplication and re-use schemas. But I don't want to be relying on undefined behaviour. I can't find any definitive answer in the reference. Both my frontend and backend code generation tools are generating valid code though.
 
Thanks!
  • Hey Southclaws 

     

    Interesting question! 

    tl;dr it's not technically valid. And it's a quirk of how OpenAPI tooling often misuses the `allOf` keyword to do inheritance. 

     

    For reference: https://json-schema.org/understanding-json-schema/reference/combining.html . As the name implies (allOf) the instance (ie: JSON data) needs to be valid against all the schemas, independently. So having one with type: string and another with type: number, makes it impossible to match both. 

     

    I don't think I'd recommend having the same field change type between read and write, one way of being valid might be to use those flags (readOnly and writeOnly). But given that they share the field name, tooling may not give you what you want. 

     

            price:
              oneOf:
              - type: string
                readOnly: true
              - type: number
                writeOnly: true