openapi3 icon indicating copy to clipboard operation
openapi3 copied to clipboard

Support for schema anyOf

Open yujinyuz opened this issue 2 years ago • 3 comments

Based on the specs here:

https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/

Given

paths:
  /pets:
    patch:
      requestBody:
        content:
          application/json:
            schema:
              anyOf:
                - $ref: '#/components/schemas/PetByAge'
                - $ref: '#/components/schemas/PetByType'
      responses:
        '200':
          description: Updated
components:
  schemas:
    PetByAge:
      type: object
      properties: 
        age: 
          type: integer
        nickname: 
          type: string
      required:
        - age
          
    PetByType:
      type: object
      properties:
        pet_type:
          type: string
          enum: [Cat, Dog]
        hunts:
          type: boolean
      required: 
        - pet_type

The following JSON response should be valid:

{
  "pet_type": "Cat",
  "hunts": true
}
{
  "nickname": "Fido",
  "pet_type": "Dog",
  "age": 4
}

@Dorthu would this be hard to implement? I'm trying to dive in to the code base. What do I need to do to in order to make this work?

yujinyuz avatar Jun 03 '23 04:06 yujinyuz

Hi,

I already implemented this in https://github.com/Dorthu/openapi3/pull/69 - been quite a lot.

import httpx
import json

import pydantic
import pytest

from aiopenapi3 import OpenAPI

DD = """
paths:
  /pets:
    patch:
      requestBody:
        content:
          application/json:
            schema:
              anyOf:
                - $ref: '#/components/schemas/PetByAge'
                - $ref: '#/components/schemas/PetByType'
      responses:
        '200':
          description: Updated
components:
  schemas:
    PetByAge:
      type: object
      properties: 
        age: 
          type: integer
        nickname: 
          type: string
      required:
        - age

    PetByType:
      type: object
      properties:
        pet_type:
          type: string
          enum: [Cat, Dog]
        hunts:
          type: boolean
      required: 
        - pet_type

openapi: "3.1.0"
info:
  version: 1.0.0
  title: https://github.com/Dorthu/openapi3/issues/101
servers:
    - url: /       
"""


def test_anyOf(httpx_mock):
    api = OpenAPI.loads("http://localhost", DD, httpx.Client)

    # create a Request so we can use the Requests "data" property to access the body schema
    m = api.createRequest(("/pets", "patch"))
    data = json.loads("""{
      "pet_type": "Cat",
      "hunts": true
    }""")
    # use the body schema to create a pydantic model to validate the data
    data = m.data.get_type().model_validate(data)

    # same
    data = json.loads("""
    {
      "nickname": "Fido",
      "pet_type": "Dog",
      "age": 4
    }""")
    data = m.data.get_type().model_validate(data)

    # fail due to missing property
    with pytest.raises(pydantic.ValidationError):
        # missing age
        data = json.loads("""
        {
          "nickname": "Frogo",
          "pet_type": "Frog"
        }""")
        data = m.data.get_type().model_validate(data)

    data = json.loads("""
    {
      "nickname": "Frogo",
      "pet_type": "Frog",
      "age": 4,
      "wet": true
    }""")
    # works as wet is an additionalProperty
    data = m.data.get_type().model_validate(data)
    # access model via root - due to use of anyOf
    assert data.root.model_extra["wet"] is True

commonism avatar Aug 13 '23 06:08 commonism