From 91eb25b76d12b7936583e7a3c482d5b3c066245e Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Fri, 30 Oct 2015 15:45:46 +0000 Subject: [PATCH] Include the full schema for an http API in the docs by resolving references to other files --- templating/matrix_templates/units.py | 66 +++++++++++++++++++++------- 1 file changed, 51 insertions(+), 15 deletions(-) diff --git a/templating/matrix_templates/units.py b/templating/matrix_templates/units.py index be113b9c..74aee26c 100644 --- a/templating/matrix_templates/units.py +++ b/templating/matrix_templates/units.py @@ -28,7 +28,25 @@ ROOM_EVENT = "core-event-schema/room_event.json" STATE_EVENT = "core-event-schema/state_event.json" -def get_json_schema_object_fields(obj, enforce_title=False): +def resolve_references(path, schema): + if isinstance(schema, dict): + result = {} + for key, value in schema.items(): + if key == "$ref": + path = os.path.join(os.path.dirname(path), value) + with open(path) as f: + schema = json.load(f) + return resolve_references(path, schema) + else: + result[key] = resolve_references(path, value) + return result + elif isinstance(schema, list): + return [resolve_references(path, value) for value in schema] + else: + return schema + + +def get_json_schema_object_fields(obj, enforce_title=False, include_parents=False): # Algorithm: # f.e. property => add field info (if field is object then recurse) if obj.get("type") != "object": @@ -36,9 +54,9 @@ def get_json_schema_object_fields(obj, enforce_title=False): "get_json_schema_object_fields: Object %s isn't an object." % obj ) if enforce_title and not obj.get("title"): - raise Exception( - "get_json_schema_object_fields: Nested object %s doesn't have a title." % obj - ) + # Force a default titile of "NO_TITLE" to make it obvious in the + # specification output which parts of the schema are missing a title + obj["title"] = 'NO_TITLE' required_keys = obj.get("required") if not required_keys: @@ -73,9 +91,15 @@ def get_json_schema_object_fields(obj, enforce_title=False): "Object %s has no properties or parents." % obj ) if not props: # parents only + if include_parents: + if obj["title"] == "NO_TITLE" and parents[0].get("title"): + obj["title"] = parents[0].get("title") + props = parents[0].get("properties") + + if not props: return [{ "title": obj["title"], - "parent": parents[0]["$ref"], + "parent": parents[0].get("$ref"), "no-table": True }] @@ -91,7 +115,8 @@ def get_json_schema_object_fields(obj, enforce_title=False): if prop_val == "object": nested_object = get_json_schema_object_fields( props[key_name]["additionalProperties"], - enforce_title=True + enforce_title=True, + include_parents=include_parents, ) key = props[key_name]["additionalProperties"].get( "x-pattern", "string" @@ -103,8 +128,9 @@ def get_json_schema_object_fields(obj, enforce_title=False): value_type = "{string: %s}" % prop_val else: nested_object = get_json_schema_object_fields( - props[key_name], - enforce_title=True + props[key_name], + enforce_title=True, + include_parents=include_parents, ) value_type = "{%s}" % nested_object[0]["title"] @@ -114,8 +140,9 @@ def get_json_schema_object_fields(obj, enforce_title=False): # if the items of the array are objects then recurse if props[key_name]["items"]["type"] == "object": nested_object = get_json_schema_object_fields( - props[key_name]["items"], - enforce_title=True + props[key_name]["items"], + enforce_title=True, + include_parents=include_parents, ) value_type = "[%s]" % nested_object[0]["title"] tables += nested_object @@ -159,7 +186,7 @@ def get_json_schema_object_fields(obj, enforce_title=False): class MatrixUnits(Units): - def _load_swagger_meta(self, api, group_name): + def _load_swagger_meta(self, filepath, api, group_name): endpoints = [] for path in api["paths"]: for method in api["paths"][path]: @@ -262,7 +289,10 @@ class MatrixUnits(Units): if is_array_of_objects: req_obj = req_obj["items"] - req_tables = get_json_schema_object_fields(req_obj) + req_tables = get_json_schema_object_fields( + resolve_references(filepath, req_obj), + include_parents=True, + ) if req_tables > 1: for table in req_tables[1:]: @@ -379,7 +409,10 @@ class MatrixUnits(Units): elif res_type and Units.prop(good_response, "schema/properties"): # response is an object: schema = good_response["schema"] - res_tables = get_json_schema_object_fields(schema) + res_tables = get_json_schema_object_fields( + resolve_references(filepath, schema), + include_parents=True, + ) for table in res_tables: if "no-table" not in table: endpoint["res_tables"].append(table) @@ -445,13 +478,16 @@ class MatrixUnits(Units): if not filename.endswith(".yaml"): continue self.log("Reading swagger API: %s" % filename) - with open(os.path.join(path, filename), "r") as f: + filepath = os.path.join(path, filename) + with open(filepath, "r") as f: # strip .yaml group_name = filename[:-5].replace("-", "_") if is_v2: group_name = "v2_" + group_name api = yaml.load(f.read()) - api["__meta"] = self._load_swagger_meta(api, group_name) + api["__meta"] = self._load_swagger_meta( + filepath, api, group_name + ) apis[group_name] = api return apis