def read_event_schema(self, filepath): logger.info("Reading %s" % filepath) with open(filepath, "r", encoding="utf-8") as f: json_schema = yaml.load(f, OrderedLoader) schema = { # one of "Message Event" or "State Event" "typeof": "", "typeof_info": "", # event type, eg "m.room.member". Note *not* the type of the # event object (which should always be 'object'). "type": None, "title": None, "desc": None, "msgtype": None, "content_fields": [ # <TypeTable> ] } # before we resolve the references, see if the first reference is to # the message event or state event schemas, and add typeof info if so. base_defs = { ROOM_EVENT: "Message Event", STATE_EVENT: "State Event" } if type(json_schema.get("allOf")) == list: firstRef = json_schema["allOf"][0]["$ref"] if firstRef in base_defs: schema["typeof"] = base_defs[firstRef] json_schema = resolve_references(filepath, json_schema) # add type schema["type"] = Units.prop( json_schema, "properties/type/enum" )[0] # add summary and desc schema["title"] = json_schema.get("title") schema["desc"] = json_schema.get("description", "") # walk the object for field info schema["content_fields"] = get_tables_for_schema( Units.prop(json_schema, "properties/content") ) # grab msgtype if it is the right kind of event msgtype = Units.prop( json_schema, "properties/content/properties/msgtype/enum" ) if msgtype: schema["msgtype"] = msgtype[0] # enum prop # link to msgtypes for m.room.message if schema["type"] == "m.room.message" and not msgtype: schema["desc"] += ( " For more information on ``msgtypes``, see "+ "`m.room.message msgtypes`_." ) # Assign state key info if it has some if schema["typeof"] == "State Event": skey_desc = Units.prop( json_schema, "properties/state_key/description" ) if not skey_desc: raise Exception("Missing description for state_key") schema["typeof_info"] = "``state_key``: %s" % skey_desc return schema
def read_event_schema(self, filepath): logger.info("Reading %s" % filepath) with open(filepath, "r") as f: json_schema = yaml.load(f, OrderedLoader) schema = { "typeof": "", "typeof_info": "", "type": None, "title": None, "desc": None, "msgtype": None, "content_fields": [ # { # title: "<title> key" # rows: [ # { key: <key_name>, type: <string>, # desc: <desc>, required: <bool> } # ] # } ] } # add typeof base_defs = { ROOM_EVENT: "Message Event", STATE_EVENT: "State Event" } if type(json_schema.get("allOf")) == list: firstRef = json_schema["allOf"][0]["$ref"] if firstRef in base_defs: schema["typeof"] = base_defs[firstRef] json_schema = resolve_references(filepath, json_schema) # add type schema["type"] = Units.prop( json_schema, "properties/type/enum" )[0] # add summary and desc schema["title"] = json_schema.get("title") schema["desc"] = json_schema.get("description", "") # walk the object for field info schema["content_fields"] = get_tables_for_schema( Units.prop(json_schema, "properties/content") ) # This is horrible because we're special casing a key on m.room.member. # We need to do this because we want to document a non-content object. if schema["type"] == "m.room.member": invite_room_state = get_tables_for_schema( json_schema["properties"]["invite_room_state"]["items"], ) schema["content_fields"].extend(invite_room_state) # grab msgtype if it is the right kind of event msgtype = Units.prop( json_schema, "properties/content/properties/msgtype/enum" ) if msgtype: schema["msgtype"] = msgtype[0] # enum prop # link to msgtypes for m.room.message if schema["type"] == "m.room.message" and not msgtype: schema["desc"] += ( " For more information on ``msgtypes``, see "+ "`m.room.message msgtypes`_." ) # Assign state key info if it has some if schema["typeof"] == "State Event": skey_desc = Units.prop( json_schema, "properties/state_key/description" ) if not skey_desc: raise Exception("Missing description for state_key") schema["typeof_info"] = "``state_key``: %s" % skey_desc return schema
def load_event_schemas(self): path = EVENT_SCHEMA schemata = {} for filename in os.listdir(path): if not filename.startswith("m."): continue filepath = os.path.join(path, filename) self.log("Reading %s" % filepath) with open(filepath, "r") as f: json_schema = yaml.load(f) schema = { "typeof": None, "typeof_info": "", "type": None, "title": None, "desc": None, "msgtype": None, "content_fields": [ # { # title: "<title> key" # rows: [ # { key: <key_name>, type: <string>, # desc: <desc>, required: <bool> } # ] # } ] } # add typeof base_defs = { ROOM_EVENT: "Message Event", STATE_EVENT: "State Event" } if type(json_schema.get("allOf")) == list: schema["typeof"] = base_defs.get( json_schema["allOf"][0].get("$ref")) elif json_schema.get("title"): schema["typeof"] = json_schema["title"] json_schema = resolve_references(filepath, json_schema) # add type schema["type"] = Units.prop(json_schema, "properties/type/enum")[0] # add summary and desc schema["title"] = json_schema.get("title") schema["desc"] = json_schema.get("description", "") # walk the object for field info schema["content_fields"] = get_tables_for_schema( Units.prop(json_schema, "properties/content")) # This is horrible because we're special casing a key on m.room.member. # We need to do this because we want to document a non-content object. if schema["type"] == "m.room.member": invite_room_state = get_tables_for_schema( json_schema["properties"]["invite_room_state"] ["items"], ) schema["content_fields"].extend(invite_room_state) # grab msgtype if it is the right kind of event msgtype = Units.prop( json_schema, "properties/content/properties/msgtype/enum") if msgtype: schema["msgtype"] = msgtype[0] # enum prop # link to msgtypes for m.room.message if schema["type"] == "m.room.message" and not msgtype: schema["desc"] += ( " For more information on ``msgtypes``, see " + "`m.room.message msgtypes`_.") # Assign state key info if it has some if schema["typeof"] == "State Event": skey_desc = Units.prop(json_schema, "properties/state_key/description") if not skey_desc: raise Exception("Missing description for state_key") schema["typeof_info"] = "``state_key``: %s" % skey_desc schemata[filename] = schema return schemata
def _load_swagger_meta(self, api, group_name): endpoints = [] for path in api["paths"]: for method in api["paths"][path]: single_api = api["paths"][path][method] full_path = api.get("basePath", "").rstrip("/") + path endpoint = { "title": single_api.get("summary", ""), "deprecated": single_api.get("deprecated", False), "desc": single_api.get("description", single_api.get("summary", "")), "method": method.upper(), "path": full_path.strip(), "requires_auth": "security" in single_api, "rate_limited": 429 in single_api.get("responses", {}), "req_param_by_loc": {}, "req_body_tables": [], "res_tables": [], "example": { "req": "", "responses": [], "good_response": "" } } self.log(" ------- Endpoint: %s %s ------- " % (method, path)) for param in single_api.get("parameters", []): param_loc = param["in"] if param_loc == "body": self._handle_body_param(param, endpoint) continue param_name = param["name"] # description desc = param.get("description", "") if param.get("required"): desc = "**Required.** " + desc # assign value expected for this param val_type = param.get("type") # integer/string if param.get("enum"): val_type = "enum" desc += (" One of: %s" % json.dumps(param.get("enum"))) endpoint["req_param_by_loc"].setdefault(param_loc, []).append({ "key": param_name, "type": val_type, "desc": desc }) # endfor[param] good_response = None for code, res in single_api.get("responses", {}).items(): if not good_response and code == 200: good_response = res description = res.get("description", "") example = res.get("examples", {}).get("application/json", "") if description and example: endpoint["example"]["responses"].append({ "code": code, "description": description, "example": example, }) # form example request if it has one. It "has one" if all params # have either "x-example" or a "schema" with an "example". params_missing_examples = [ p for p in single_api.get("parameters", []) if ("x-example" not in p and not Units.prop(p, "schema/example")) ] if len(params_missing_examples) == 0: path_template = api.get("basePath", "").rstrip("/") + path qps = [] body = "" for param in single_api.get("parameters", []): if param["in"] == "path": path_template = path_template.replace( "{%s}" % param["name"], urllib.quote(param["x-example"])) elif param["in"] == "body": body = param["schema"]["example"] elif param["in"] == "query": example = param["x-example"] if type(example) == list: for value in example: qps.append((param["name"], value)) else: qps.append((param["name"], example)) query_string = "" if len( qps) == 0 else "?" + urllib.urlencode(qps) if body: endpoint["example"][ "req"] = "%s %s%s HTTP/1.1\nContent-Type: application/json\n\n%s" % ( method.upper(), path_template, query_string, body) else: endpoint["example"]["req"] = "%s %s%s HTTP/1.1\n\n" % ( method.upper(), path_template, query_string) else: self.log( "The following parameters are missing examples :( \n %s" % [p["name"] for p in params_missing_examples]) # add response params if this API has any. if good_response: self.log("Found a 200 response for this API") res_type = Units.prop(good_response, "schema/type") res_name = Units.prop(good_response, "schema/name") if res_type and res_type not in ["object", "array"]: # response is a raw string or something like that good_table = { "title": None, "rows": [{ "key": "<" + res_type + ">" if not res_name else res_name, "type": res_type, "desc": res.get("description", ""), "req_str": "" }] } if good_response.get("headers"): for (header_name, header ) in good_response.get("headers").iteritems(): good_table["rows"].append({ "key": header_name, "type": "Header<" + header["type"] + ">", "desc": header["description"], "req_str": "" }) endpoint["res_tables"].append(good_table) elif res_type and Units.prop(good_response, "schema/properties"): # response is an object: schema = good_response["schema"] res_tables = get_tables_for_schema( schema, mark_required=False, ) endpoint["res_tables"].extend(res_tables) elif res_type and Units.prop(good_response, "schema/items"): # response is an array: # FIXME: Doesn't recurse at all. schema = good_response["schema"] array_type = Units.prop(schema, "items/type") if Units.prop(schema, "items/allOf"): array_type = (Units.prop(schema, "items/title")) endpoint["res_tables"].append({ "title": schema.get("title", ""), "rows": [{ "key": "N/A", "type": ("[%s]" % array_type), "desc": schema.get("description", ""), "req_str": "" }] }) for response_table in endpoint["res_tables"]: self.log("Response: %s" % response_table["title"]) for r in response_table["rows"]: self.log("Row: %s" % r) if len(endpoint["res_tables"]) == 0: self.log( "This API appears to have no response table. Are you " + "sure this API returns no parameters?") endpoints.append(endpoint) return { "base": api.get("basePath").rstrip("/"), "group": group_name, "endpoints": endpoints, }
def _load_swagger_meta(self, api, group_name): endpoints = [] for path in api["paths"]: for method in api["paths"][path]: single_api = api["paths"][path][method] full_path = api.get("basePath", "").rstrip("/") + path endpoint = { "title": single_api.get("summary", ""), "deprecated": single_api.get("deprecated", False), "desc": single_api.get("description", single_api.get("summary", "")), "method": method.upper(), "path": full_path.strip(), "requires_auth": "security" in single_api, "rate_limited": 429 in single_api.get("responses", {}), "req_param_by_loc": {}, "req_body_tables": [], "res_tables": [], "responses": [], "example": { "req": "", } } self.log(" ------- Endpoint: %s %s ------- " % (method, path)) for param in single_api.get("parameters", []): param_loc = param["in"] if param_loc == "body": self._handle_body_param(param, endpoint) continue param_name = param["name"] # description desc = param.get("description", "") if param.get("required"): desc = "**Required.** " + desc # assign value expected for this param val_type = param.get("type") # integer/string if param.get("enum"): val_type = "enum" desc += ( " One of: %s" % json.dumps(param.get("enum")) ) endpoint["req_param_by_loc"].setdefault(param_loc, []).append({ "key": param_name, "type": val_type, "desc": desc }) # endfor[param] good_response = None for code in sorted(single_api.get("responses", {}).keys()): res = single_api["responses"][code] if not good_response and code == 200: good_response = res description = res.get("description", "") example = res.get("examples", {}).get("application/json", "") endpoint["responses"].append({ "code": code, "description": description, "example": example, }) path_template = api.get("basePath", "").rstrip("/") + path qps = [] body = "" for param in single_api.get("parameters", []): try: example = get_example_for_param(param) if not example: self.log( "The parameter %s is missing an example." % param["name"]) continue if param["in"] == "path": path_template = path_template.replace( "{%s}" % param["name"], urllib.quote(example) ) elif param["in"] == "body": body = example elif param["in"] == "query": if type(example) == list: for value in example: qps.append((param["name"], value)) else: qps.append((param["name"], example)) except Exception, e: raise Exception("Error handling parameter %s" % param["name"], e) query_string = "" if len(qps) == 0 else "?"+urllib.urlencode(qps) if body: endpoint["example"]["req"] = "%s %s%s HTTP/1.1\nContent-Type: application/json\n\n%s" % ( method.upper(), path_template, query_string, body ) else: endpoint["example"]["req"] = "%s %s%s HTTP/1.1\n\n" % ( method.upper(), path_template, query_string ) # add response params if this API has any. if good_response: self.log("Found a 200 response for this API") res_type = Units.prop(good_response, "schema/type") res_name = Units.prop(good_response, "schema/name") if res_type and res_type not in ["object", "array"]: # response is a raw string or something like that good_table = { "title": None, "rows": [{ "key": "<" + res_type + ">" if not res_name else res_name, "type": res_type, "desc": res.get("description", ""), "req_str": "" }] } if good_response.get("headers"): for (header_name, header) in good_response.get("headers").iteritems(): good_table["rows"].append({ "key": header_name, "type": "Header<" + header["type"] + ">", "desc": header["description"], "req_str": "" }) endpoint["res_tables"].append(good_table) elif res_type and Units.prop(good_response, "schema/properties"): # response is an object: schema = good_response["schema"] res_tables = get_tables_for_schema(schema, mark_required=False, ) endpoint["res_tables"].extend(res_tables) elif res_type and Units.prop(good_response, "schema/items"): # response is an array: # FIXME: Doesn't recurse at all. schema = good_response["schema"] array_type = Units.prop(schema, "items/type") if Units.prop(schema, "items/allOf"): array_type = ( Units.prop(schema, "items/title") ) endpoint["res_tables"].append({ "title": schema.get("title", ""), "rows": [{ "key": "N/A", "type": ("[%s]" % array_type), "desc": schema.get("description", ""), "req_str": "" }] }) for response_table in endpoint["res_tables"]: self.log("Response: %s" % response_table["title"]) for r in response_table["rows"]: self.log("Row: %s" % r) if len(endpoint["res_tables"]) == 0: self.log( "This API appears to have no response table. Are you " + "sure this API returns no parameters?" ) endpoints.append(endpoint)