diff --git a/src/caosadvancedtools/json_schema_exporter.py b/src/caosadvancedtools/json_schema_exporter.py index 494429224a332452ee0cac8057f8615b9c19a3b0..66cda6d858bc366c937ec6c0c317eadc51809f1f 100644 --- a/src/caosadvancedtools/json_schema_exporter.py +++ b/src/caosadvancedtools/json_schema_exporter.py @@ -137,12 +137,15 @@ class JsonSchemaExporter: # TODO: different issue raise NotImplementedError("Files have not been implemented yet.") else: - values = self._retrieve_enum_values(f"RECORD '{prop.datatype}'") - if prop.datatype in self._do_not_create: + prop_name = prop.datatype + if isinstance(prop.datatype, db.Entity): + prop_name = prop.datatype.name + values = self._retrieve_enum_values(f"RECORD '{prop_name}'") + if prop_name in self._do_not_create: # Only a simple list of values json_prop["enum"] = values else: - rt = db.execute_query(f"FIND RECORDTYPE WITH name='{prop.datatype}'", + rt = db.execute_query(f"FIND RECORDTYPE WITH name='{prop_name}'", unique=True) subschema = self._make_segment_from_recordtype(rt) subschema["title"] = "Create new" diff --git a/src/caosadvancedtools/models/data_model.py b/src/caosadvancedtools/models/data_model.py index beff7e9d847c6a0854d4e38d7cee900d8a376eab..50ad8e73747f0d9223f3a9b45b05cf8ce82a39e9 100644 --- a/src/caosadvancedtools/models/data_model.py +++ b/src/caosadvancedtools/models/data_model.py @@ -262,3 +262,27 @@ class DataModel(dict): all_ents[prop.name] = prop return list(all_ents.values()) + + def get_deep(self, name, visited: set = None): + """Attempt to resolve references for the given ``name``. + + This methods only uses data which is available in this datamodel, which acts kind of like a + cache pool. + """ + entity = self.get(name) + if not entity: + return entity + if not visited: + visited = set() + + # new_props = [] + for prop in list(entity.get_properties()): # Make a change-resistant list copy. + if prop.name in visited: + continue + visited.add(prop.name) + if prop.name in self: + entity.remove_property(prop).add_property(self.get_deep(prop.name, + visited=visited)) + else: + print(f"Referenced property \"{prop.name}\" not found in data model.") + return entity diff --git a/unittests/test_json_schema_exporter.py b/unittests/test_json_schema_exporter.py index 8a8c3bcc3583650ec54b3922feabb7dd6e95ad58..2c04e970c389db903dbf1c2c94dddce0712bdbfb 100644 --- a/unittests/test_json_schema_exporter.py +++ b/unittests/test_json_schema_exporter.py @@ -22,6 +22,8 @@ """Tests the Json schema exporter.""" +import json + import linkahead as db from jsonschema import FormatChecker, validate, ValidationError @@ -29,6 +31,16 @@ from pytest import raises from unittest.mock import Mock, patch from caosadvancedtools.json_schema_exporter import recordtype_to_json_schema as rtjs +from caosadvancedtools.models.parser import parse_model_from_string + +RT1 = parse_model_from_string(""" +RT1: + description: some description + obligatory_properties: + some_date: + datatype: DATETIME + description: Just some date +""").get_deep("RT1") def _mock_execute_query(query_string, unique=False, **kwargs): @@ -62,6 +74,10 @@ def _mock_execute_query(query_string, unique=False, **kwargs): return referencing_type_records elif query_string == "FIND RECORDTYPE WITH name='ReferencingType'" and unique is True: return referencing_type_rt + elif query_string == "SELECT name, id FROM RECORD 'RT1'": + return referencing_type_records # wrong types, but who cares for the test? + elif query_string == "FIND RECORDTYPE WITH name='RT1'" and unique is True: + return RT1 elif query_string == "SELECT name, id FROM RECORD": return all_records elif query_string == "SELECT name, id FROM FILE": @@ -548,3 +564,113 @@ def test_broken(): rtjs(rt) assert "MultiProp" in str(nie) assert str(nie).startswith("Creating a schema for multi-properties is not specified.") + + +@patch("linkahead.execute_query", new=Mock(side_effect=_mock_execute_query)) +def test_reference_options(): + """Testing miscellaneous options. + """ + + model_str = """ +RT1: + description: some description + obligatory_properties: + some_date: + datatype: DATETIME + description: Just some date +RT2: + obligatory_properties: + RT1: + """ + model = parse_model_from_string(model_str) + # First test: without reference + rt1_dict = rtjs(model.get_deep("RT1")) + assert json.dumps(rt1_dict, indent=2) == """{ + "type": "object", + "required": [], + "additionalProperties": true, + "properties": { + "some_date": { + "description": "Just some date", + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "string", + "format": "date-time" + } + ] + } + }, + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "RT1", + "description": "some description" +}""" + # Second test: with reference + + # from IPython import embed + # embed() + rt2_dict = rtjs(model.get_deep("RT2")) + assert json.dumps(rt2_dict, indent=2) == """{ + "type": "object", + "required": [], + "additionalProperties": true, + "properties": { + "RT1": { + "description": "some description", + "oneOf": [ + { + "title": "Existing entries", + "enum": [ + "103", + "104, referencing" + ] + }, + { + "type": "object", + "required": [], + "additionalProperties": true, + "properties": { + "some_date": { + "description": "Just some date", + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "string", + "format": "date-time" + } + ] + } + }, + "title": "Create new" + } + ] + } + }, + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "RT2" +}""" + + # Third test: Reference prop shall be only existing references, no option to create new ones. + rt2_dict = rtjs(model.get_deep("RT2"), do_not_create=["RT1"]) + assert json.dumps(rt2_dict, indent=2) == """{ + "type": "object", + "required": [], + "additionalProperties": true, + "properties": { + "RT1": { + "description": "some description", + "enum": [ + "103", + "104, referencing" + ] + } + }, + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "RT2" +}"""