rest-guide icon indicating copy to clipboard operation
rest-guide copied to clipboard

Test allOf and oneOf guidelines with validator tooling

Open pvdbosch opened this issue 3 months ago • 2 comments

pvdbosch avatar Sep 24 '25 14:09 pvdbosch

When testing one of the examples with oneOf of two object subschemas, I noticed that it's missing required properties to make the subschemas non-overlapping:

    OneOfTwoObjects:
      type: object
      oneOf:
        - type: object
          properties:
            propertyA:
              type: string
          # required: [propertyA]   --> needs to be added, else no value is valid because any object matches both subschemas
        - type: object
          properties:
            propertyB:
              type: string
          # required: [propertyB]   --> needs to be added, else no value is valid because any object matches both subschemas

However, when generating code with the required added, both propertyA and propertyB getters get a @NotNull annotation in the generated class combining all properties.

update: done a test with a variant that uses shared property in subschemas, but with different constraints:

    OneOfTwoObjects:
      type: object
      required: [sharedProperty]
      oneOf:
        - type: object
          properties:
            sharedProperty:
              type: string
              pattern: "$A.*$"
        - type: object
          properties:
            sharedProperty:
              type: string
              pattern: "$B.*$"

Should work for swagger-request-validator, but for codegen with bean validation enabled, this results in only one of the two pattern constraints 'survives':

  @NotNull @Pattern(regexp = "$B.*$") 
  @Schema(name = "sharedProperty", requiredMode = Schema.RequiredMode.REQUIRED)
  @JsonProperty("sharedProperty")
  public String getSharedProperty() {
    return sharedProperty;
  }

So, when oneOf subschemas:

  • are overlapping -> incorrect, values are always invalid
  • are disjoint by required property -> incorrect @NotNull
  • are disjoint by constraints on property -> incorrect bean validation annotations

So lots of pitfalls ... maybe we should recommend to limit usage of oneOf to only required, e.g.

oneOf:
- required: [propertyA]
- required: [propertyB]

pvdbosch avatar Nov 20 '25 08:11 pvdbosch

for the example "Making a property of another schema required", validation of the property being required doesn't seem to work (using swagger-request-validator 2.46.0, couldn't get it to work in v2.44.1 ) unless declaring it as required on root schema level. Then again, declaring it within the allOf, is useful to trigger code generation to create an inheritance relationship.

components:
  schemas:
    ObjectSchema:
      type: object
      properties:
        firstProperty:
          type: string
        secondProperty:
          type: string
    RestrictedSchema:
      type: object
      required: [firstProperty] #  this seems necessary to make validation work
      allOf:
        - $ref: "#/components/schemas/ObjectSchema"
        - required: [firstProperty] # makes property defined in ObjectSchema required
          type: object # object type already in ObjectSchema, but will generate inheritance in code generation

The other oneOf/allOf examples work as intended.

pvdbosch avatar Nov 20 '25 09:11 pvdbosch

REST WG is OK that oneOf with object type subschemas shouldn't be used. We can add the swagger-request-validator buggy behavior in the example, but not in the rule bc it might be fixed in a next release. I'll create a PR.

pvdbosch avatar Nov 21 '25 14:11 pvdbosch

PR created.

restricts allowed usage to:

  • subschemas without a type constraint that only list required properties
  • subschemas with an enum of a simple type (i.e. no array or object)

The enum case works, because of the SIMPLIFY_ONEOF_ANYOF_ENUM normalizer that's active by default.

pvdbosch avatar Nov 25 '25 16:11 pvdbosch