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][PYTHON-FASTAPI] Generator specifies path parameter default values #16029

Closed
5 of 6 tasks
lvijnck opened this issue Jul 7, 2023 · 3 comments · Fixed by #17532
Closed
5 of 6 tasks

[BUG][PYTHON-FASTAPI] Generator specifies path parameter default values #16029

lvijnck opened this issue Jul 7, 2023 · 3 comments · Fixed by #17532

Comments

@lvijnck
Copy link

lvijnck commented Jul 7, 2023

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

The generator adds default values to path parameters, which FastAPI does not like.

openapi-generator version

v7.0.0-beta

OpenAPI declaration file content or url
{
  "openapi": "3.0.0",
  "paths": {
    "/project/{projectId}/status": {
      "post": {
        "operationId": "ProjectController_projectStatus",
        "summary": "Notify of changes in the project",
        "description": "Synchronous endpoint. Platform Api would call this endpoint to notify any changes made to a project.",
        "parameters": [
          {
            "name": "projectId",
            "required": true,
            "in": "path",
            "description": "Globally unique ID of an project across time",
            "example": "1bd98aa5-5a83-4bf3-97a2-a8a7e444c73e",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectStatusRequestBodyDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          },
        },
        "tags": [
          "Project related endpoints"
        ]
      }
    }
  },
  "info": {
    "title": "Asset API Specification",
    "description": "API Specification that an asset has to implement in order to integrate with the Platform.",
    "version": "1.0.0",
    "contact": {}
  },
  "tags": [],
  "servers": [
    {
      "url": "dummy"
    }
  ]
}

This yields

# FastAPI does not like the `None` below, it should be `...`
projectId: str = Path(None, description="Globally unique ID of an project across time"),

This check is explicitly codified in the FastAPI source code:

https://github.com/tiangolo/fastapi/blob/master/fastapi/params.py

i.e.,

class Path(Param):
    in_ = ParamTypes.path

    def __init__(
        self,
        default: Any = ...,
        *,
        alias: Optional[str] = None,
        title: Optional[str] = None,
        description: Optional[str] = None,
        gt: Optional[float] = None,
        ge: Optional[float] = None,
        lt: Optional[float] = None,
        le: Optional[float] = None,
        min_length: Optional[int] = None,
        max_length: Optional[int] = None,
        regex: Optional[str] = None,
        examples: Optional[List[Any]] = None,
        example: Annotated[
            Optional[Any],
            deprecated(
                "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
                "although still supported. Use examples instead."
            ),
        ] = Undefined,
        deprecated: Optional[bool] = None,
        include_in_schema: bool = True,
        **extra: Any,
    ):
        assert default is ..., "Path parameters cannot have a default value"
        ...
Generation Details
Steps to reproduce
  1. Use config above to generate with FastAPI generator
  2. See generated output
Suggest a fix

Ensure default path params are always set to ...

@rockc2020
Copy link

I met the same issue when running openapi-generator for python fastapi, and then pytest failed.
After changing None to ... as suggested, all tests passed.
openapi-generator version: 6.6.0

@giulio-opal
Copy link

I'm encountering the same issue, even with the latest version:

Traceback (most recent call last):
  File "/venv/bin/uvicorn", line 8, in <module>
    sys.exit(main())
  File "/venv/lib/python3.7/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
  File "/venv/lib/python3.7/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
  File "/venv/lib/python3.7/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/venv/lib/python3.7/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
  File "/venv/lib/python3.7/site-packages/uvicorn/main.py", line 456, in main
    h11_max_incomplete_event_size=h11_max_incomplete_event_size,
  File "/venv/lib/python3.7/site-packages/uvicorn/main.py", line 578, in run
    server.run()
  File "/venv/lib/python3.7/site-packages/uvicorn/server.py", line 61, in run
    return asyncio.run(self.serve(sockets=sockets))
  File "/usr/local/lib/python3.7/asyncio/runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "uvloop/loop.pyx", line 1517, in uvloop.loop.Loop.run_until_complete
  File "/venv/lib/python3.7/site-packages/uvicorn/server.py", line 68, in serve
    config.load()
  File "/venv/lib/python3.7/site-packages/uvicorn/config.py", line 473, in load
    self.loaded_app = import_from_string(self.app)
  File "/venv/lib/python3.7/site-packages/uvicorn/importer.py", line 21, in import_from_string
    module = importlib.import_module(module_str)
  File "/usr/local/lib/python3.7/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/venv/lib/python3.7/site-packages/openapi_server/main.py", line 15, in <module>
    from openapi_server.apis.groups_api import router as GroupsApiRouter
  File "/venv/lib/python3.7/site-packages/openapi_server/apis/groups_api.py", line 53, in <module>
    group_id: str = Path(None, description="The id of the group."),
  File "/venv/lib/python3.7/site-packages/fastapi/param_functions.py", line 85, in Path
    **extra,
  File "/venv/lib/python3.7/site-packages/fastapi/params.py", line 182, in __init__
    assert default is ..., "Path parameters cannot have a default value"
AssertionError: Path parameters cannot have a default value

I can either remove None or replace it with ... but every time I regenerate the schema, that change is overridden. Is there a workaround I can adopt to avoid having to replace it every time? Thank you!

@yoccodog
Copy link

i ran into this and used a custom template file to fix the issue. the contents of my custom endpoint_argument_definition.mustache file are:

{{#isPathParam}}{{baseName}}{{/isPathParam}}{{^isPathParam}}{{paramName}}{{/isPathParam}}: {{>param_type}} = {{#isPathParam}}Path{{/isPathParam}}{{#isHeaderParam}}Header{{/isHeaderParam}}{{#isFormParam}}Form{{/isFormParam}}{{#isQueryParam}}Query{{/isQueryParam}}{{#isCookieParam}}Cookie{{/isCookieParam}}{{#isBodyParam}}Body{{/isBodyParam}}({{&defaultValue}}{{^defaultValue}}{{#isPathParam}}...{{/isPathParam}}{{^isPathParam}}None{{/isPathParam}}{{/defaultValue}}, description="{{description}}"{{#isLong}}{{#minimum}}, ge={{.}}{{/minimum}}{{#maximum}}, le={{.}}{{/maximum}}{{/isLong}}{{#isInteger}}{{#minimum}}, ge={{.}}{{/minimum}}{{#maximum}}, le={{.}}{{/maximum}}{{/isInteger}}{{#pattern}}, regex=r"{{.}}"{{/pattern}}{{#minLength}}, min_length={{.}}{{/minLength}}{{#maxLength}}, max_length={{.}}{{/maxLength}})

it's hard to see the diff but i changed this:

{{^defaultValue}}None{{/defaultValue}}

to this:

{{^defaultValue}}{{#isPathParam}}...{{/isPathParam}}{{^isPathParam}}None{{/isPathParam}}{{/defaultValue}}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants