question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Overriding object keys in allOf - valid or undefined behaviour?

See original GitHub issue

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:

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" }

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 and as desired, the most recent entry into the allOf list overrides earlier conflicting keys.

image

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

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!

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:7 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
handrewscommented, Nov 6, 2022

@Southclaws anything that does any sort of overriding or merging of schema assertions (any keyword that can cause validation to fail) or applicators (any keyword that applies a schema to an instance location, including $ref and all the keywords with subschemas in their values) is not compliant with the JSON Schema specifications.

Some code generation tools in particular seem to impose semantics on the ordering of keywords like allOf but that behavior is implementation-specific.

Annotation keywords (like title) defer handling of multiple values to the application (from JSON Schema’s point of view, both the OpenAPI specification and all implementations of it are “applications”). But those are the only keywords that do that. And some annotations (like readOnly and writeOnly) have pretty clear semantics that applications shouldn’t change (e.g. if any schema marks a field as readOnly it should be considered read-only).

Generally what you want to do is factor out the properties that vary and only pull them in at the point where they are needed.

Handling variations that depend on HTTP context (e.g. PATCH vs GET) is a very complex topic without a clear solution. It’s something we’re discussing a lot for 4.0 (although I think an extension could be done for 3.1 as well).

1reaction
rafalkrupinskicommented, Nov 4, 2022
schemas:
  ProductCommon:
    properties:
       name:
         type: string
  ProductMutableParts:
    properties:
      price:
        type: string
  ProductImmutableParts:
    properties:
      price:
        type: number
  ProductMutable:
    allOf:
    - ProductCommon
    - ProductMutableParts

Or a bit simpler:

schemas:
  ProductCommon:
    properties:
       name:
         type: string
  ProductMutable:
    allOf:
    - ProductCommon
    properties:
      price:
        type: number
  ProductMutable:
    allOf:
    - ProductCommon
    properties:
      price:
        type: string
Read more comments on GitHub >

github_iconTop Results From Across the Web

Lodash - difference between .extend() / .assign() and .merge()
So essentially we merge object hierarchy from source to destination. While for extend / assign , it's simple one level copy of properties...
Read more >
Object - JavaScript - MDN Web Docs
Chrome Edge Object Full support. Chrome1. Toggle history Full support. Edge12. Toggle hi... Object() constructor Full support. Chrome1. Toggle history Full support. Edge12. Toggle hi... assign...
Read more >
Proxy and Reflect - The Modern JavaScript Tutorial
A Proxy object wraps another object and intercepts operations, like reading/writing properties and others, optionally handling them on its ...
Read more >
Methods for deep cloning objects in JavaScript - LogRocket Blog
There are several ways to shallow clone objects in JavaScript, but deep cloning objects is trickier. We highlight several methods to do so....
Read more >
The 10 Most Common JavaScript Issues Developers Face
If you guess that the console.log() call would either output undefined or throw ... and any object will be coerced to a boolean...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found