Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OAS Dialect does not require the OAS vocabulary #3901

Closed
jam01 opened this issue Jun 13, 2024 · 10 comments
Closed

OAS Dialect does not require the OAS vocabulary #3901

jam01 opened this issue Jun 13, 2024 · 10 comments
Labels
clarification requests to clarify, but not change, part of the spec discriminator
Milestone

Comments

@jam01
Copy link

jam01 commented Jun 13, 2024

Schema Object#Properties states that

The OpenAPI Schema Object dialect is defined as requiring the OAS base vocabulary ...

yet the dialect schema does not require that vocab:

https://github.com/OAI/OpenAPI-Specification/blob/main/schemas/v3.1/dialect/base.schema.json#L16C57-L16C62

@handrews
Copy link
Member

The false for the vocab in $vocabulary means that the vocabulary does not directly impact validation (discriminator can optimize validation performance, but does not change the outcome). So an implementation that does not understand that vocabulary can safely process the schema anyway, unlike if it doesn't understand a vocabulary that is required with true.

So the dialect schema is correct, it's just that the meaning of true and false has to do with validation rather than overall usage.

@karenetheridge
Copy link
Member

karenetheridge commented Jun 13, 2024

The base dialect does actually "include" that vocabulary: it's at https://github.com/OAI/OpenAPI-Specification/blob/main/schemas/v3.1/dialect/base.schema.json#L23 (this is where the definitions of xml, discriminator etc are pulled in). This will ensure that if someone does use those keywords in their schema openapi document, that their use is syntactically correct; whether or not an implementation actually understands and processes that vocabulary is left as optional, as Henry described above with the use of false in the vocabulary declaration.

@jam01
Copy link
Author

jam01 commented Jun 13, 2024

Thank you both for your responses.

However my understanding is that discriminator is only a performance hint when used in conjunction with all/any/oneOf. But given the example from the spec:

Scenario 1

components:
  schemas:
    Pet:
      type: object
      required:
      - petType
      properties:
        petType:
          type: string
      discriminator:
        propertyName: petType
        mapping:
          dog: Dog
    Cat:
      allOf:
      - $ref: '#/components/schemas/Pet'
      - type: object
        # all other properties specific to a `Cat`
    Dog:
      allOf:
      - $ref: '#/components/schemas/Pet'
      - type: object
        # all other properties specific to a `Dog`

a plain 2020-12 validator validating against Pet wouldn't know to apply any of the Cat or Dog as expected, unless it understands discriminator. So the outcome is changed.

Or am I misunderstanding how discriminator works? 🙃

@jam01
Copy link
Author

jam01 commented Jun 13, 2024

And actually, even in the case of all/any/oneOf the result might be different, no?

Scenario 2

MyResponseType:
  allOf:
  - $ref: '#/components/schemas/Cat'
  - $ref: '#/components/schemas/Dog'
  discriminator:
    propertyName: petType
{ "petType": "Fish" }

allOf could reference some schemas Cat and Dog but the discriminator value could be Fish. This scenario would require that allOf[Cat, Dog] && Fish are validated, meaning a different outcome than a plain 2020-12 validator.

Currently the specification only mandates listing all possible schemas explicitly for oneOf and anyOf.

Scenario 3

MyResponseType:
  oneOf:
  - $ref: '#/components/schemas/Fish'
  - $ref: '#/components/schemas/Dog' # {"properties": { "bark": true }}
  discriminator:
    propertyName: petType
{ "petType": "Fish", "bark": "woof" }

In oneOf and anyOf where the references must be exhaustive, the payload may still validate against, say, Dog but the discriminator value may be Fish. This scenario would require that oneOf[Fish, Dog] && Fish are validated, meaning a different outcome than a plain 2020-12 validator which may validate against Dog only.

@handrews
Copy link
Member

discriminator has always been.. difficult. The following issue records that the TSC decided that discriminator cannot change the validation outcome:

I looked up the TSC agenda issue for that week but it does not elaborate (we've since gotten better about explaining decisions, but unless it's still possible to dig up old recordings, there's no way to look back at that one- I wasn't around at the time so I don't recall the details).

Of course, as with many things in JSON Schema, it's possible to write things that don't work the way you'd want the to.

This scenario would require that oneOf[Fish, Dog] && Fish are validated, meaning a different outcome than a plain 2020-12 validator which may validate against Dog only.

The correct behavior in this case is the JSON Schema behavior. discriminator cannot contradict oneOf or anyOf. It can't optimize oneOf because you have to check every branch before you can determine the outcome. It can only optimize anyOf if you're not using annotations and aren't using any keywords (like unevaluatedProperties) that depend on annotations.

This has been clarified for the forthcoming 3.1.1 and 3.0.4 releases- see PR #2618 and #3846.

Or am I misunderstanding how discriminator works? 🙃

The allOf use case of discriminator is weird, and maybe there's one more clarification to slip into the patch releases here.

For validation purposes, if you validate using the Pet schema, then you just validate against Pet, and not against any other schema. In this usage, discriminator only really helps with things like code generation and data de-serialization. It doesn't help with validation.

I'll re-open while deciding if there's anything more to do to improve the wording.

@handrews handrews reopened this Jun 13, 2024
@handrews handrews added discriminator clarification requests to clarify, but not change, part of the spec and removed schema-object question labels Jun 13, 2024
@handrews handrews added this to the v3.0.4 milestone Jun 13, 2024
@jam01
Copy link
Author

jam01 commented Jun 13, 2024

Thanks @handrews . I gotta say json-schema and OAS set the standard for community driven projects on decision making and documenting mechanisms.

I started digging into the links you shared (and their own links) which might take a minute. But I'm clear now on the expected functionality.

My question arises from a new json-schema implementation in Scala that I'm soon completing, and wanted to look into OAS support. Given the nature of my implementation and now understanding that discriminator does not affect validation, I'll abandon its support.

I originally understood discriminator to be like what dependentSchemas is except at run-time vs schema design-time. The run-time behavior still feels valuable though... 🤔

@handrews
Copy link
Member

@jam01 thanks! If you look through that issue I linked to, I think it includes info on the proposed propertyDependencies keyword from JSON Schema. Which I think is still in their plan - I'm focused on OpenAPI these days so you'd have to check their repo for the status.

I'm pretty sure that part of the reason why "no impact on validation" was the decision is that implementing schema location and invocation in that allOf usage is completely against how anything else in JSON Schema works. All connections between schemas in JSON Schema are by URI, never by implicit names or special location patterns. So that was deemed OAS-only behavior, and therefore outside of validation.

@handrews
Copy link
Member

@jam01 take a look at PR #3907 and see if that helps more! Your timing is good- we're almost done adding clarifications to the patch releases.

@jam01
Copy link
Author

jam01 commented Jun 13, 2024

Ah! I see propertyDependencies and it goes one step further than dependentSchemas but it's still bounded at schema design time. What I thought discriminator intended to accomplish (when I first came upon it a while back) was polymorphism bounded at run-time. Being completely separate from other keywords.

So for example consider

FHIRBundle:
  allOf: [{ $ref: '#/hasBundleId' }, ...other bundle reqs ]
  properties:
    resources:
      items:
        allOf: [{ required: ['resourceType']}, { $ref: '#/hasBundleChildId' }, ...other bundle child reqs ]
        discriminator: 
          propertyName: resourceType
ResourceA: {}
ResourceB: {}

This way we can apply requirements that only apply to children of a Bundle but don't have to change the definition of FHIRBundle any time we introduce a new Resource (as would with depSchemas or propertyDeps)

Anyways, I'm aware this is unrelated to the OAS Vocab question. Thanks again.

EDIT: fixed example a bit

@handrews
Copy link
Member

@jam01 I'm not entirely sure I follow the runtime thing, which is possibly complicated by what seems like two distinct hierarchies (one at the top object level, and the other within items within the resources property), which is a bit like something @hudlow asked me about recently.

I'm going to close this because the PR for the original issue has been merged, but if you want to delve into that more, please feel free to open a discussion for it (because it's an open-ended topic rather than something that will necessarily be addressed by a PR).

@handrews handrews modified the milestones: v3.0.4, v3.1.1 Jun 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clarification requests to clarify, but not change, part of the spec discriminator
Projects
None yet
Development

No branches or pull requests

3 participants