Redocly / redocly-cli

⚒️ Redocly CLI makes OpenAPI easy. Lint/validate to any standard, generate beautiful docs, and more.
https://redocly.com/docs/cli/
MIT License
953 stars 148 forks source link

Rule no-invalid-media-type-examples check fails on example with combination of two anyOf objects #1658

Open rsams-tw opened 3 months ago

rsams-tw commented 3 months ago

Describe the bug

Given a request body specification consisting of an anyOf of two objects, it is expected that a merged object consisting of the fields of both objects should be valid. However, the no-invalid-media-type-examples rule fails when given an example with such a merged object.

To Reproduce

This is the full ruleset we use, applied to a simplified version of our specification.

redocly.yaml

extends:
  - recommended
rules:
  no-invalid-media-type-examples:
    severity: error
    allowAdditionalProperties: false
  no-invalid-parameter-examples:
    severity: error
    allowAdditionalProperties: false
  component-name-unique:
    severity: error
    schemas: error
    parameters: on
    responses: error
    requestBodies: error
  rule/get-should-not-define-requestBody:
    severity: warn
    message: '"GET" SHOULD NOT define a "requestBody" schema'
    subject:
      type: Operation
      filterInParentKeys:
        - get
    assertions:
      disallowed:
        - requestBody

spec.yml

openapi: 3.1.0
info:
  title: My API
  version: v1
  contact:
    name: Me
    url: https://www.my-domain.com
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0
servers:
  - url: https://www.my-domain.com/api/v1
paths:
  /request:
    post:
      operationId: send-post-request
      summary: send post request
      requestBody:
        content:
          application/json:
            schema:
              type: object
              anyOf:
                - type: object
                  properties:
                    attrOne:
                      type: string
                - type: object
                  properties:
                    attrTwo:
                      type: string
            examples: 
              Example 1 - attrOne - valid:
                value:
                  attrOne: value1
              Example 2 - attrTwo - valid:
                value:
                  attrTwo: value2
              Example 3 - attrOne and attrTwo - invalid:
                value:
                  attrOne: value1
                  attrTwo: value2
components:
  securitySchemes:
    basic_auth:
      type: http
      scheme: basic
security:
  - basic_auth: []

When running redocly lint spec.yml --config redocly.yaml, the validation fails:

[1] spec.yml:42:19 at #/paths/~1request/post/requestBody/content/application~1json/examples/Example 3 - attrOne and attrTwo - invalid/value/attrTwo

Example value must conform to the schema: must NOT have unevaluated properties `attrTwo`.

40 |                 value:
41 |                   attrOne: value1
42 |                   attrTwo: value2
43 | components:
44 |   securitySchemes:

referenced from spec.yml:21:13 at #/paths/~1request/post/requestBody/content/application~1json 

Error was generated by the no-invalid-media-type-examples rule.

[2] spec.yml:41:19 at #/paths/~1request/post/requestBody/content/application~1json/examples/Example 3 - attrOne and attrTwo - invalid/value/attrOne

Example value must conform to the schema: must NOT have unevaluated properties `attrOne`.

39 |               Example 3 - attrOne and attrTwo - invalid:
40 |                 value:
41 |                   attrOne: value1
42 |                   attrTwo: value2
43 | components:

referenced from spec.yml:21:13 at #/paths/~1request/post/requestBody/content/application~1json 

Error was generated by the no-invalid-media-type-examples rule.

[3] spec.yml:41:19 at #/paths/~1request/post/requestBody/content/application~1json/examples/Example 3 - attrOne and attrTwo - invalid/value

Example value must conform to the schema: must match a schema in anyOf.

39 |               Example 3 - attrOne and attrTwo - invalid:
40 |                 value:
41 |                   attrOne: value1
42 |                   attrTwo: value2
43 | components:
44 |   securitySchemes:

referenced from spec.yml:21:13 at #/paths/~1request/post/requestBody/content/application~1json 

Error was generated by the no-invalid-media-type-examples rule.

Expected behavior

Example 3 (combination of the two anyOf options) should be considered valid by the linter.

Redocly Version(s)

1.19.0

Node.js Version(s)

22.4.0

jeremyfiel commented 3 months ago

This configuration is overriding the schema constraints.

 no-invalid-parameter-examples:
    severity: error
    allowAdditionalProperties: false

set it to true and it will work per the schema. This was a design decision by the tooling to enforce additionalProperties: false by default.

rsams-tw commented 3 months ago

This seems like a workaround, neither property is "additional" in this specification and the intent is to explicitly allow only these properties in any combination.

jeremyfiel commented 3 months ago

This is not a workaround, this is changing the default behavior of a decision made by the tooling vendor to override the JSON Schema default behavior. You need to explicitly set the tooling to follow the JSON Schema rather than their own behavior.

ipletnjov-tw commented 3 months ago

Hey @jeremyfiel Just to make sure I understand: allowAdditionalProperties: false forces Redocly CLI to not use JSON Schema, which (as a side-effect) bypasses the desired anyOf behavior that JSON Schema defines (https://json-schema.org/understanding-json-schema/reference/combining). Setting this property to true forces the Redocly CLI to start strictly following the capabilities defined by JSON Schema (including anyOf).

Is that right?

jeremyfiel commented 3 months ago

@ipletnjov-tw the only behavior modified is the use of additionalProperties but that has an impact on other JSON Schema keywords and affects their behavior too.

To clarify, Redocly does not "turn off" the use of JSON Schema,

tatomyr commented 2 months ago

@jeremyfiel in this case it should be no-invalid-media-type-examples instead of no-invalid-parameter-examples, I believe.

@ipletnjov-tw in your case you can also switch to the default JSON Schema behaviour by explicitly adding additionalProperties: true inside each schema inside anyOf. This will have similar effect as setting allowAdditionalProperties: true but only for that local schema. However, the default JSON Schema behaviour doesn't make too much sense since it will allow literally any object including object with properties attrOne and attrTwo being numbers, booleans etc. because each of those could be any object (e.g., {attrOne: 42} is valid against the second schema in anyOf in your example), so proper validation becomes almost impossible. That's why adding additionalProperties: true explicitly is better as it doesn't impact validation of other schemas.

tatomyr commented 2 months ago

~Not sure why anyOf doesn't work the same way as allOf does though. It should use unevaluatedProperties along the way and should be resolved correctly (see this issue for more context). We have to check this.~

Actually, I think anyOf works a bit differently. From what I understand from the spec, it doesn't allow attrOne and attrTwo simultaneously if you set unevaluatedProperties: false on the top of the object (Redocly CLI does this by defalut). It's similar to oneOf except it doesn't allow overlapping. So the Example 3 is indeed invalid unless I'm missing something.