Connexion v3 找不到“$ref”部分中定义的文件路径

问题描述 投票:0回答:1

我们的团队基于 connextion v3 为应用程序创建了一个 API。这个 API 工作得很好。为了测试应用程序的流程,我们创建了测试,在测试中我们以与启动 API 完全相同的方式和相同的参数来准备连接服务器。唯一的区别是我们运行 prepare_server() 方法的路径,因为该文件位于 tests 目录中。我们的 API 由许多 Endpoint 组成,因此 swagger 文档分为许多参考文件,在构建 API 时很容易将其组合成一个大整体。但是,运行测试时,组装路径时会出现 FileNotFoundError 错误。

测试/fixtures.py

"""Setup the test environment connexion server."""

import contextlib
import os
import yaml
from typing import Generator

from connexion import FlaskApp
from connexion.resolver import MethodResolver
from flask.testing import FlaskClient


@contextlib.contextmanager
def prepare_server() -> Generator[FlaskClient, str, None]:
    """Context manager that launches local API instance and lets you send request to it.
    Example usage:
    ```
    with prepare_server() as (server, token):
        server.post(
        "/api/v1/control/my_endpoint",
        data = json.dumps(dict)
        headers={
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json",
        })
    """

    flask_app: FlaskApp = FlaskApp("main", specification_dir=".")
    flask_app.add_api("openAPI.yml", resolver=MethodResolver("test_api"))

    server: FlaskClient = flask_app.app.test_client()
    yield (server, token)

openAPI.yml

openapi: 3.0.3
info:
  title: My API
  description: Rest api for my application
  version: 0.0.1
servers:
  - url: "/api/v1"
paths:
  /application/endpoint/endpoint1/{Id}:
    $ref: 'paths/to/endpoint1.yaml'
  /application/endpoint/endpoint2:
    $ref: 'paths/to/endpoint2.yaml'
  /application/endpoint/endpoint3:
    $ref: 'paths/to/endpoint3.yaml'
  /application/endpoint/endpoint4:
    $ref: 'paths/to/endpoint4.yaml'
  ...
components:
  schemas:
    SchemaOne:
      $ref: 'components/schemas/schema_one.yaml'
    SchemaTwo:
      $ref: 'components/schemas/schema_two.yaml'
    SchemaThree:
      $ref: 'components/schemas/schema_tree.yaml'

在 Windows 上运行测试后,出现错误:

Error
Traceback (most recent call last):
  File "path\to\my\app\venv\Lib\site-packages\connexion\json_schema.py", line 88, in _do_resolve
    retrieved = deep_get(spec, path)
                ^^^^^^^^^^^^^^^^^^^^
  File "path\to\my\app\venv\Lib\site-packages\connexion\utils.py", line 112, in deep_get
    return deep_get(obj[keys[0]], keys[1:])
                    ~~~^^^^^^^^^
KeyError: 'ths'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "path\to\my\app\venv\Lib\site-packages\jsonschema\validators.py", line 1092, in resolve_from_url
    document = self.store[url]
               ~~~~~~~~~~^^^^^
  File "path\to\my\app\venv\Lib\site-packages\jsonschema\_utils.py", line 20, in __getitem__
    return self.store[self.normalize(uri)]
           ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
KeyError: 'paths/to/endpoint1.yaml'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "path\to\my\app\venv\Lib\site-packages\jsonschema\validators.py", line 1095, in resolve_from_url
    document = self.resolve_remote(url)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "path\to\my\app\venv\Lib\site-packages\jsonschema\validators.py", line 1192, in resolve_remote
    result = self.handlers[scheme](uri)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "path\to\my\app\venv\Lib\site-packages\connexion\json_schema.py", line 41, in __call__
    with open(filepath) as fh:
         ^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: '\\\\\\paths\\to\\endpoint1.yaml'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "path\to\my\app\venv\Lib\site-packages\responses\__init__.py", line 218, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "path\to\my\app\app\tests\decorators.py", line 67, in wrap
    func(self, *args, **kwargs)
  File "path\to\my\app\venv\Lib\site-packages\responses\__init__.py", line 218, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "path\to\my\app\app\tests\decorators.py", line 35, in wrap
    func(self, *args, **kwargs)
  File "path\to\my\app\venv\Lib\site-packages\responses\__init__.py", line 218, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "path\to\my\app\app\tests\decorators.py", line 19, in wrap
    func(self, *args, **kwargs)
  File "path\to\my\app\app\tests\bb_api_main\cmdb\test_mail_verification.py", line 117, in test_happy_flow
    with prepare_server() as (server, token):
  File "C:\Users\HW36WN\AppData\Local\Programs\Python\Python311\Lib\contextlib.py", line 137, in __enter__
    return next(self.gen)
           ^^^^^^^^^^^^^^
  File "path\to\my\app\app\tests\fixtures.py", line 41, in prepare_server
    flask_app.add_api("openAPI.yml", resolver=MethodResolver("test_api"))
  File "path\to\my\app\venv\Lib\site-packages\connexion\apps\abstract.py", line 180, in add_api
    return self.middleware.add_api(
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "path\to\my\app\venv\Lib\site-packages\connexion\middleware\main.py", line 420, in add_api
    specification = Specification.load(specification, arguments=arguments)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "path\to\my\app\venv\Lib\site-packages\connexion\spec.py", line 205, in load
    return cls.from_file(spec, arguments=arguments, base_uri=base_uri)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "path\to\my\app\venv\Lib\site-packages\connexion\spec.py", line 159, in from_file
    return cls.from_dict(spec, base_uri=base_uri)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "path\to\my\app\venv\Lib\site-packages\connexion\spec.py", line 196, in from_dict
    return OpenAPISpecification(spec, base_uri=base_uri)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "path\to\my\app\venv\Lib\site-packages\connexion\spec.py", line 83, in __init__
    self._spec = resolve_refs(raw_spec, base_uri=base_uri)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "path\to\my\app\venv\Lib\site-packages\connexion\json_schema.py", line 106, in resolve_refs
    res = _do_resolve(spec)
          ^^^^^^^^^^^^^^^^^
  File "path\to\my\app\venv\Lib\site-packages\connexion\json_schema.py", line 100, in _do_resolve
    node[k] = _do_resolve(v)
              ^^^^^^^^^^^^^^
  File "path\to\my\app\venv\Lib\site-packages\connexion\json_schema.py", line 100, in _do_resolve
    node[k] = _do_resolve(v)
              ^^^^^^^^^^^^^^
  File "path\to\my\app\venv\Lib\site-packages\connexion\json_schema.py", line 96, in _do_resolve
    with resolver.resolving(node["$ref"]) as resolved:
  File "C:\Users\HW36WN\AppData\Local\Programs\Python\Python311\Lib\contextlib.py", line 137, in __enter__
    return next(self.gen)
           ^^^^^^^^^^^^^^
  File "path\to\my\app\venv\Lib\site-packages\jsonschema\validators.py", line 1034, in resolving
    url, resolved = self.resolve(ref)
                    ^^^^^^^^^^^^^^^^^
  File "path\to\my\app\venv\Lib\site-packages\jsonschema\validators.py", line 1081, in resolve
    return url, self._remote_cache(url)
                ^^^^^^^^^^^^^^^^^^^^^^^
  File "path\to\my\app\venv\Lib\site-packages\jsonschema\validators.py", line 1097, in resolve_from_url
    raise exceptions._RefResolutionError(exc)
jsonschema.exceptions._RefResolutionError: [Errno 2] No such file or directory: '\\\\\\paths\\to\\endpoint1.yaml'

在 Linux 上,此错误看起来类似:

 Traceback (most recent call last):
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/flask/app.py", line 867, in full_dispatch_request
     rv = self.dispatch_request()
          ^^^^^^^^^^^^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/flask/app.py", line 841, in dispatch_request
     self.raise_routing_exception(req)
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/flask/app.py", line 450, in raise_routing_exception
     raise request.routing_exception  # type: ignore
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/flask/ctx.py", line 353, in match_request
     result = self.url_adapter.match(return_rule=True)  # type: ignore
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/werkzeug/routing/map.py", line 624, in match
     raise NotFound() from None
 werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
 
 During handling of the above exception, another exception occurred:
 
 Traceback (most recent call last):
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/responses/__init__.py", line 232, in wrapper
     return func(*args, **kwargs)
            ^^^^^^^^^^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/app/tests/decorators.py", line 67, in wrap
     func(self, *args, **kwargs)
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/responses/__init__.py", line 232, in wrapper
     return func(*args, **kwargs)
            ^^^^^^^^^^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/app/tests/decorators.py", line 35, in wrap
     func(self, *args, **kwargs)
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/responses/__init__.py", line 232, in wrapper
     return func(*args, **kwargs)
            ^^^^^^^^^^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/app/tests/decorators.py", line 19, in wrap
     func(self, *args, **kwargs)
   File "/home/vsts/work/1/repo/app/tests/application/endpoint/test_endpoint_one.py", line 118, in test_happy_flow
     response: werkzeug.Response = server.post(
                                   ^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/werkzeug/test.py", line 1165, in post
     return self.open(*args, **kw)
            ^^^^^^^^^^^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/flask/testing.py", line 232, in open
     response = super().open(
                ^^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/werkzeug/test.py", line 1114, in open
     response = self.run_wsgi_app(request.environ, buffered=buffered)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/werkzeug/test.py", line 986, in run_wsgi_app
     rv = run_wsgi_app(self.application, environ, buffered=buffered)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/werkzeug/test.py", line 1262, in run_wsgi_app
     app_rv = app(environ, start_response)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/flask/app.py", line 1478, in __call__
     return self.wsgi_app(environ, start_response)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/flask/app.py", line 1458, in wsgi_app
     response = self.handle_exception(e)
                ^^^^^^^^^^^^^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/flask/app.py", line 1455, in wsgi_app
     response = self.full_dispatch_request()
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/flask/app.py", line 869, in full_dispatch_request
     rv = self.handle_user_exception(e)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/flask/app.py", line 759, in handle_user_exception
     return self.ensure_sync(handler)(e)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/home/vsts/work/1/repo/venv/lib/python3.11/site-packages/connexion/apps/flask.py", line 245, in _http_exception
     raise starlette.exceptions.HTTPException(exc.code, detail=exc.description)
 starlette.exceptions.HTTPException: 404: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
 

我已经尝试添加参考swagger文件的绝对路径,并且尝试以各种方式操作

specification_dir
参数,但不幸的是我没有达到预期的效果。

openapi connexion
1个回答
0
投票

连接使用的 jsonschema 库似乎有问题,导致路径混乱。我可以通过使用自定义文件处理程序猴子修补文件处理程序来使其工作:

from pathlib import Path
import yaml
import connexion.json_schema
from connexion.json_schema import ExtendedSafeLoader

class CustomFileHandler:
    """Handler to resolve file refs."""
    def __call__(self, uri):
        path = Path("your_corret_path_to_spec").resolve()
        with open(path) as fh:
            return yaml.load(fh, ExtendedSafeLoader)


connexion.json_schema.handlers.update(
    {
        "file": CustomFileHandler(),
        "": CustomFileHandler(),
    }
)
© www.soinside.com 2019 - 2024. All rights reserved.