From 4a3f142e2929da2e846b5f39e00d735b3d2eb3b6 Mon Sep 17 00:00:00 2001 From: Brandon Wilson Date: Tue, 9 Jul 2024 10:15:56 -0400 Subject: [PATCH] Allow unknown shapes/layers (#47) * Unknown layers and shapes will no longer fail spec * Add test files for unknown shape + layer * Dynamically generate unknown layers/shape list * Uncomment out adding unknown objects * WIP - Add type enums to existing 'unkown' object * Update unknown object generation and validation --- schema/layers/all-layers.json | 3 ++ schema/layers/unknown-layer.json | 14 +++++ schema/shapes/all-graphic-elements.json | 3 +- schema/shapes/unknown-shape.json | 14 +++++ tests/animations/valid/unknown-layer.json | 25 +++++++++ tests/animations/valid/unknown-shape.json | 63 +++++++++++++++++++++++ tools/schema-merge.py | 26 ++++++++++ tools/schema-validate.py | 10 +++- 8 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 schema/layers/unknown-layer.json create mode 100644 schema/shapes/unknown-shape.json create mode 100644 tests/animations/valid/unknown-layer.json create mode 100644 tests/animations/valid/unknown-shape.json diff --git a/schema/layers/all-layers.json b/schema/layers/all-layers.json index 1b1e520..0064899 100644 --- a/schema/layers/all-layers.json +++ b/schema/layers/all-layers.json @@ -15,6 +15,9 @@ }, { "$ref": "#/$defs/layers/shape-layer" + }, + { + "$ref": "#/$defs/layers/unknown-layer" } ] } diff --git a/schema/layers/unknown-layer.json b/schema/layers/unknown-layer.json new file mode 100644 index 0000000..2efbe78 --- /dev/null +++ b/schema/layers/unknown-layer.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "title": "Unknown layer types", + "description": "Unknown layer types. Types not defined by the specification are still allowed.", + "properties": { + "ty": { + "not": { + "$comment": "enum list is dynamically generated", + "enum": [] + } + } + } +} diff --git a/schema/shapes/all-graphic-elements.json b/schema/shapes/all-graphic-elements.json index 752255a..b399038 100644 --- a/schema/shapes/all-graphic-elements.json +++ b/schema/shapes/all-graphic-elements.json @@ -12,6 +12,7 @@ {"$ref": "#/$defs/shapes/rectangle"}, {"$ref": "#/$defs/shapes/stroke"}, {"$ref": "#/$defs/shapes/transform"}, - {"$ref": "#/$defs/shapes/trim-path"} + {"$ref": "#/$defs/shapes/trim-path"}, + {"$ref": "#/$defs/shapes/unknown-shape"} ] } diff --git a/schema/shapes/unknown-shape.json b/schema/shapes/unknown-shape.json new file mode 100644 index 0000000..25c3864 --- /dev/null +++ b/schema/shapes/unknown-shape.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "title": "Unknown shape types", + "description": "Unknown shape types. Types not defined by the specification are still allowed.", + "properties": { + "ty": { + "not": { + "$comment": "enum list is dynamically generated", + "enum": [] + } + } + } +} diff --git a/tests/animations/valid/unknown-layer.json b/tests/animations/valid/unknown-layer.json new file mode 100644 index 0000000..c20763f --- /dev/null +++ b/tests/animations/valid/unknown-layer.json @@ -0,0 +1,25 @@ +{ + "v": "5.5.7", + "ip": 0, + "op": 180, + "nm": "Animation", + "mn": "{8f1618e3-6f83-4531-8f65-07dd4b68ee2e}", + "fr": 60, + "w": 512, + "h": 512, + "assets": [ + ], + "layers": [ + { + "ddd": 0, + "ty": 137, + "ind": 0, + "st": 0, + "ip": 0, + "op": 180, + "nm": "Unknown layer type" + } + ] +} + + diff --git a/tests/animations/valid/unknown-shape.json b/tests/animations/valid/unknown-shape.json new file mode 100644 index 0000000..2135442 --- /dev/null +++ b/tests/animations/valid/unknown-shape.json @@ -0,0 +1,63 @@ +{ + "v": "5.5.7", + "ip": 0, + "op": 180, + "nm": "Animation", + "mn": "{8f1618e3-6f83-4531-8f65-07dd4b68ee2e}", + "fr": 60, + "w": 512, + "h": 512, + "assets": [ + ], + "layers": [ + { + "ddd": 0, + "ty": 4, + "ind": 0, + "st": 0, + "ip": 0, + "op": 180, + "nm": "Shape Layer", + "mn": "{85f37d8b-1792-4a4f-82d2-1b3b6d829c07}", + "ks": { + "a": { + "a": 0, + "k": [ + 256, + 256 + ] + }, + "p": { + "a": 0, + "k": [ + 256, + 256 + ] + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ] + }, + "r": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100 + } + }, + "shapes": [ + { + "ty": "unknown", + "nm": "Unknown Shape" + } + ] + } + ] +} + + diff --git a/tools/schema-merge.py b/tools/schema-merge.py index b44a813..7900c1b 100755 --- a/tools/schema-merge.py +++ b/tools/schema-merge.py @@ -4,6 +4,8 @@ import pathlib import argparse +from schema_tools.schema import SchemaPath, Schema +from schema_tools import type_info def join_parts( json_data: dict, @@ -30,7 +32,19 @@ def join_parts( return json_data +def add_vals_to_unknown_object( + objects, + unknown_type_dict: dict +): + types = [] + for ele in objects.concrete: + type = ele.properties['ty'].const + if type is not None: + types.append(type) + + unknown_type_dict["properties"]["ty"]["not"]["enum"] = types + root = pathlib.Path(__file__).absolute().parent.parent parser = argparse.ArgumentParser(description="Joins JSON schema in a single file") @@ -63,6 +77,18 @@ def join_parts( join_parts(json_data, input_dir, root_path) +schema = Schema(json_data) +ts = type_info.TypeSystem(schema) + +add_vals_to_unknown_object( + ts.from_path(SchemaPath("#/$defs/layers/all-layers")), + json_data["$defs"]["layers"]["unknown-layer"] +) +add_vals_to_unknown_object( + ts.from_path(SchemaPath("#/$defs/shapes/all-graphic-elements")), + json_data["$defs"]["shapes"]["unknown-shape"] +) + os.makedirs(output_path.parent, exist_ok=True) with open(output_path, "w") as file: diff --git a/tools/schema-validate.py b/tools/schema-validate.py index 7bc6f55..3d55f88 100755 --- a/tools/schema-validate.py +++ b/tools/schema-validate.py @@ -8,6 +8,13 @@ from schema_tools.schema import SchemaPath, Schema import lottie_markdown +# By default, tool expects a link for all schema files. +# This is generally true, but may not always be the case +unneededLinks = [ + ("shapes","base-gradient"), + ("layers","unknown-layer"), + ("shapes","unknown-shape") +] class Validator: def __init__(self): @@ -65,7 +72,8 @@ def check_links(self, html_path: pathlib.Path): file_cache = {} ts = lottie_markdown.typed_schema(self.root) - checked.add(("shapes","base-gradient")) + for link in unneededLinks: + checked.add(link) for ref in self.expected_refs: link = ts.from_path(ref).link