python-openapi / openapi-core

Openapi-core is a Python library that adds client-side and server-side support for the OpenAPI v3.0 and OpenAPI v3.1 specification.
BSD 3-Clause "New" or "Revised" License
287 stars 131 forks source link

[Bug]: No casting applied if type is object #800

Open jonathanberthias opened 4 months ago

jonathanberthias commented 4 months ago

Actual Behavior

When validating a request, I get a different validation error depending on the structure of the schema.

If I have the following schema:

Pet:
  type: object  # <-- this causes the difference
  properties:
    id:
      type: integer
Pet2:
  allOf:
    - properties:
        id:
          type: integer

and send a request with body {"id": "hello"},

with the first schema I get:

openapi_core.casting.schemas.exceptions.CastError: Failed to cast value to integer type: hello

whereas in the second one I get:

openapi_core.validation.schemas.exceptions.InvalidSchemaValue: Value {'id': 'hello'} not valid for schema of type any: (<ValidationError: "'hello' is not of type 'integer'">,)

Expected Behavior

These schemas are equivalent, and as such I expected to get the same error. Specifically, I expected the validation error in both cases rather than the casting error as I asked for request validation. Also, removing the type: object in the first schema seems to bypass casting entirely and leads to the same validation error as the second schema.

Steps to Reproduce

schema.yaml ```yaml openapi: "3.0.0" info: title: "Sample API" version: "1.0.0" servers: - url: / paths: /pets: post: summary: Create a pet operationId: createPet requestBody: content: application/json: schema: $ref: "#/components/schemas/Pet" responses: '201': description: Created content: application/json: schema: $ref: "#/components/schemas/Pet" /pets2: post: summary: Create a pet operationId: createPet2 requestBody: content: application/json: schema: $ref: "#/components/schemas/Pet2" responses: '201': description: Created content: application/json: schema: $ref: "#/components/schemas/Pet" components: schemas: Pet: type: object # <-- this causes the difference properties: id: type: integer Pet2: allOf: - properties: id: type: integer ```
import json
import traceback
import openapi_core
from openapi_core.contrib.starlette import StarletteOpenAPIRequest
from starlette.requests import Request

def starlette_request(path: str):
    return Request({
        "type": "http",
        "method": "POST",
        "path": path,
        "headers": [(b"content-type", b"application/json")],
        "query_string": "",
    })

body = json.dumps({"id": "hello"})

openapi = openapi_core.OpenAPI.from_file_path("schema.yaml")
try:
    openapi.validate_request(
        StarletteOpenAPIRequest(starlette_request("/pets"), body=body)
    )
except openapi_core.exceptions.OpenAPIError:
    traceback.print_exc()

try:
    openapi.validate_request(
        StarletteOpenAPIRequest(starlette_request("/pets2"), body=body)
    )
except openapi_core.exceptions.OpenAPIError:
    traceback.print_exc()

OpenAPI Core Version

0.19.0

OpenAPI Core Integration

starlette

Affected Area(s)

casting, validation

References

No response

Anything else we need to know?

No response

Would you like to implement a fix?

None