OAI / OpenAPI-Specification

The OpenAPI Specification Repository
https://openapis.org
Apache License 2.0
28.64k stars 9.06k forks source link

Discriminator: how properly detect valid reference #2520

Closed Logioniz closed 1 month ago

Logioniz commented 3 years ago

Suppose we have such OpenAPI document which served by location http://example.com/openapi/desc

servers:
    - url: 'http://example.com/'
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`
            properties:
              name:
                type: string
        Dog:
          allOf:
          - $ref: '#/components/schemas/Pet'
          - type: object
            # all other properties specific to a `Dog`
            properties:
              bark:
                type: string
        Lizard:
          allOf:
          - $ref: '#/components/schemas/Pet'
          - type: object
            # all other properties specific to a `Lizard`
            properties:
              lovesRocks:
                type: boolean

it is not clear what mean Dog in the mapping subscheme. Variants:

  1. http://example.com/openapi/Dog (accroding to base uri http://example.com/openapi/desc)
  2. /components/schemas/Dog

If i have subschema in another url (http://example.com/openapi/Dog) thеn Dog is relative link. If i have subschema in components thеn Dog is #/components/schemas/Dog (which is also a relative link, but different)

How to correctly determine that Dog is a relative link or subschema in #/components/schema?

Sorry for bad english.

Logioniz commented 3 years ago

Any recommendations? Not sure how to properly handle this kind of situation.

jdesrosiers commented 3 years ago

(Note: I deleted my previous wrong response)

I believe is example has a bug. The values of mapping are URIs that must match the URI of one of the schemas being discriminated. I don't think OpenAPI has a concept of an identifier the way JSON Schema does. In that case, Dog doesn't have anything to resolve against and that is an invalid reference. If OpenAPI does have a concept of an identifier, then it would resolve to http://example.com/openapi/Dog, which doesn't exist. Constructing #/components/schemas/Dog should only happen when auto mapping, not with mapping.

The discriminator in the example should be,

discriminator:
  propertyName: petType
  mapping:
    dog: '#/components/schemas/Dog'
Logioniz commented 3 years ago

I believe is example has a bug.

This example is taken from the specification (see the example in the end of discriminator object), i.e. I have not invented it. I understand correctly that you want to say that this is a bug in the documentation description (or bug in example in documentation)?

If the construction of the #/components/schemas/Dog will be done only with auto-mapping, then in my opinion this will solve the problem too. In such case mapping MUST have ONLY reference, so my example MUST resolve it via http://example.com/openapi/Dog (based on base uri http://example.com/openapi/desc).

At the moment I'm trying to figure out, should I handle this situation like this, or is it under discussion?

jdesrosiers commented 3 years ago

I didn't think you made it up. I recognized it as the example is from the documentation. Yes, I believe the example from the documentation has a bug.

I don't think that OpenAPI has a concept of a base URI, so I don't think you can resolve Dog against http://example.com/openapi/desc. I think Dog is just an invalid reference. It needs to be an absolute URI. I could be wrong about that part.

Logioniz commented 3 years ago

@jdesrosiers thanks for your reply.

I don't think that OpenAPI has a concept of a base URI

Hmm... Maybe i misunderstood the documentation. Pay attention to part of the specification Relative References in URLs. For me, this part is not very clear and requires clarification. From specification:

Relative references are resolved using the URLs defined in the Server Object as a Base URI.

I think what is meant here is about url in the subscheme paths (i do not know of any other examples of what this might mean, for me it is not clear). There is a special note for $ref:

Relative references used in $ref are processed as per JSON Reference, using the URL of the current document as the base URI

Thus, if OpenAPI is served by url http://example.com/openapi/desc, then it is the base url for $ref. It means that you may to split your OpenAPI description for several files like this:

openapi: '3.0.3'
servers:
    - url: 'http://example.com/'
paths:
  /company:
    $ref: 'company.yml#/paths/~1company'

Suppose that this OpenAPI description served by url http://example.com/openapi/main.yml, then

'http://example.com/' is a base URI for /company, so absolute url is 'http://example.com/company'. 'http://example.com/openapi/main.yml' is a base URI for 'company.yml#/paths/~1company', so absolute url is 'http://example.com/openapi/company.yml#/paths/~1company'

jdesrosiers commented 3 years ago

I agree with your conclusion. This is what I missed,

Relative references are resolved using the URLs defined in the Server Object as a Base URI.

That is very clear. So, I agree that in the previous example, Dog would resolve to http://example.com/openapi/Dog.

karenetheridge commented 2 years ago

This appears to have been fixed in the spec now. https://spec.openapis.org/oas/v3.1.0#discriminator-object has the example:

MyResponseType:
  oneOf:
  - $ref: '#/components/schemas/Cat'
  - $ref: '#/components/schemas/Dog'
  - $ref: '#/components/schemas/Lizard'
  - $ref: 'https://gigantic-server.com/schemas/Monster/schema.json'
  discriminator:
    propertyName: petType
    mapping:
      dog: '#/components/schemas/Dog'
      monster: 'https://gigantic-server.com/schemas/Monster/schema.json'

where the dog mapping properly has the uri reference, rather than just a snippet "Dog".

MaceWindu commented 2 years ago

No, it wasn't. Last example still use Dog

BTW, I've come here, because I've stumbled upon real openapi document which use this notation https://github.com/LandRegistry/digital-street-conveyancer-api/blob/master/src/main/resources/openapi.json#L730-L731

and expect it resolved to #/components/schemas/<name> ...

jdesrosiers commented 2 years ago

I was wrong that "schema names" are not allowed in mapping. The spec says mapping is "an object to hold mappings between payload values and schema names or references". (The spec never defines what "schema names" means, but it's assumed that it means the property names used under /components/schemas which is equivalent to #/components/schemas/{schema name}.) Since a schema name is also a valid URI, there definitely seems to be an ambiguity in the spec.

I think the most reasonable thing to do would be to assume it's a schema name if it looks like a schema name and fallback to treating it like a URI if that schema doesn't exist. It would be nice if there was a linting rule to flag the use of schema names in mapping as something to be avoided.

handrews commented 1 month ago

PRs merged! closing.