sanic-org / sanic-ext

Extended Sanic functionality

Home Page:https://sanic.dev/en/plugins/sanic-ext/getting-started.html

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

OpenAPI does not include properties to an embedded object

alg opened this issue · comments

Describe the bug
It was working nicely in sanic_openapi, but now as we attempt moving to sanic-ext it does not.

When a schema embeds another in a field as an object, it's not embedded. Instead it embeds an object without properties.

image

When embedded as an array of objects it works as expected.

image

Screenshots

list_categories:

image

get_category:

image

To Reproduce

class Category:
    id = doc.Integer(required=True, example="1")
    name = doc.String(required=True, example="Full-time")
    instruction = doc.String(example="Category instruction template")

class CategoryResponse:
    data = Category


class CategoriesResponse:
    data = [Category]


@app.get("/api/categories")
@doc.response(200, {"application/json": CategoriesResponse}, "Success")
async def list_categories(_request):
    ...

@app.get("/api/categories/<category_id:int>")
@doc.response(200, {"application/json": CategoryResponse}, "Success")
async def get_category(_request, category_id: int):
    ...

Expected behavior

Embedded schema appears as appropriate.

Environment (please complete the following information):

  • OS: Mac OS X
  • Browser Firefox
  • Version 108b

Additional context

Sanic version: 22.9.1
Python 3.10.8
sanic-routing==22.8.0, sanic-testing==22.9.0, sanic-ext==22.9.1

You can achieve what you want with this I believe:

class CategoryResponse:
    data: Category

The problem is that we are trying to avoid nested class definitions like may be used in pydantic:

class Foo:
    class Config:
        ...

Thanks! I was just looking through the tests to figure what are my options. :)

There is a PR #161 that solves the narrow case, but I am not sure it is the right solution.

At conflict are these two items:

class Foo:
    class Ignore:
        ...

class Bar:
    foo = Foo

We had been disallowing classes as properties for Pydantic support because of the nested Config class. But, it would not cover this use case:

class Ignore:
    ...

class Foo:
    ignore = Ignore

This seems like it would otherwise be legit pattern for Pydantic users:

class MyConfig:
    extra = "allow"

class Foo(BaseModel):
    bar: int

    Config = MyConfig

I am wondering if perhaps we need an opt-in. Something like this:

```python
@openapi.include
class Foo:
    class Ignore:
        ...

class Bar:
    foo = Foo

@alg How do you feel about the opt-in pattern?

@ahopkins I'm not too deep into the way sanic-ext things are done yet, so I'm not best suggester. I am actually fine with the version you pointed first. The version we used (Data = Category) worked in sanic_openapi and that's why I was asking. As long as we know how to transition, it's all good.

There are some competing goals. (1) I absolutely want there to be an easy migration pattern (meaning backwards compat), and (2) easy to implement (meaning compat with other tools and not forcing developers into a narrow pattern).

data: Category is certainly the way to go as it will certainly work. sanic-openapi was very opinionated and wanted you to use it's specific fields. This is not ideal and I really would like to get away from that.

I think that PR might sit on hold until next year so that we can think this one over. I want to leave this issue open because it does touch on something that needs solving.

I am using pydantic models in my sanic application. Ability to use nested Pydantic models within OpenAPI so it can include properties of an embedded object will be awesome.
My Pydantic model looks something like this-

class Foo(BaseModel):
        data: Bar  #where Bar is in turn a user defined pydantic model.

OpenAPI shows Bar just as object and do not include it's properties.
I don't want to use sanic_openapi because of the same reason @ahopkins mentioned that it is highly opinionated and I probably won't be able to use pydantic with it.

Just putting this comment here to emphasize on need for this enhancement.

@iamvishalkhare That should work. You should be able to use Pydantic no problem. The issue here is using the old school sanic-openapi property types that had its own modelling system for creating definitions.

If you have a more complete example of something that is not working, LMK.

@ahopkins - As I wrote last comment, I realised what I was doing wrong. Also, I was driven by this discussion to believe this is not possible. You are right. This is no problem. I got this working. Thank you.

I got this working.

@iamvishalkhare Possibly of interest to you might be a new feature in 22.12.

@openapi.definition(
    body={"application/json": openapi.Component(SomePydanticModel)}
)

That will automatically build out the schema using Pydantic's schema method and inject them into the OAS components to make your doc more DRY.

openapi.Component(SomePydanticModel) is the solution i searched for, but it's not in the docs. You might want to add this to the docs :D