Skip to content

Commit 0f4f534

Browse files
peter-doggartPeter Doggart
andauthored
Move from jsonschema.RefResolver to the new referencing library. (#639)
Co-authored-by: Peter Doggart <[email protected]>
1 parent ffb079c commit 0f4f534

File tree

4 files changed

+86
-7
lines changed

4 files changed

+86
-7
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Bug Fixes
3535
::
3636

3737
* Add python version requirement on setup.py (#586) [jason-the-j]
38+
* Fix reference resolution for definitions in schema. (#553) [peter-doggart]
3839

3940

4041
.. _section-1.3.0:

flask_restx/api.py

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
from flask.signals import got_request_exception
1818

19-
from jsonschema import RefResolver
19+
from referencing import Registry
2020

2121
from werkzeug.utils import cached_property
2222
from werkzeug.datastructures import Headers
@@ -133,7 +133,7 @@ def __init__(
133133
format_checker=None,
134134
url_scheme=None,
135135
default_swagger_filename="swagger.json",
136-
**kwargs
136+
**kwargs,
137137
):
138138
self.version = version
139139
self.title = title or "API"
@@ -825,7 +825,44 @@ def payload(self):
825825
@property
826826
def refresolver(self):
827827
if not self._refresolver:
828-
self._refresolver = RefResolver.from_schema(self.__schema__)
828+
# Create a registry that can resolve references within our schema
829+
registry = Registry()
830+
schema = self.__schema__
831+
832+
# If schema has definitions, register it
833+
if "definitions" in schema:
834+
schema_id = schema.get("$id", "http://localhost/schema.json")
835+
registry = registry.with_resource(schema_id, schema)
836+
else:
837+
# If no definitions in schema, register all models individually
838+
for name, model in self.models.items():
839+
model_schema = model.__schema__
840+
# Add $id to the model schema so it can be referenced
841+
if "$id" not in model_schema:
842+
model_schema = model_schema.copy()
843+
model_schema["$id"] = (
844+
f"http://localhost/schema.json#/definitions/{name}"
845+
)
846+
registry = registry.with_resource(
847+
f"http://localhost/schema.json#/definitions/{name}",
848+
model_schema,
849+
)
850+
851+
# Also register the root schema with definitions
852+
if self.models:
853+
definitions = {}
854+
for name, model in self.models.items():
855+
definitions[name] = model.__schema__
856+
857+
schema_with_definitions = {
858+
"$id": "http://localhost/schema.json",
859+
"definitions": definitions,
860+
}
861+
registry = registry.with_resource(
862+
"http://localhost/schema.json", schema_with_definitions
863+
)
864+
865+
self._refresolver = registry
829866
return self._refresolver
830867

831868
@staticmethod
@@ -861,7 +898,7 @@ def _blueprint_setup_add_url_rule_patch(
861898
"%s.%s" % (blueprint_setup.blueprint.name, endpoint),
862899
view_func,
863900
defaults=defaults,
864-
**options
901+
**options,
865902
)
866903

867904
def _deferred_blueprint_init(self, setup_state):

flask_restx/model.py

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from .errors import abort
1212

1313
from jsonschema import Draft4Validator
14+
from jsonschema.validators import validator_for
1415
from jsonschema.exceptions import ValidationError
1516

1617
from .utils import not_none
@@ -89,9 +90,48 @@ def inherit(cls, name, *parents):
8990
return model
9091

9192
def validate(self, data, resolver=None, format_checker=None):
92-
validator = Draft4Validator(
93-
self.__schema__, resolver=resolver, format_checker=format_checker
94-
)
93+
# For backward compatibility, resolver can be either a RefResolver or a Registry
94+
if resolver is not None and hasattr(resolver, "resolve"):
95+
# Old RefResolver - convert to registry
96+
registry = None
97+
validator = Draft4Validator(
98+
self.__schema__, resolver=resolver, format_checker=format_checker
99+
)
100+
else:
101+
# New Registry or None
102+
# If we have a registry, we need to create a schema that includes definitions
103+
schema_to_validate = self.__schema__
104+
if resolver is not None:
105+
# Check if the schema has $ref that need to be resolved
106+
import json
107+
108+
schema_str = json.dumps(self.__schema__)
109+
if '"$ref"' in schema_str:
110+
# Create a schema with inline definitions from the registry
111+
definitions = {}
112+
for uri in resolver:
113+
resource = resolver[uri]
114+
if isinstance(resource, dict) and "definitions" in resource:
115+
definitions.update(resource["definitions"])
116+
117+
if definitions:
118+
# Create a new schema that includes the definitions
119+
schema_to_validate = {
120+
"$id": "http://localhost/schema.json",
121+
"definitions": definitions,
122+
**self.__schema__,
123+
}
124+
125+
ValidatorClass = validator_for(schema_to_validate)
126+
if resolver is not None:
127+
validator = ValidatorClass(
128+
schema_to_validate, registry=resolver, format_checker=format_checker
129+
)
130+
else:
131+
validator = ValidatorClass(
132+
schema_to_validate, format_checker=format_checker
133+
)
134+
95135
try:
96136
validator.validate(data)
97137
except ValidationError:

requirements/install.pip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
aniso8601>=0.82
22
jsonschema
3+
referencing
34
Flask>=0.8, !=2.0.0
45
werkzeug!=2.0.0
56
importlib_resources

0 commit comments

Comments
 (0)