bauerji / flask-pydantic

flask extension for integration with the awesome pydantic package
MIT License
368 stars 58 forks source link

Add function to generate openapi spec. #41

Closed buildpeak closed 6 months ago

buildpeak commented 3 years ago

This is a very simple version of OpenAPI spec just for internal use. At least the developer can use the Swagger page to do some RESTful tests.

bauerji commented 3 years ago

Hi, Thank you for contribution. I am trying to access /docs/ endpoint on both example applications. For example_app.app I am getting empty html response (20 status code). example_app gives me 404 for /docs/. Am I doing something wrong? I thought the example apps should run out of the box. Jirka

sonarcloud[bot] commented 3 years ago

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 1 Code Smell

No Coverage information No Coverage information
0.0% 0.0% Duplication

buildpeak commented 3 years ago

Hi Jirka,

I just figured out the problem and fixed them. Could you try again?

On Tue, Sep 28, 2021 at 8:35 PM bauerji @.***> wrote:

Hi, Thank you for contribution. I am trying to access /docs/ endpoint on both example applications. For example_app.app I am getting empty html response (20 status code). example_app gives me 404 for /docs/. Am I doing something wrong? I thought the example apps should run out of the box. Jirka

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/bauerji/flask_pydantic/pull/41#issuecomment-929167466, or unsubscribe https://github.com/notifications/unsubscribe-auth/AMSIX5R2K2ZHPI6PE7TXT3DUEGY7JANCNFSM5EVJG6PQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

bauerji commented 3 years ago

It seems to work fine now. Except for path parameters. I could not find a way how to specify url path parameters while trying out the API through swagger UI. Would it be a simple fix or should we release it without it?

buildpeak commented 3 years ago

Hi,

Is this what you are looking for?

@app.route("/many/<int:count>", methods=["GET"])
@openapi_docs(exceptions=[APIError(code=403, msg="Access Denied")])
@validate(response_many=True)
def get_many(count: int):
    """
    This route returns response containing many serialized objects.
    """
    return [
        ResponseModel(id=1, age=95, name="Geralt", nickname="White Wolf"),
        ResponseModel(id=2, age=45, name="Triss Merigold", nickname="sorceress"),
        ResponseModel(id=3, age=42, name="Julian Alfred Pankratz", nickname="Jaskier"),
        ResponseModel(id=4, age=101, name="Yennefer", nickname="Yenn"),
    ][:count]

Thanks

On Sun, Oct 3, 2021 at 9:05 PM bauerji @.***> wrote:

It seems to work fine now. Except for path parameters. I could not find a way how to specify url path parameters while trying out the API through swagger UI. Would it be a simple fix or should we release it without it?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/bauerji/flask_pydantic/pull/41#issuecomment-932947439, or unsubscribe https://github.com/notifications/unsubscribe-auth/AMSIX5UZDJR4UAJHHE4NPA3UFBIIDANCNFSM5EVJG6PQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

bauerji commented 3 years ago

The problem I was mentioning was in the swagger UI. You have the option to "Try it out" - call the API from UI. I wasn't able to specify the URL path parameters in the UI.

When I am trying it now, I am only getting the empty page when trying to display docs in the example app. I don't know what I am doing wrong

sonarcloud[bot] commented 2 years ago

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 1 Code Smell

No Coverage information No Coverage information
0.0% 0.0% Duplication

bauerji commented 2 years ago

Hey @buildpeak, I am sorry for such a late response. I've rebased your branch onto current master and I'm getting ImportError: cannot import name 'parse_rule' from 'werkzeug.routing' error. Apparently werkzeug marked the parse_rule method as internal in the latest release.

The-Podsiadly commented 2 years ago

@bauerji @buildpeak I found a workaround. Not the prettiest, but it works apparently according to flask-restx in this PR #463. Let me know if you'd like me to add it this PR.

import re

RE_PARSE_RULE = re.compile(
    r"""
    (?P<static>[^<]*)                           # static rule data
    <
    (?:
        (?P<converter>[a-zA-Z_][a-zA-Z0-9_]*)   # converter name
        (?:\((?P<args>.*?)\))?                  # converter arguments
        \:                                      # variable delimiter
    )?
    (?P<variable>[a-zA-Z_][a-zA-Z0-9_]*)        # variable name
    >
    """,
    re.VERBOSE,
)

def parse_rule(rule):
    """
    Parse a rule and return it as generator. Each iteration yields tuples in the form
    ``(converter, arguments, variable)``. If the converter is `None` it's a static url part, otherwise it's a dynamic
    one.
    Note: This originally lived in werkzeug.routing.parse_rule until it was removed in werkzeug 2.2.0.
    """
    pos = 0
    end = len(rule)
    do_match = RE_PARSE_RULE.match
    used_names = set()
    while pos < end:
        m = do_match(rule, pos)
        if m is None:
            break
        data = m.groupdict()
        if data["static"]:
            yield None, None, data["static"]
        variable = data["variable"]
        converter = data["converter"] or "default"
        if variable in used_names:
            raise ValueError(f"variable name {variable!r} used twice.")
        used_names.add(variable)
        yield converter, data["args"] or None, variable
        pos = m.end()
    if pos < end:
        remaining = rule[pos:]
        if ">" in remaining or "<" in remaining:
            raise ValueError(f"malformed url rule: {rule!r}")
        yield None, None, remaining