mirror of https://github.com/OISF/suricata
evedoc.py: script to generate rst doc from eve schema
Also supports a "--flat" command line option to produce a "dot" separated version of all the fields in the EVE schema.pull/11895/head
parent
2b16369071
commit
2626895a93
@ -0,0 +1,211 @@
|
||||
#! /usr/bin/env python3
|
||||
#
|
||||
# Generate Sphinx documentation from JSON schema
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import json
|
||||
|
||||
|
||||
def errprint(*args, **kwargs):
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
|
||||
|
||||
def find_ref(schema: dict, ref: str) -> dict:
|
||||
parts = ref.split("/")
|
||||
|
||||
root = parts.pop(0)
|
||||
if root != "#":
|
||||
raise Exception("Unsupported reference: {}".format(ref))
|
||||
|
||||
while parts:
|
||||
schema = schema[parts.pop(0)]
|
||||
|
||||
return schema
|
||||
|
||||
|
||||
def get_type(props: dict, name: str) -> str:
|
||||
prop_type = props["type"]
|
||||
if prop_type == "array":
|
||||
try:
|
||||
array_type = props["items"]["type"]
|
||||
except KeyError:
|
||||
errprint("warning: array property without items: {}".format(name))
|
||||
array_type = "unknown"
|
||||
prop_type = "array of {}s".format(array_type)
|
||||
return prop_type
|
||||
|
||||
|
||||
def render_flat(schema: dict):
|
||||
stack = [(schema, [])]
|
||||
|
||||
while stack:
|
||||
(current, path) = stack.pop(0)
|
||||
|
||||
for name, props in current["properties"].items():
|
||||
if "$ref" in props:
|
||||
ref = find_ref(schema, props["$ref"])
|
||||
if not ref:
|
||||
raise Exception("$ref not found: {}".format(props["$ref"]))
|
||||
props = ref
|
||||
if props["type"] in ["string", "integer", "boolean", "number"]:
|
||||
# End of the line...
|
||||
print("{}: {}".format(".".join(path + [name]), props["type"]))
|
||||
elif props["type"] == "object":
|
||||
print("{}: object".format(".".join(path + [name])))
|
||||
if "properties" in props:
|
||||
stack.insert(0, (props, path + [name]))
|
||||
else:
|
||||
errprint(
|
||||
"warning: object without properties: {}".format(
|
||||
".".join(path + [name])
|
||||
)
|
||||
)
|
||||
elif props["type"] == "array":
|
||||
if "items" in props and "type" in props["items"]:
|
||||
print(
|
||||
"{}: {}[]".format(
|
||||
".".join(path + [name]), props["items"]["type"]
|
||||
)
|
||||
)
|
||||
if "properties" in props["items"]:
|
||||
stack.insert(
|
||||
0,
|
||||
(
|
||||
props["items"],
|
||||
path + ["{}[]".format(name)],
|
||||
),
|
||||
)
|
||||
else:
|
||||
errprint(
|
||||
"warning: undocumented array: {}".format(
|
||||
".".join(path + [name])
|
||||
)
|
||||
)
|
||||
print("{}: array".format(".".join(path + [name])))
|
||||
else:
|
||||
raise Exception("Unsupported type: {}".format(props["type"]))
|
||||
|
||||
|
||||
def render_rst(schema: dict):
|
||||
stack = [(schema, [], "object")]
|
||||
|
||||
while stack:
|
||||
(current, path, type) = stack.pop(0)
|
||||
|
||||
items = []
|
||||
|
||||
for name, props in current["properties"].items():
|
||||
if "$ref" in props:
|
||||
ref = find_ref(schema, props["$ref"])
|
||||
if not ref:
|
||||
raise Exception(
|
||||
"Reference not found: {}".format(props["$ref"])
|
||||
)
|
||||
props = ref
|
||||
prop_type = get_type(props, name)
|
||||
description = props.get("description", "")
|
||||
|
||||
items.append(
|
||||
{"name": name, "type": prop_type, "description": description}
|
||||
)
|
||||
|
||||
if props["type"] == "object" and "properties" in props:
|
||||
stack.insert(0, (props, path + [name], "object"))
|
||||
elif (
|
||||
props["type"] == "array"
|
||||
and "items" in props
|
||||
and "properties" in props["items"]
|
||||
):
|
||||
array_type = props["items"]["type"]
|
||||
stack.insert(
|
||||
0,
|
||||
(
|
||||
props["items"],
|
||||
path + ["{}".format(name)],
|
||||
"array of {}s".format(array_type),
|
||||
),
|
||||
)
|
||||
|
||||
render_rst_table(items, path, type)
|
||||
|
||||
|
||||
def render_rst_table(items: list, path: list, type: str):
|
||||
if not path:
|
||||
title = "Top Level"
|
||||
else:
|
||||
title = ".".join(path)
|
||||
title = "{} ({})".format(title, type)
|
||||
print(title)
|
||||
print("^" * len(title))
|
||||
|
||||
name_len = max([len(item["name"]) for item in items] + [len("Name")])
|
||||
desc_len = max(
|
||||
[len(item["description"]) for item in items] + [len("Description")]
|
||||
)
|
||||
type_len = max([len(item["type"]) for item in items])
|
||||
|
||||
print(".. table::")
|
||||
print(" :width: 100%")
|
||||
print(" :widths: 30 25 45")
|
||||
print("")
|
||||
|
||||
print(" {} {} {}".format("=" * name_len, "=" * type_len, "=" * desc_len))
|
||||
print(
|
||||
" {} {} {}".format(
|
||||
"Name".ljust(name_len),
|
||||
"Type".ljust(type_len),
|
||||
"Description".ljust(desc_len),
|
||||
)
|
||||
)
|
||||
print(" {} {} {}".format("=" * name_len, "=" * type_len, "=" * desc_len))
|
||||
for item in items:
|
||||
print(
|
||||
" {} {} {}".format(
|
||||
item["name"].ljust(name_len),
|
||||
item["type"].ljust(type_len),
|
||||
item["description"].ljust(desc_len),
|
||||
)
|
||||
)
|
||||
print(" {} {} {}".format("=" * name_len, "=" * type_len, "=" * desc_len))
|
||||
print("")
|
||||
|
||||
|
||||
epilog = """
|
||||
|
||||
By default, the EVE schema is rendered as Sphinx documentation. To
|
||||
create "flat" or "dot" separated output, use the --flat option.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate documentation from JSON schema",
|
||||
epilog=epilog,
|
||||
formatter_class=argparse.RawTextHelpFormatter,
|
||||
)
|
||||
parser.add_argument("--object", help="Object name")
|
||||
parser.add_argument("--output", help="Output file")
|
||||
parser.add_argument("--flat", help="Flatten output", action="store_true")
|
||||
parser.add_argument("filename", help="JSON schema file")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
root = json.load(open(args.filename))
|
||||
schema = root
|
||||
|
||||
if args.object:
|
||||
schema = schema["properties"][args.object]
|
||||
|
||||
if args.output:
|
||||
sys.stdout = open(args.output, "w")
|
||||
|
||||
if args.flat:
|
||||
render_flat(schema)
|
||||
else:
|
||||
render_rst(schema)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
Loading…
Reference in New Issue