From 371d7cba06f2a95c8b92429f98c735eb251efc15 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Sun, 18 May 2025 10:18:03 -0600 Subject: [PATCH] schema: add script to check or sort the schema --- scripts/schema-sort.py | 124 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100755 scripts/schema-sort.py diff --git a/scripts/schema-sort.py b/scripts/schema-sort.py new file mode 100755 index 0000000000..5ba83032de --- /dev/null +++ b/scripts/schema-sort.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +# +# Script to sort or just check that properties are in alphabetic order + + +import json +import sys +import argparse +from collections import OrderedDict + + +def sort_properties(obj, path=""): + """Recursively sort 'properties' keys in a JSON schema object.""" + if isinstance(obj, dict): + new_obj = OrderedDict() + + for key, value in obj.items(): + current_path = f"{path}.{key}" if path else key + + if key == "properties" and isinstance(value, dict): + sorted_properties = OrderedDict(sorted(value.items())) + new_obj[key] = sorted_properties + for prop_key, prop_value in sorted_properties.items(): + new_obj[key][prop_key] = sort_properties( + prop_value, f"{current_path}.{prop_key}" + ) + else: + new_obj[key] = sort_properties(value, current_path) + + return new_obj + + elif isinstance(obj, list): + return [sort_properties(item, f"{path}[{i}]") for i, item in enumerate(obj)] + + else: + return obj + + +def check_properties_sorted(obj, path=""): + """Check if all 'properties' keys have their contents sorted alphabetically.""" + errors = [] + + if isinstance(obj, dict): + for key, value in obj.items(): + current_path = f"{path}.{key}" if path else key + if key == "properties" and isinstance(value, dict): + keys_list = list(value.keys()) + sorted_keys = sorted(keys_list) + + if keys_list != sorted_keys: + errors.append(f"Properties not sorted at path: {current_path}") + errors.append(f" Current order: {keys_list}") + errors.append(f" Should be: {sorted_keys}") + + for prop_key, prop_value in value.items(): + errors.extend( + check_properties_sorted( + prop_value, f"{current_path}.{prop_key}" + ) + ) + else: + errors.extend(check_properties_sorted(value, current_path)) + + elif isinstance(obj, list): + for i, item in enumerate(obj): + errors.extend(check_properties_sorted(item, f"{path}[{i}]")) + + return errors + + +def main(): + parser = argparse.ArgumentParser( + description="Sort JSON schema properties alphabetically" + ) + parser.add_argument("schema_file", help="Path to the JSON schema file") + parser.add_argument( + "--check", + action="store_true", + help="Check if properties are sorted (exit 1 if not)", + ) + parser.add_argument( + "--in-place", + action="store_true", + help="Sort the file in place (only if not in check mode)", + ) + + args = parser.parse_args() + + try: + with open(args.schema_file, "r") as f: + schema = json.load(f, object_pairs_hook=OrderedDict) + except Exception as e: + print(f"Error reading schema file: {e}", file=sys.stderr) + sys.exit(1) + + if args.check: + errors = check_properties_sorted(schema) + + if errors: + print("Schema properties are not sorted!", file=sys.stderr) + for error in errors: + print(error, file=sys.stderr) + sys.exit(1) + else: + print("Schema properties are properly sorted.") + sys.exit(0) + else: + sorted_schema = sort_properties(schema) + + if args.in_place: + try: + with open(args.schema_file, "w") as f: + json.dump(sorted_schema, f, indent=4) + f.write("\n") + print(f"Sorted schema written to {args.schema_file}") + except Exception as e: + print(f"Error writing schema file: {e}", file=sys.stderr) + sys.exit(1) + else: + print(json.dumps(sorted_schema, indent=4)) + + +if __name__ == "__main__": + main()