import unittest
from copy import deepcopy
+from typing import Dict
-from wrpylib.json_tools import order_json_keys
+from wrpylib.json_tools import order_json_keys, _resolve_ref
-
-schema_object = {
+schema_object: Dict = {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
}
-class TestJsonValidate(unittest.TestCase):
+class TestResolveRef(unittest.TestCase):
+ def test_no_ref(self):
+ sub_schema = schema_object['properties']['aliases']
+ actual = _resolve_ref(sub_schema, schema_object)
+ self.assertEqual(actual, sub_schema)
+
+ def test_ref(self):
+ root_schema = schema_object.copy()
+ root_schema["definitions"] = {
+ "weblink": {
+ "type": "string",
+ }
+ }
+ sub_schema = {
+ "$ref": "#/definitions/weblink"
+ }
+ expected = {
+ "type": "string",
+ }
+ actual = _resolve_ref(sub_schema, root_schema)
+ self.assertEqual(expected, actual)
+
+
+class TestOrderJsonKeys(unittest.TestCase):
def test_string_empty(self):
actual = order_json_keys('', {"type": "string"})
self.assertEqual('', actual)
return f'schema[{"][".join(map(repr, path))}]'
+def _resolve_ref_not_recursive(sub_schema: JsonTypes, schema: JsonTypes) -> JsonTypes:
+ """In case the sub_schema is a dict and has direct "$ref" keys,
+ it is replaced by a dict where the "$ref" key is replaced by the corresponding definition in schema.
+ Nested $ref keys are not resolved.
+ Recursive $ref keys are not resolved.
+
+ :param sub_schema: JSON sub-schema where a "$ref" key is possible replaced.
+ The value of "$ref" could be e.g. "#/definitions/position"
+ :param schema: JSON root schema containing definitions for the keys.
+ :raise ValidationError: In case a "$ref" could not be resolved.
+ """
+ if not isinstance(sub_schema, dict) or '$ref' not in sub_schema:
+ return sub_schema
+ ref = sub_schema['$ref']
+ if not isinstance(ref, str):
+ raise ValidationError(f'Type of reference {ref} is not string.')
+ path = ref.split('/')
+ if len(path) == 0 or path[0] != '#':
+ raise ValidationError(f'Unsupported reference {ref}.')
+ ref_schema = schema
+ for p in path[1:]:
+ if not isinstance(ref_schema, dict) or p not in ref_schema:
+ raise ValidationError(f'Reference path {ref} not found in schema.')
+ ref_schema = ref_schema[p]
+ if not isinstance(ref_schema, dict):
+ raise ValidationError(f'Reference path {ref} is no dict.')
+ sub_schema = sub_schema.copy()
+ del sub_schema['$ref']
+ resolved_schema = ref_schema.copy()
+ resolved_schema.update(sub_schema)
+ return resolved_schema
+
+
+def _resolve_ref(sub_schema: JsonTypes, schema: JsonTypes) -> JsonTypes:
+ """Same as `_resolve_ref_not_recursive` but recursively resolves $ref keys.
+ However, does not resolve nested $ref keys.
+ """
+ resolved_schema = sub_schema
+ while isinstance(resolved_schema, dict) and '$ref' in resolved_schema:
+ resolved_schema = _resolve_ref_not_recursive(resolved_schema, schema)
+ return resolved_schema
+
+
def _order_json_keys_string(sub_value: JsonTypes, sub_schema: JsonTypes, schema: JsonTypes, path: List) -> str:
if not isinstance(sub_value, str):
raise ValidationError(f'Type of {_fmt_path(path)} needs to be string (Python str).')
if sub_schema:
return sub_value
raise ValidationError(f'Value {sub_value} not allowed in {_fmt_path(path)}.')
+ if isinstance(sub_schema, dict):
+ sub_schema = _resolve_ref(sub_schema, schema)
return {
'string': _order_json_keys_string,
'number': _order_json_keys_number,