diff --git a/CHANGELOG.md b/CHANGELOG.md index e95f37c..e37cf00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added + +- Cache results of processing. Repeated calls to get methods will be faster. +- Can process dependentSchemas keyword +- Can process if / then / else keywords ### Removed diff --git a/compiletojsonschema/compiletojsonschema.py b/compiletojsonschema/compiletojsonschema.py index a670635..ef244b6 100644 --- a/compiletojsonschema/compiletojsonschema.py +++ b/compiletojsonschema/compiletojsonschema.py @@ -27,8 +27,23 @@ def __init__( self.codelist_base_directory = os.path.expanduser(codelist_base_directory) else: self.codelist_base_directory = os.getcwd() + # These vars hold output + self._processed = False + self._output_json = None def get(self): + self.__process() + return self._output_json + + def get_as_string(self): + return json.dumps(self.get(), indent=2) + + def __process(self): + # If already processed, return ..... + if self._processed: + return + + # Process now .... if self.input_filename: with open(self.input_filename) as fp: resolved = jsonref.load( @@ -42,15 +57,10 @@ def get(self): resolved = jsonref.JsonRef.replace_refs(self.input_schema) else: raise Exception("Must pass input_filename or input_schema") + self._output_json = self.__process_data(resolved) + self._processed = True - resolved = self.__process(resolved) - - return resolved - - def get_as_string(self): - return json.dumps(self.get(), indent=2) - - def __process(self, source): + def __process_data(self, source): out = deepcopy(source) @@ -61,24 +71,36 @@ def __process(self, source): if "properties" in source: for leaf in list(source["properties"]): - out["properties"][leaf] = self.__process(source["properties"][leaf]) + out["properties"][leaf] = self.__process_data( + source["properties"][leaf] + ) if self.set_additional_properties_false_everywhere: out["additionalProperties"] = False if "items" in source: - out["items"] = self.__process(source["items"]) + out["items"] = self.__process_data(source["items"]) if "oneOf" in source: for idx, data in enumerate(list(source["oneOf"])): - out["oneOf"][idx] = self.__process(source["oneOf"][idx]) + out["oneOf"][idx] = self.__process_data(source["oneOf"][idx]) if "anyOf" in source: for idx, data in enumerate(list(source["anyOf"])): - out["anyOf"][idx] = self.__process(source["anyOf"][idx]) + out["anyOf"][idx] = self.__process_data(source["anyOf"][idx]) if "allOf" in source: for idx, data in enumerate(list(source["allOf"])): - out["allOf"][idx] = self.__process(source["allOf"][idx]) + out["allOf"][idx] = self.__process_data(source["allOf"][idx]) + + if "dependentSchemas" in source and isinstance( + source["dependentSchemas"], dict + ): + for k, v in source["dependentSchemas"].items(): + out["dependentSchemas"][k] = self.__process_data(v) + + for keyword in ["if", "then", "else"]: + if keyword in source: + out[keyword] = self.__process_data(source[keyword]) if "codelist" in source and ( "openCodelist" not in source or not source["openCodelist"] diff --git a/tests/fixtures/simple/file-dependentSchemas.json b/tests/fixtures/simple/file-dependentSchemas.json new file mode 100644 index 0000000..e72c20d --- /dev/null +++ b/tests/fixtures/simple/file-dependentSchemas.json @@ -0,0 +1,19 @@ +{ + "type": "object", + "properties": { + "credit_card": { + "type": "number" + } + }, + "dependentSchemas": { + "credit_card": { + "properties": { + "address": { + "$ref": "file-definitions.json#/definition/address", + "title": "Credit card number (with optional address elements)" + } + }, + "required": ["address"] + } + } +} diff --git a/tests/fixtures/simple/file-if-then-else.json b/tests/fixtures/simple/file-if-then-else.json new file mode 100644 index 0000000..7acadf7 --- /dev/null +++ b/tests/fixtures/simple/file-if-then-else.json @@ -0,0 +1,33 @@ +{ + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "credit_card": { + "type": "string" + } + }, + "if": { + "properties": { + "credit_card": { + "pattern": "[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]" + } + } + }, + "then": { + "properties": { + "address": { + "$ref": "file-definitions.json#/definition/address", + "title": "Must have an address with a credit card" + } + }, + "required": ["address"] + }, + "else": { + "properties": { + "cash_on_delivery": { "const": "Yup" } + }, + "required": ["cash_on_delivery"] + } +} diff --git a/tests/test_simple.py b/tests/test_simple.py index a769d60..6baef96 100644 --- a/tests/test_simple.py +++ b/tests/test_simple.py @@ -141,3 +141,39 @@ def test_file_list_allof(): def test_passing_empty_schema_is_ok(): ctjs = CompileToJsonSchema(input_schema={}) assert "{}" == ctjs.get_as_string() + + +def test_file_dependentSchemas(): + + input_filename = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "fixtures", + "simple", + "file-dependentSchemas.json", + ) + + ctjs = CompileToJsonSchema(input_filename=input_filename) + out = ctjs.get() + + assert ( + out["dependentSchemas"]["credit_card"]["properties"]["address"]["title"] + == "Credit card number (with optional address elements)" + ) + + +def test_file_if_then_else(): + + input_filename = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "fixtures", + "simple", + "file-if-then-else.json", + ) + + ctjs = CompileToJsonSchema(input_filename=input_filename) + out = ctjs.get() + + assert ( + out["then"]["properties"]["address"]["title"] + == "Must have an address with a credit card" + )