Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG]API schema is not updated for enums and objects nested inside the dependency (only request body schema) #208

Open
vanitha-basavanna opened this issue Feb 28, 2022 · 7 comments
Labels
bug Something isn't working

Comments

@vanitha-basavanna
Copy link

Describe the bug
If a enum is defined in a dependency model and the Field pydantic model type is used the Schema generated is not updated

To Reproduce
The code snippet to generate the behavior
from enum import Enum
from random import random

from flask import Flask, abort, jsonify, request
from flask.views import MethodView
from pydantic import BaseModel, Field

from spectree import Response, SpecTree

app = Flask(name)
api = SpecTree("flask", app=app)

class Query(BaseModel):
text: str = "default query strings"

class Resp(BaseModel):
label: int
score: float = Field(
...,
gt=0,
lt=1,
)

class Language(str, Enum):
en = "en-US"
zh = "zh-CN"

class SubData(BaseModel):
uid: str
lang: Language = Field(Language.en, description="the supported languages")

class Data(BaseModel):
uid: str
limit: int = 5
vip: bool
data: SubData = Field(description="created a test dependency")

class Header(BaseModel):
Lang: Language = Field(Language.en, description="the supported languages")

class Cookie(BaseModel):
key: str

@app.route(
"/api/predict/<string(length=2):source>/<string(length=2):target>", methods=["POST"]
)
@api.validate(
query=Query, json=Data, resp=Response("HTTP_403", HTTP_200=Resp), tags=["model"]
)
def predict(source, target):
"""
predict demo
demo for query, data, resp, x
query with
http POST ':8000/api/predict/zh/en?text=hello' uid=xxx limit=5 vip=false
"""
print(f"=> from {source} to {target}") # path
print(f"JSON: {request.context.json}") # Data
print(f"Query: {request.context.query}") # Query
if random() < 0.5:
abort(403)

return jsonify(label=int(10 * random()), score=random())

@app.route("/api/header", methods=["POST"])
@api.validate(
headers=Header, cookies=Cookie, resp=Response("HTTP_203"), tags=["test", "demo"]
)
def with_code_header():
"""
demo for JSON with status code and header
query with http POST :8000/api/header Lang:zh-CN Cookie:key=hello
"""
return jsonify(language=request.context.headers.Lang), 203, {"X": 233}

Expected behavior
The schema should be proper in the API doc, since the schema section behaves fine the same is expected in default sec schema.

Error Message
image

Desktop (please complete the following information):

  • OS: [e.g. Linux]
  • Version [e.g. Ubuntu-18.04]

Python Information (please complete the following information):

  • Python Version [e.g. Python=3.10]
  • Library Version [e.g. spectree=0.7.6]
  • Other dependencies [e.g. flask=2.0.0]

Additional context
Add any other context about the problem here.

@kemingy
Copy link
Member

kemingy commented Mar 1, 2022

I found that in the Schemas it has all the information. I suspect it's a frontend-related bug. Let me try to figure it out.

image

Here is the formatted code:

from enum import Enum
from random import random

from flask import Flask, abort, jsonify, request
from pydantic import BaseModel, Field

from spectree import Response, SpecTree

app = Flask(__name__)
api = SpecTree("flask", app=app)


class Query(BaseModel):
    text: str = "default query strings"


class Resp(BaseModel):
    label: int
    score: float = Field(
        ...,
        gt=0,
        lt=1,
    )


class Language(str, Enum):
    en = "en-US"
    zh = "zh-CN"


class SubData(BaseModel):
    uid: str
    lang: Language = Field(Language.en, description="the supported languages")


class Data(BaseModel):
    uid: str
    limit: int = 5
    vip: bool
    data: SubData = Field(description="created a test dependency")


class Header(BaseModel):
    Lang: Language = Field(Language.en, description="the supported languages")


class Cookie(BaseModel):
    key: str


@app.route(
    "/api/predict/<string(length=2):source>/<string(length=2):target>", methods=["POST"]
)
@api.validate(
    query=Query, json=Data, resp=Response("HTTP_403", HTTP_200=Resp), tags=["model"]
)
def predict(source, target):
    """
    predict demo
    demo for query, data, resp, x
    query with
    http POST ':8000/api/predict/zh/en?text=hello' uid=xxx limit=5 vip=false
    """
    print(f"=> from {source} to {target}")  # path
    print(f"JSON: {request.context.json}")  # Data
    print(f"Query: {request.context.query}")  # Query
    if random() < 0.5:
        abort(403)

    return jsonify(label=int(10 * random()), score=random())


@app.route("/api/header", methods=["POST"])
@api.validate(
    headers=Header, cookies=Cookie, resp=Response("HTTP_203"), tags=["test", "demo"]
)
def with_code_header():
    """
    demo for JSON with status code and header
    query with http POST :8000/api/header Lang:zh-CN Cookie:key=hello
    """
    return jsonify(language=request.context.headers.Lang), 203, {"X": 233}


if __name__ == "__main__":
    app.run(port=8000)

@kemingy kemingy added the bug Something isn't working label Mar 1, 2022
@vanitha-basavanna
Copy link
Author

Hi @kemingy Yes. In Schema it is always updated but not in the default API section of schema. So, it was hard figure it out but if we remove Field then the schema seems to be fine.

@vanitha-basavanna
Copy link
Author

Hi @kemingy any update on the issue?

@kemingy
Copy link
Member

kemingy commented Mar 10, 2022

Hi @874890089, I checked the generated OpenAPI JSON file, it's correct.

Click to expand the code example:

{
    "components": {
        "schemas": {
            "3a64a9f.Cookie": {
                "properties": {
                    "key": {
                        "title": "Key",
                        "type": "string"
                    }
                },
                "required": [
                    "key"
                ],
                "title": "Cookie",
                "type": "object"
            },
            "3a64a9f.Data": {
                "properties": {
                    "data": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/3a64a9f.Data.SubData"
                            }
                        ],
                        "description": "created a test dependency",
                        "title": "Data"
                    },
                    "limit": {
                        "default": 5,
                        "title": "Limit",
                        "type": "integer"
                    },
                    "uid": {
                        "title": "Uid",
                        "type": "string"
                    },
                    "vip": {
                        "title": "Vip",
                        "type": "boolean"
                    }
                },
                "required": [
                    "uid",
                    "vip",
                    "data"
                ],
                "title": "Data",
                "type": "object"
            },
            "3a64a9f.Data.Language": {
                "description": "An enumeration.",
                "enum": [
                    "en-US",
                    "zh-CN"
                ],
                "title": "Language",
                "type": "string"
            },
            "3a64a9f.Data.SubData": {
                "properties": {
                    "lang": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/3a64a9f.Data.Language"
                            }
                        ],
                        "default": "en-US",
                        "description": "the supported languages"
                    },
                    "uid": {
                        "title": "Uid",
                        "type": "string"
                    }
                },
                "required": [
                    "uid"
                ],
                "title": "SubData",
                "type": "object"
            },
            "3a64a9f.Header": {
                "properties": {
                    "Lang": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/3a64a9f.Header.Language"
                            }
                        ],
                        "default": "en-US",
                        "description": "the supported languages"
                    }
                },
                "title": "Header",
                "type": "object"
            },
            "3a64a9f.Header.Language": {
                "description": "An enumeration.",
                "enum": [
                    "en-US",
                    "zh-CN"
                ],
                "title": "Language",
                "type": "string"
            },
            "3a64a9f.Query": {
                "properties": {
                    "text": {
                        "default": "default query strings",
                        "title": "Text",
                        "type": "string"
                    }
                },
                "title": "Query",
                "type": "object"
            },
            "3a64a9f.Resp": {
                "properties": {
                    "label": {
                        "title": "Label",
                        "type": "integer"
                    },
                    "score": {
                        "exclusiveMaximum": 1,
                        "exclusiveMinimum": 0,
                        "title": "Score",
                        "type": "number"
                    }
                },
                "required": [
                    "label",
                    "score"
                ],
                "title": "Resp",
                "type": "object"
            },
            "6a07bef.ValidationError": {
                "description": "Model of a validation error response.",
                "items": {
                    "$ref": "#/components/schemas/6a07bef.ValidationError.ValidationErrorElement"
                },
                "title": "ValidationError",
                "type": "array"
            },
            "6a07bef.ValidationError.ValidationErrorElement": {
                "description": "Model of a validation error response element.",
                "properties": {
                    "ctx": {
                        "title": "Error context",
                        "type": "object"
                    },
                    "loc": {
                        "items": {
                            "type": "string"
                        },
                        "title": "Missing field name",
                        "type": "array"
                    },
                    "msg": {
                        "title": "Error message",
                        "type": "string"
                    },
                    "type": {
                        "title": "Error type",
                        "type": "string"
                    }
                },
                "required": [
                    "loc",
                    "msg",
                    "type"
                ],
                "title": "ValidationErrorElement",
                "type": "object"
            }
        }
    },
    "info": {
        "title": "Service API Document",
        "version": "0.1.0"
    },
    "openapi": "3.0.3",
    "paths": {
        "/api/header": {
            "post": {
                "description": "",
                "operationId": "post_/api/header",
                "parameters": [
                    {
                        "description": "the supported languages",
                        "in": "header",
                        "name": "Lang",
                        "required": false,
                        "schema": {
                            "allOf": [
                                {
                                    "$ref": "#/components/schemas/3a64a9f.Header.Language"
                                }
                            ],
                            "default": "en-US",
                            "description": "the supported languages"
                        }
                    },
                    {
                        "description": "",
                        "in": "cookie",
                        "name": "key",
                        "required": true,
                        "schema": {
                            "title": "Key",
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "203": {
                        "description": "Non-Authoritative Information"
                    },
                    "422": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/6a07bef.ValidationError"
                                }
                            }
                        },
                        "description": "Unprocessable Entity"
                    }
                },
                "summary": "demo for JSON with status code and header query with http POST :8000/api/header Lang:zh-CN Cookie:key=hello",
                "tags": [
                    "test",
                    "demo"
                ]
            }
        },
        "/api/predict/{source}/{target}": {
            "post": {
                "description": "",
                "operationId": "post_/api/predict/{source}/{target}",
                "parameters": [
                    {
                        "description": "",
                        "in": "path",
                        "name": "source",
                        "required": true,
                        "schema": {
                            "length": 2,
                            "type": "string"
                        }
                    },
                    {
                        "description": "",
                        "in": "path",
                        "name": "target",
                        "required": true,
                        "schema": {
                            "length": 2,
                            "type": "string"
                        }
                    },
                    {
                        "description": "",
                        "in": "query",
                        "name": "text",
                        "required": false,
                        "schema": {
                            "default": "default query strings",
                            "title": "Text",
                            "type": "string"
                        }
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/3a64a9f.Data"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/3a64a9f.Resp"
                                }
                            }
                        },
                        "description": "OK"
                    },
                    "403": {
                        "description": "Forbidden"
                    },
                    "422": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/6a07bef.ValidationError"
                                }
                            }
                        },
                        "description": "Unprocessable Entity"
                    }
                },
                "summary": "predict demo demo for query, data, resp, x query with http POST ':8000/api/predict/zh/en?text=hello' uid=xxx limit=5 vip=false",
                "tags": [
                    "model"
                ]
            }
        }
    },
    "security": [],
    "tags": [
        {
            "name": "test"
        },
        {
            "name": "demo"
        },
        {
            "name": "model"
        }
    ]
}

You can see that they all link to the same definition in "components"/"schemas".

I found a related issue tiangolo/fastapi#329. It has been fixed. But I'm not sure why it still exists.

@vanitha-basavanna
Copy link
Author

HI @kemingy it is true that the json schema is proper but the issue here is with swagger UI. If you cross check I have defined enum with type properly but the schema updated down in the schema section is not reflected with API model schema. So, I don't see any issues with the way we are integrating. could you please cross from UI side how is it been rendering?

@kemingy
Copy link
Member

kemingy commented Mar 15, 2022

Hi @874890089, I don't know about React.

I try to search in the https://github.com/swagger-api/swagger-ui repo and found that: https://github.com/swagger-api/swagger-ui/blob/a920357c227f723ffb884f54bde419e57a8e987a/src/core/components/object-model.jsx#L85 which should be related to this issue. I may need to learn some basics about React to debug.

@alexted
Copy link

alexted commented May 1, 2022

Hi @874890089
please fix the formatting in your first post - it is completely unreadable. nothing is understandable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants