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

"value property should match exactly one schema in oneOf" message with right example #1482

Closed
GillesTourreau opened this issue Jan 16, 2021 · 2 comments
Assignees
Labels
cs/reported t/bug Something isn't working

Comments

@GillesTourreau
Copy link

I have the following YAML example which I edit on the Stoplight Studio (Windows version)

openapi: 3.0.0
info:
  title: Test
  version: '1.0'
servers:
  - url: 'http://localhost:3000'
paths:
  /pets:
    patch:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/Cat'
                - $ref: '#/components/schemas/Dog'
            examples:
              'Cat example':
                value:    # <--- "`value` property should match exactly one schema in oneOf" error display.
                  hunts: true
                  age: 15
              'Dog example':
                value:    # <--- "`value` property should match exactly one schema in oneOf" error display.
                  bark: true
                  breed: Dingo
      responses:
        '200':
          description: Updated
components:
  schemas:
    Dog:
      type: object
      properties:
        bark:
          type: boolean
        breed:
          type: string
          enum: [Dingo, Husky, Retriever, Shepherd]
    Cat:
      type: object
      properties:
        hunts:
          type: boolean
        age:
          type: integer

Two errors are displayed at the line 19 and 23 which explain that the examples are invalid and does not match the Dog or Cat schemas.
Can you confirm that it is a bug of the Stoplight studio validator ?

@philsturgeon philsturgeon transferred this issue from stoplightio/studio Jan 18, 2021
@philsturgeon
Copy link
Contributor

I've moved this over to the spectral repository, which is the validator powering this functionality in Studio.

@ariatron ariatron added team/void-crew t/bug Something isn't working cs/reported labels May 3, 2021
@P0lip P0lip self-assigned this May 11, 2021
@P0lip
Copy link
Contributor

P0lip commented May 11, 2021

Hey @GillesTourreau!
Thanks a lot for filing the issue.
The behavior described is valid.
Let me explain why.

oneOf compound keyword enforces a single match of the schema from the array.
To illustrate it, let's take the following schema as an example.

{
  "oneOf": [
     { "type": "number" },
     { "type": "integer" }
  ]
}

Now, 2.2 value would be considered valid, because it's a number, but it's not an integer.
However 2 would be treated as invalid, because it's both a number and an integer.

On the other hand, anyOf would be satisfied, since it's a "one or more" kind of scenario.

(using anyOf is one of the way of resolving the issue in the document.)

fixed doc (option 1)
openapi: 3.0.0
info:
  title: Test
  version: '1.0'
servers:
  - url: 'http://localhost:3000'
paths:
  /pets:
    patch:
      requestBody:
        content:
          application/json:
            schema:
              anyOf:
                - $ref: '#/components/schemas/Cat'
                - $ref: '#/components/schemas/Dog'
            examples:
              'Cat example':
                value:    # <--- "`value` property should match exactly one schema in oneOf" error display.
                  hunts: true
                  age: 15
              'Dog example':
                value:    # <--- "`value` property should match exactly one schema in oneOf" error display.
                  bark: true
                  breed: Dingo
      responses:
        '200':
          description: Updated
components:
  schemas:
    Dog:
      type: object
      properties:
        bark:
          type: boolean
        breed:
          type: string
          enum: [Dingo, Husky, Retriever, Shepherd]
    Cat:
      type: object
      properties:
        hunts:
          type: boolean
        age:
          type: integer

Now, one may ask why both schemas are actually matched.
Both of them look slightly different, after all.
The thing here is that they lack required or additionalProperties.
JSON Schema is relatively relaxed, and simplifying things a bit, relies on keywords to perform the validation.

Let's extract one of the schemas.

type: object
properties:
  bark:
    type: boolean
  breed:
    type: string
    enum: [Dingo, Husky, Retriever, Shepherd]

Now, if you provide such a schema to any validator, these are, more or less, the steps it'll perform.

  • verify the type of the data -> must be object

    2 // fails the validation, it's not an object
  • alright, it's an object. Let's take the next keyword properties and iterate over the values in it.

    • if bark is present, verify the type of bark -> must be boolean

       { "bark": 2 } // invalid! it's not a boolean
    • if breed is present, verify the type of breed

      { "breed": 2 } // invalid! it's not a string
      • verify breed against the enum values

        { "breed": "York" } // invalid, incorrect breed!

This means, that as long as all these steps pass, the value is considered value.
If you take a closer look, you can see that if you don't provide a property, it'll pass through just fine.

{
  "foo": {} // this is valid!
}

To change it, you can either leverage additionalProperties and set its value to false, to instrument the validator you don't expect other properties other than the ones you authored under properties.
Alternatively, you can use required to demand the presence of a given property.

fixed doc option 2
openapi: 3.0.0
info:
  title: Test
  version: '1.0'
servers:
  - url: 'http://localhost:3000'
paths:
  /pets:
    patch:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/Cat'
                - $ref: '#/components/schemas/Dog'
            examples:
              'Cat example':
                value:    # <--- "`value` property should match exactly one schema in oneOf" error display.
                  hunts: true
                  age: 15
              'Dog example':
                value:    # <--- "`value` property should match exactly one schema in oneOf" error display.
                  bark: true
                  breed: Dingo
      responses:
        '200':
          description: Updated
components:
  schemas:
    Dog:
      type: object
      properties:
        bark:
          type: boolean
        breed:
          type: string
          enum: [Dingo, Husky, Retriever, Shepherd]
      additionalProperties: false
    Cat:
      type: object
      properties:
        hunts:
          type: boolean
        age:
          type: integer
      additionalProperties: false
fixed doc option 3
openapi: 3.0.0
info:
  title: Test
  version: '1.0'
servers:
  - url: 'http://localhost:3000'
paths:
  /pets:
    patch:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/Cat'
                - $ref: '#/components/schemas/Dog'
            examples:
              'Cat example':
                value:    # <--- "`value` property should match exactly one schema in oneOf" error display.
                  hunts: true
                  age: 15
              'Dog example':
                value:    # <--- "`value` property should match exactly one schema in oneOf" error display.
                  bark: true
                  breed: Dingo
      responses:
        '200':
          description: Updated
components:
  schemas:
    Dog:
      type: object
      properties:
        bark:
          type: boolean
        breed:
          type: string
          enum: [Dingo, Husky, Retriever, Shepherd]
      required: [bark, breed]
    Cat:
      type: object
      properties:
        hunts:
          type: boolean
        age:
          type: integer
      required: [hunts, age]

oneOf can remain in such a case, as one of the values will be invalid.

Does that make sense?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cs/reported t/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants