Skip to content

Commit

Permalink
convert new entry frontend to react
Browse files Browse the repository at this point in the history
  • Loading branch information
boompig committed Aug 23, 2019
1 parent 8eed731 commit 4361765
Show file tree
Hide file tree
Showing 28 changed files with 3,241 additions and 525 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"csstype": "^2.6.0",
"jquery": "3.4.1",
"js-cookie": "2.1.4",
"lodash": "^4.17.15",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-tooltip": "^3.9.1",
Expand Down
9 changes: 9 additions & 0 deletions passzero/app_factory.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import binascii
import json
import os
from datetime import timedelta

Expand All @@ -19,6 +21,12 @@
from passzero.models import ApiToken, db


def dict_to_base64(d: dict) -> str:
# NOTE: internal use only
s = binascii.b2a_base64(json.dumps(d).encode('utf-8')).rstrip()
return s.decode('utf-8')


def create_app(name: str, settings_override: dict = {}):
compress = Compress()

Expand Down Expand Up @@ -91,6 +99,7 @@ def error_handler(e):

# register CSRF generation function
app.jinja_env.globals["csrf_token"] = generate_csrf_token
app.jinja_env.globals["to_base64"] = dict_to_base64

# create SSL secret keys
if "FLASK_SECRET_KEY" in os.environ:
Expand Down
4 changes: 2 additions & 2 deletions passzero/main_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def new_entry_view():
"default_random_password_length": user.default_random_password_length,
"default_random_passphrase_length": user.default_random_passphrase_length
}
return render_template("new.html", title="PassZero · New Entry",
return render_template("new.pug", title="PassZero · New Entry",
user_prefs=user_prefs, error=None)


Expand Down Expand Up @@ -209,7 +209,7 @@ def edit_entry(entry_id: int):
"default_random_passphrase_length": user.default_random_passphrase_length
}
return render_template(
"new.html",
"new.pug",
user_prefs=user_prefs,
e_id=entry_id,
entry=fe[0],
Expand Down
7 changes: 7 additions & 0 deletions scripts/api_doc_builder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# About

Automatically generate API docs for the API.

Start with running `create_api_docs.sh`. After this, copy over any documents that are different and look visually right.

I am not confident enough in the script to just clobber blindly.
Empty file.
185 changes: 185 additions & 0 deletions scripts/api_doc_builder/api_docs_from_swagger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
"""
Because flask-restplus has been successful at extracting route information, etc,
All I have to do is parse the description
"""

import logging
import re
from argparse import ArgumentParser
from pprint import pprint

import coloredlogs
import docutils
import yaml

from rst_utils import (get_bullet_list_children, get_bullet_list_item_text,
get_text_child, has_child_with_tag, parse_rst, get_section_title)


class ParsedDoc:
def __init__(self):
self.summary = None
self.parameters = []
self.response = []
self.status_codes = {}

def add_parameter(self, name: str, type: str, is_required: bool, subtypes=None):
if subtypes:
self.parameters.append({
"name": name,
"type": type,
"is_required": is_required,
"subtypes": subtypes
})
else:
self.parameters.append({
"name": name,
"type": type,
"is_required": is_required
})

def add_response(self, type: str, val: str, tag=None):
if tag:
self.response.append({
"type": type,
"val": val,
"tag": tag
})
else:
self.response.append({
"type": type,
"val": val
})

def add_status_code(self, status_code: int, description: str):
assert status_code not in self.status_codes
self.status_codes[status_code] = description


def parse_param(arg):
pattern = re.compile(r'(\w+): (\w+) (\(\w+\))?$')
m = pattern.match(arg)
try:
arg_name = m.group(1)
arg_type = m.group(2)
is_required = m.group(3) == "(required)"
return {
"name": arg_name,
"type": arg_type,
"is_required": is_required
}
except Exception as e:
print("Failed with this pattern:")
print(pattern)
print("Failed with this argument:")
print(arg)
print("Exception:")
raise e


def parse_rst_docstring_response(node, parsed_doc, tag=None):
l = [c for c in node.children if c.tagname != "title"]
for child in l:
if child.tagname == "paragraph":
parsed_doc.add_response(type="text", val=child.astext(), tag=tag)
elif child.tagname == "literal_block":
parsed_doc.add_response(type="code", val=child.astext(), tag=tag)
elif child.tagname == "section":
parse_rst_docstring_response(child, parsed_doc,
tag=get_section_title(child))
else:
print(child)
raise Exception


def parse_docstring_rst(docstring: str, http_method: str, route: str):
"""
The response list will contain unparsed RST tags
"""
doc = parse_rst(docstring)
parsed_doc = ParsedDoc()
logging.debug("Trying to parse [{method}] {route}".format(
method=http_method, route=route))
for i, section in enumerate(doc.children):
if section.tagname == "section":
# get the title
title = get_section_title(section)
if title == "Arguments":
if has_child_with_tag(section, "bullet_list"):
for child in get_bullet_list_children(section):
# figure out if there is a nested type
if has_child_with_tag(child, "bullet_list"):
l = []
for subchild in get_bullet_list_children(child):
arg = parse_param(
get_bullet_list_item_text(subchild)
)
d = { "name": arg["name"],
"type": arg["type"],
"is_required": arg["is_required"]
}
l.append(d)
arg = parse_param(get_bullet_list_item_text(child))
parsed_doc.add_parameter(
name=arg["name"],
type=arg["type"],
is_required=arg["is_required"],
subtypes=l
)
else:
arg = parse_param(child.astext())
parsed_doc.add_parameter(
name=arg["name"],
type=arg["type"],
is_required=arg["is_required"]
)
else:
logging.debug("ignoring arguments for [{method}] {route} because they are not in a bullet list".format(
method=http_method, route=route))
elif title == "Status codes":
for child in get_bullet_list_children(section):
arg = child.astext()
head, tail = arg.split(": ", 1)
parsed_doc.add_status_code(int(head), tail)
elif title == "Response":
parse_rst_docstring_response(section, parsed_doc)
elif title == "Authentication":
# get a text subnode
child = get_text_child(section)
parsed_doc.authentication = child.astext()
else:
logging.error("Error: unknown title: %s", title)
raise Exception
else:
if isinstance(section, docutils.nodes.TextElement):
parsed_doc.summary = section.astext()
return parsed_doc


def docs_from_swagger_file(fname: str):
with open(fname) as fp:
swagger_conf = yaml.load(fp)
for path in swagger_conf["paths"]:
# the path is going to be a route
for method, method_conf in swagger_conf["paths"][path].items():
if method == "parameters":
continue
try:
description = method_conf["description"]
except Exception as e:
print(method_conf)
raise e
# print("*" * 20)
# print(description)
# print("*" * 20)
parsed_description = parse_docstring_rst(description, method, path)
pprint(parsed_description.__dict__)


if __name__ == "__main__":
parser = ArgumentParser()
parser.add_argument("filename", help="swagger file to parse")
args = parser.parse_args()
logging.basicConfig(level=logging.DEBUG)
coloredlogs.install(level=logging.DEBUG)
docs_from_swagger_file(args.filename)
8 changes: 8 additions & 0 deletions scripts/api_doc_builder/bs_convert.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash

for fname in templates/api_v1_docs/*.html
do
python "utils/api_doc_builder/compare_docs.py" "$fname" > "/tmp/$(basename $fname).real"
done

echo 'done'
20 changes: 20 additions & 0 deletions scripts/api_doc_builder/compare_docs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import sys
from bs4 import BeautifulSoup

def compare():
l = []
for arg in sys.argv[1:]:
with open(arg) as fp:
contents = fp.read()
soup = BeautifulSoup(contents, "html.parser")
l.append(soup)

assert l[0].prettify() == l[1].prettify()

def bs_prettify():
with open(sys.argv[1]) as fp:
contents = fp.read()
soup = BeautifulSoup(contents, "html.parser")
print(soup.prettify())

bs_prettify()
18 changes: 18 additions & 0 deletions scripts/api_doc_builder/copy_docs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash

src="$1"
dest="$2"

if [ -z "$src" ] || [ -z "$dest" ]
then
echo "usage $0 src dest">&1
exit 1
fi

for f in "$src"/*.html
do
if ! cmp -s "$f" "$dest"/$(basename "$f")
then
cp -f -v "$f" "$dest"
fi
done

0 comments on commit 4361765

Please sign in to comment.