Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Bug Fixes
::

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


.. _section-1.3.0:
Expand Down
45 changes: 41 additions & 4 deletions flask_restx/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from flask.signals import got_request_exception

from jsonschema import RefResolver
from referencing import Registry

from werkzeug.utils import cached_property
from werkzeug.datastructures import Headers
Expand Down Expand Up @@ -133,7 +133,7 @@ def __init__(
format_checker=None,
url_scheme=None,
default_swagger_filename="swagger.json",
**kwargs
**kwargs,
):
self.version = version
self.title = title or "API"
Expand Down Expand Up @@ -825,7 +825,44 @@ def payload(self):
@property
def refresolver(self):
if not self._refresolver:
self._refresolver = RefResolver.from_schema(self.__schema__)
# Create a registry that can resolve references within our schema
registry = Registry()
schema = self.__schema__

# If schema has definitions, register it
if "definitions" in schema:
schema_id = schema.get("$id", "http://localhost/schema.json")
registry = registry.with_resource(schema_id, schema)
else:
# If no definitions in schema, register all models individually
for name, model in self.models.items():
model_schema = model.__schema__
# Add $id to the model schema so it can be referenced
if "$id" not in model_schema:
model_schema = model_schema.copy()
model_schema["$id"] = (
f"http://localhost/schema.json#/definitions/{name}"
)
registry = registry.with_resource(
f"http://localhost/schema.json#/definitions/{name}",
model_schema,
)

# Also register the root schema with definitions
if self.models:
definitions = {}
for name, model in self.models.items():
definitions[name] = model.__schema__

schema_with_definitions = {
"$id": "http://localhost/schema.json",
"definitions": definitions,
}
registry = registry.with_resource(
"http://localhost/schema.json", schema_with_definitions
)

self._refresolver = registry
return self._refresolver

@staticmethod
Expand Down Expand Up @@ -861,7 +898,7 @@ def _blueprint_setup_add_url_rule_patch(
"%s.%s" % (blueprint_setup.blueprint.name, endpoint),
view_func,
defaults=defaults,
**options
**options,
)

def _deferred_blueprint_init(self, setup_state):
Expand Down
46 changes: 43 additions & 3 deletions flask_restx/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from .errors import abort

from jsonschema import Draft4Validator
from jsonschema.validators import validator_for
from jsonschema.exceptions import ValidationError

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

def validate(self, data, resolver=None, format_checker=None):
validator = Draft4Validator(
self.__schema__, resolver=resolver, format_checker=format_checker
)
# For backward compatibility, resolver can be either a RefResolver or a Registry
if resolver is not None and hasattr(resolver, "resolve"):
# Old RefResolver - convert to registry
registry = None
validator = Draft4Validator(
self.__schema__, resolver=resolver, format_checker=format_checker
)
else:
# New Registry or None
# If we have a registry, we need to create a schema that includes definitions
schema_to_validate = self.__schema__
if resolver is not None:
# Check if the schema has $ref that need to be resolved
import json

schema_str = json.dumps(self.__schema__)
if '"$ref"' in schema_str:
# Create a schema with inline definitions from the registry
definitions = {}
for uri in resolver:
resource = resolver[uri]
if isinstance(resource, dict) and "definitions" in resource:
definitions.update(resource["definitions"])

if definitions:
# Create a new schema that includes the definitions
schema_to_validate = {
"$id": "http://localhost/schema.json",
"definitions": definitions,
**self.__schema__,
}

ValidatorClass = validator_for(schema_to_validate)
if resolver is not None:
validator = ValidatorClass(
schema_to_validate, registry=resolver, format_checker=format_checker
)
else:
validator = ValidatorClass(
schema_to_validate, format_checker=format_checker
)

try:
validator.validate(data)
except ValidationError:
Expand Down
1 change: 1 addition & 0 deletions requirements/install.pip
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
aniso8601>=0.82
jsonschema
referencing
Flask>=0.8, !=2.0.0
werkzeug!=2.0.0
importlib_resources