-
-
Notifications
You must be signed in to change notification settings - Fork 762
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
connexion does not parse simple reference links #967
Comments
I have the exact same issue when trying to reference a yaml file that is in the same location as the current file. Is there any solution or workaround to this?
|
If I prefix the URI with file: it gets closer
|
This can be forced to work with
But that is not really going to work on a server |
Hi all, I could work around this issue by specifing handlers for "no scheme" as illustrated below (in my app.py): # -------------------- workaround begin
# these handlers are used in RefResolver.resolve_remote()
from connexion.json_schema import default_handlers as json_schema_handlers
from openapi_spec_validator import default_handlers as spec_validator_handlers
# jsonschema does not handle relative file paths:
# https://github.com/zalando/connexion/issues/967
#default_file_handler = default_handlers['file']
SPECIFICATION_DIR = dirname(__file__)
def my_local_file_handler(uri):
'''return the parsed result'''
import yaml
with open(join(SPECIFICATION_DIR, uri), 'r') as f:
return yaml.load(f, Loader=yaml.SafeLoader)
json_schema_handlers[''] = my_local_file_handler
spec_validator_handlers[''] = my_local_file_handler
# patch refresolver-bug: (does not handle relative refs correct in path scopes)
from jsonschema.validators import RefResolver
def myresolve(self, ref):
url = self._urljoin_cache(self.resolution_scope, ref)
return ref, self._remote_cache(url)
RefResolver.resolve = myresolve
# -------------------- workaround end
# ... later:
connexion_app = connexion.App("appname", specification_dir=SPECIFICATION_DIR)
api = connexion_app.add_api("OpenApi.yml") when referring then a relative file like: $ref: 'securitySchemes.yml#/ApiKey' it will open the local file as expected. |
If you want to use refs also in files, you have also to patch resolve_refs function from connexion.json_schema, because it does not resolve refs recursively. Actually it is only about changing: return resolved to: return _do_resolve(resolved) Here complete patch code: from copy import deepcopy
from jsonschema import RefResolver
from connexion.utils import deep_get
try:
from collections.abc import Mapping
except ImportError:
from collections import Mapping
def my_resolve_refs(spec, store=None, handlers=None):
"""
Resolve JSON references like {"$ref": <some URI>} in a spec.
Optionally takes a store, which is a mapping from reference URLs to a
dereferenced objects. Prepopulating the store can avoid network calls.
"""
from connexion.json_schema import default_handlers
spec = deepcopy(spec)
store = store or {}
handlers = handlers or default_handlers
resolver = RefResolver('', spec, store, handlers=handlers)
def _do_resolve(node):
if isinstance(node, Mapping) and '$ref' in node:
path = node['$ref'][2:].split("/")
try:
# resolve known references
node.update(deep_get(spec, path))
del node['$ref']
return node
except KeyError:
# resolve external references
with resolver.resolving(node['$ref']) as resolved:
# if not fixed:
# return resolved
# else:
return _do_resolve(resolved)
# endfix
elif isinstance(node, Mapping):
for k, v in node.items():
node[k] = _do_resolve(v)
elif isinstance(node, (list, tuple)):
for i, _ in enumerate(node):
node[i] = _do_resolve(node[i])
return node
res = _do_resolve(spec)
return res
from connexion import spec
spec.resolve_refs = my_resolve_refs |
Hello, Any news on this issue? I'm interested in because we have huge YML file and we want to split it. |
I am on v2.9.0. It appears that @klorenz patch was adopted at some point, because it's now in the source. If you want to use relative URLs without prefixing import os
import pytest
import jsonschema
import openapi_spec_validator as oapi
import openapi_spec_validator.readers as oapi_readers
def _run(path):
path = os.path.abspath(path)
def _validate_spec(cls, spec):
spec_dict, spec_url = oapi_readers.read_from_filename(path)
oapi.openapi_v3_spec_validator.validate(spec, spec_url)
class _RefResolver(jsonschema.RefResolver):
def __init__(self, base_url, *args, **kwargs):
if len(base_url) == 0:
base_url = f"file:///{path}"
super().__init__(base_url, *args, **kwargs)
with pytest.MonkeyPatch().context() as m:
m.setattr("connexion.spec.OpenAPISpecification._validate_spec", _validate_spec)
m.setattr("connexion.json_schema.RefResolver", _RefResolver)
from connexion.cli import run
return run([path]) |
Hi all, It seems this issue persists in latest version. Looking at the code in def resolve_refs(spec, store=None, handlers=None):
"""
Resolve JSON references like {"$ref": <some URI>} in a spec.
Optionally takes a store, which is a mapping from reference URLs to a
dereferenced objects. Prepopulating the store can avoid network calls.
"""
spec = deepcopy(spec)
store = store or {}
handlers = handlers or default_handlers
resolver = RefResolver('', spec, store, handlers=handlers)
def _do_resolve(node):
if isinstance(node, Mapping) and '$ref' in node:
path = node['$ref'][2:].split("/")
try:
# resolve known references
node.update(deep_get(spec, path))
del node['$ref']
return node
except KeyError:
# resolve external references
with resolver.resolving(node['$ref']) as resolved:
return resolved
elif isinstance(node, Mapping):
for k, v in node.items():
node[k] = _do_resolve(v)
elif isinstance(node, (list, tuple)):
for i, _ in enumerate(node):
node[i] = _do_resolve(node[i])
return node
res = _do_resolve(spec)
return res So in order to keep your OpenAPI specs in different files, you are obliged to perform some kind of hacking. |
I still experience this issue. a.yaml: I keep getting the same exception as the issue starter.
Should this be fixed by now and am I doing something wrong with my referencing or is this still not supported? Would be a bummer if this isn't supported.
|
I have had partial success. I have to use the So if the full path to your file was /foo/bar/test.yaml...
Note there are 3 slashes after When I say "partial" success, I cannot use a reference to an |
This still only appears to work with FULL paths. This makes it very unpractical to use in a deployment scenario, since you must know the file system full path to your specification... |
@meiswjn See my workaround above. |
Im still getting an error when i have a simple ref referencing a local file like this:
The error is I thought it should be fixed with #1648? connexion version: 3.0.5 |
Description
My OpenAPI 3.0.0 specification contains entries that look like this:
This is "remote reference" as documented in the OpenAPI documentation.
Expected behaviour
I expect connexion to resolve the link and read the specified content from the specified file.
Actual behaviour
A positively ungodly traceback:
Additional info:
Output of the commands:
python --version
:Python 3.7.3
pip show connexion | grep "^Version\:"
:Version: 2.2.0
(although this was in fact installed from 0a05243)The text was updated successfully, but these errors were encountered: