Incorrect validation of readOnly and required property

Issue #391 new
Andreas Petersen created an issue

If the property is marked as readOnly being true and is in the required list, the required will take effect on the response only.

https://swagger.io/specification/

swagger-request-validator-core version 2.28.2 does not honor this though. Consider the following:

Validator configuration:

String openApiUrl = URI.create(...).toString();
ParseOptions parseOptions = new ParseOptions();
parseOptions.setResolve(true);
parseOptions.setResolveFully(false);
parseOptions.setResolveCombinators(true);

OpenApiInteractionValidator validator = OpenApiInteractionValidator
        .createForSpecificationUrl(openApiUrl)
        .withParseOptions(parseOptions)
        .build();

OpenAPI:

---
openapi: 3.0.3
info:
  title: readonly-not-validating
  version: v1
paths:
  /:
    post:
      summary: Create a new item.
      operationId: postItem
      requestBody:
        description: Create new item.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Item'
        required: true
      responses:
        "201":
          description: Item created.
components:
  schemas:
    Item:
      required:
        - id
        - description
      type: object
      properties:
        id:
          description: An ID.
          type: string
          readOnly: true
        description:
          description: Some description.
          type: string

Request body:

{
    "description": "required description"
}

Then the following validation error will be provided:

{
  "key" : "validation.request.body.schema.discriminator",
  "level" : "ERROR",
  "message" : "Failed validation of discriminator schema '/components/schemas/Item'",
  "nestedMessages" : [ {
    "key" : "validation.request.body.schema.required",
    "level" : "ERROR",
    "message" : "Object has missing required properties ([\"description\"])",
    "context" : {
      "pointers" : {
        "instance" : "/",
        "schema" : "/components/schemas/Item"
      }
    }
  } ],
  "context" : {
    "requestPath" : "/",
    "apiRequestContentType" : "application/json",
    "location" : "REQUEST",
    "pointers" : {
      "instance" : "/",
      "schema" : "/components/schemas/Item"
    },
    "requestMethod" : "POST"
  }
}

when no validation error should be given.

Comments (2)

  1. James Navin

    Thanks for raising this. Can I just confirm that the error you are posting comes from the schema + example you’ve given?

    The reason I ask - it is reporting an error on the description field (not the id field that you’ve marked as readOnly). And it is reporting it as part of discriminator validation, which I would only expect if you were using allOf or similar with the discriminator keyword (which I can’t see in your examples).

    So, if these examples are accurate then it looks like the problem is not with the readOnly validation but something else instead.

  2. Andreas Petersen reporter

    Hi,

    Sorry, the example I gave above actually works and gives no validation errors as expected. I got the error on a more complex example, but tried to simplify it, without testing it first. Again sorry for that.

    Here is an example that actually does give an error:

    Validator configuration:

    String openApiUrl = URI.create(...).toString();
    
    ParseOptions parseOptions = new ParseOptions();
    parseOptions.setResolve(true);
    parseOptions.setResolveFully(false);
    parseOptions.setResolveCombinators(true);
    
    LevelResolver levelResolver = LevelResolver.create()
            .withLevel("validation.schema.additionalProperties", IGNORE) // Necessary as the properties in Item would otherwise give an error for properties not defined. But it makes no difference in the error message regarding readOnly required.
            .build();
    
    OpenApiInteractionValidator validator = OpenApiInteractionValidator
            .createForSpecificationUrl(openApiUrl)
            .withParseOptions(parseOptions)
            .withLevelResolver(levelResolver)
            .build();
    

    OpenAPI:

    ---
    openapi: 3.0.3
    info:
      title: readonly-not-validating
      version: v1
    paths:
      /:
        post:
          summary: Create a new item.
          operationId: ref
          requestBody:
            description: Create new item.
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/Item'
            required: true
          responses:
            "201":
              description: Item created.
    components:
      schemas:
        Ownership:
          required:
            - shares
          type: object
          properties:
            shares:
              description: List of shares
              minItems: 0
              type: array
              items:
                $ref: '#/components/schemas/Share'
              readOnly: true
        Share:
          required:
            - share
          type: object
          properties:
            shareId:
              readOnly: true
              description: Share id.
              type: string
              nullable: true
            share:
              description: A decimal number indicating the size of this share where 1
                is 100%.
              type: number
              example: 0.5
        OwnershipItem:
          required:
            - mainOwner
          type: object
          allOf:
            - $ref: '#/components/schemas/Item'
          properties:
            mainOwner:
              type: string
              readOnly: true
            ownership:
              allOf:
                - $ref: '#/components/schemas/Ownership'
                - description: Identifies how the item is owned.
                  readOnly: true
        Item:
          required:
            - type
            - description
          type: object
          properties:
            id:
              description: An ID.
              type: string
              readOnly: true
            description:
              description: Some description.
              type: string
            type:
              description: Descriminator
              enum:
                - OwnershipItem
              type: string
          discriminator:
            propertyName: type
            mapping:
              OwnershipItem: '#/components/schemas/OwnershipItem'
    

    POST request body:

    {
        "type": "OwnershipItem",
        "description": "required description"
    }
    

    Then the following validation error will be provided:

    {
      "messages" : [ {
        "key" : "validation.request.body.schema.discriminator",
        "level" : "ERROR",
        "message" : "Failed validation of discriminator schema '/components/schemas/OwnershipItem'",
        "nestedMessages" : [ {
          "key" : "validation.request.body.schema.required",
          "level" : "ERROR",
          "message" : "Object has missing required properties ([\"mainOwner\"])",
          "context" : {
            "pointers" : {
              "instance" : "/",
              "schema" : "/components/schemas/OwnershipItem"
            }
          }
        } ],
        "context" : {
          "requestPath" : "/ref",
          "apiRequestContentType" : "application/json",
          "location" : "REQUEST",
          "pointers" : {
            "instance" : "/",
            "schema" : "/components/schemas/OwnershipItem"
          },
          "requestMethod" : "POST"
        }
      } ]
    }
    

  3. Log in to comment