From a442f25c2142c5ac63ebabf2e280e8eada082848 Mon Sep 17 00:00:00 2001 From: etapau Date: Sun, 3 Jun 2018 23:51:35 -0400 Subject: [PATCH] RefResolver export function for resolving references --- .gitignore | 4 ++++ jsonschema/tests/fixtures/__init__.py | 6 ++++++ jsonschema/tests/fixtures/apple.json | 19 ++++++++++++++++++ jsonschema/tests/fixtures/orange.json | 19 ++++++++++++++++++ jsonschema/tests/fixtures/tree.json | 21 ++++++++++++++++++++ jsonschema/tests/test_validators.py | 14 ++++++++++++++ jsonschema/validators.py | 28 +++++++++++++++++++++++++++ 7 files changed, 111 insertions(+) create mode 100644 jsonschema/tests/fixtures/__init__.py create mode 100644 jsonschema/tests/fixtures/apple.json create mode 100644 jsonschema/tests/fixtures/orange.json create mode 100644 jsonschema/tests/fixtures/tree.json diff --git a/.gitignore b/.gitignore index 31236db57..f3e0c86f8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,7 @@ _static _templates TODO +.idea +.python-version +.eggs +jsonschema.egg-info diff --git a/jsonschema/tests/fixtures/__init__.py b/jsonschema/tests/fixtures/__init__.py new file mode 100644 index 000000000..8649d7926 --- /dev/null +++ b/jsonschema/tests/fixtures/__init__.py @@ -0,0 +1,6 @@ +from pathlib import Path +from json import loads + +apple = loads(open(str(Path(__file__).parents[0]) + '/apple.json').read()) +orange = loads(open(str(Path(__file__).parents[0]) + '/orange.json').read()) +tree = loads(open(str(Path(__file__).parents[0]) + '/tree.json').read()) diff --git a/jsonschema/tests/fixtures/apple.json b/jsonschema/tests/fixtures/apple.json new file mode 100644 index 000000000..f06fd1210 --- /dev/null +++ b/jsonschema/tests/fixtures/apple.json @@ -0,0 +1,19 @@ +{ + "$id": "apples", + "type": "object", + "$schema": "http://json-schema.org/draft-06/schema#", + "properties": { + "name": { + "type": "string" + }, + "quantity": { + "type": "integer" + }, + "types": { + "type": "array", + "items": { + "type": "string" + } + } + } +} \ No newline at end of file diff --git a/jsonschema/tests/fixtures/orange.json b/jsonschema/tests/fixtures/orange.json new file mode 100644 index 000000000..7d6d2892c --- /dev/null +++ b/jsonschema/tests/fixtures/orange.json @@ -0,0 +1,19 @@ +{ + "$id": "oranges", + "type": "object", + "$schema": "http://json-schema.org/draft-06/schema#", + "properties": { + "name": { + "type": "string" + }, + "quantity": { + "type": "integer" + }, + "types": { + "type": "array", + "items": { + "type": "string" + } + } + } +} \ No newline at end of file diff --git a/jsonschema/tests/fixtures/tree.json b/jsonschema/tests/fixtures/tree.json new file mode 100644 index 000000000..20997ebda --- /dev/null +++ b/jsonschema/tests/fixtures/tree.json @@ -0,0 +1,21 @@ +{ + "$id": "tree", + "type": "object", + "$schema": "http://json-schema.org/draft-06/schema#", + "properties": { + "name": { + "type": "string" + }, + "fruits": { + "type": "object", + "properties": { + "apples": { + "$ref": "apples" + }, + "oranges": { + "$ref": "oranges" + } + } + } + } +} \ No newline at end of file diff --git a/jsonschema/tests/test_validators.py b/jsonschema/tests/test_validators.py index f2f5a1f8b..edfbb9e26 100644 --- a/jsonschema/tests/test_validators.py +++ b/jsonschema/tests/test_validators.py @@ -16,6 +16,7 @@ validators, ) from jsonschema.tests.compat import mock +from jsonschema.tests.fixtures import apple, orange, tree class TestCreateAndExtend(TestCase): @@ -1273,6 +1274,19 @@ def test_helpful_error_message_on_failed_pop_scope(self): resolver.pop_scope() self.assertIn("Failed to pop the scope", str(exc.exception)) + def test_export_resolved_references(self): + ref_resolver = validators.RefResolver( + '', + None, + store={ + 'apples': apple, + 'oranges': orange, + }) + resolved_tree_refs = ref_resolver.export_resolved_references(tree) + self.assertTrue(apple, resolved_tree_refs['properties']['fruits']['properties']['apples']) + self.assertTrue(orange, resolved_tree_refs['properties']['fruits']['properties']['oranges']) + + class UniqueTupleItemsMixin(object): """ diff --git a/jsonschema/validators.py b/jsonschema/validators.py index a47c3aefa..e1233129e 100644 --- a/jsonschema/validators.py +++ b/jsonschema/validators.py @@ -6,6 +6,7 @@ import numbers from six import add_metaclass +from urllib.parse import urlparse from jsonschema import _utils, _validators, _types from jsonschema.compat import ( @@ -759,6 +760,33 @@ def resolve_remote(self, uri): self.store[uri] = result return result + def export_resolved_references(self, schema: dict): + """ + Resolves json references and merges them into a consolidated schema for validation purposes + :param schema: + :return: schema merged with resolved references + """ + if len(self.store) <= 2: + return RefResolutionError('RefResolver does not have any additional ' +\ + 'referenced schemas outside of draft 3 & 4') + + if isinstance(schema, dict): + for key, value in schema.items(): + if key == "$ref": + ref_schema = self.resolve(urlparse(value).path) + if ref_schema: + return ref_schema[1] + + resolved_ref = self.export_resolved_references(value) + if resolved_ref: + schema[key] = resolved_ref + elif isinstance(schema, list): + for (idx, value) in enumerate(schema): + resolved_ref = self.export_resolved_references(value) + if resolved_ref: + schema[idx] = resolved_ref + return schema + def validate(instance, schema, cls=None, *args, **kwargs): """