diff --git a/tools/check-schemas b/tools/check-schemas index b0a4162e47..c8debc4da4 100755 --- a/tools/check-schemas +++ b/tools/check-schemas @@ -10,7 +10,6 @@ # We compare the Python and OpenAPI schemas by converting the OpenAPI data # into the event_schema style of types and the diffing the schemas. import argparse -import difflib import os import subprocess import sys @@ -53,36 +52,7 @@ os.environ["DJANGO_SETTINGS_MODULE"] = "zproject.test_settings" django.setup() from zerver.lib import event_schema -from zerver.lib.data_types import ( - DictType, - EnumType, - ListType, - NumberType, - StringDictType, - UnionType, - make_checker, - schema, -) -from zerver.openapi.openapi import openapi_spec - -# This list of exemptions represents details we should fix in Zulip's -# API structure and/or validators. -EXEMPT_OPENAPI_NAMES = [ - # Additional keys(push_users_notify) due to bug in API. - "message_event", - # tuple handling - "muted_topics_event", - # bots, delivery_email, profile_data - "realm_user_add_event", - # OpenAPI is incomplete - "realm_update_dict_event", -] - -# This is a list of events still documented in the OpenAPI that -# are deprecated and no longer checked in event_schema.py. -DEPRECATED_EVENTS = [ - "realm_filters_event", -] +from zerver.lib.data_types import make_checker def get_event_checker(event: dict[str, Any]) -> Callable[[str, dict[str, Any]], None] | None: @@ -133,100 +103,11 @@ def verify_fixtures_are_sorted(names: list[str]) -> None: ) -def from_openapi(node: dict[str, Any]) -> Any: - """Converts the OpenAPI data into event_schema.py style type - definitions for convenient comparison with the types used for backend - tests declared there.""" - if "oneOf" in node: - return UnionType([from_openapi(n) for n in node["oneOf"]]) - - if node["type"] == "object": - if ( - "additionalProperties" in node - # this might be a glitch in our current spec? or - # maybe I just understand it yet - and isinstance(node["additionalProperties"], dict) - ): - return StringDictType(from_openapi(node["additionalProperties"])) - - if "properties" not in node: - return dict - - required_keys = [] - for key, sub_node in node["properties"].items(): - required_keys.append((key, from_openapi(sub_node))) - return DictType(required_keys) - - if node["type"] == "boolean": - return bool - - if node["type"] == "integer": - if "enum" in node: - return EnumType(node["enum"]) - return int - - if node["type"] == "number": - return NumberType() - - if node["type"] == "string": - if "enum" in node: - return EnumType(node["enum"]) - return str - - if node["type"] == "array": - return ListType(from_openapi(node["items"])) - - raise AssertionError("cannot handle node") - - -def validate_openapi_against_event_schema() -> None: - node = openapi_spec.openapi()["paths"]["/events"]["get"]["responses"]["200"]["content"][ - "application/json" - ]["schema"]["properties"]["events"]["items"]["oneOf"] - - for sub_node in node: - name = sub_node["properties"]["type"]["enum"][0] - if "op" in sub_node["properties"]: - name += "_" + sub_node["properties"]["op"]["enum"][0] - - name += "_event" - - if not hasattr(event_schema, name): - if name not in DEPRECATED_EVENTS: - print("WARNING - NEED SCHEMA to match OpenAPI", name) - continue - - openapi_type = from_openapi(sub_node) - openapi_schema = schema(name, openapi_type) - - py_type = getattr(event_schema, name) - py_schema = schema(name, py_type) - - if name in EXEMPT_OPENAPI_NAMES: - if openapi_schema == py_schema: - raise AssertionError(f"unnecessary exemption for {name}") - continue - - if openapi_schema != py_schema: - print(f"py\n{py_schema}\n") - print(f"openapi\n{openapi_schema}\n") - - for line in difflib.unified_diff( - py_schema.split("\n"), - openapi_schema.split("\n"), - fromfile="py", - tofile="openapi", - ): - print(line) - raise AssertionError("openapi schemas disagree") - - def run() -> None: fixtures = read_fixtures() verify_fixtures_are_sorted(list(fixtures.keys())) for name, event in fixtures.items(): check_event(name, event) - validate_openapi_against_event_schema() print("Successful check. All tests passed.")