| 
									
										
										
										
											2021-05-22 21:04:19 -08:00
										 |  |  | import re | 
					
						
							|  |  |  | from enum import Enum | 
					
						
							|  |  |  | from itertools import groupby | 
					
						
							|  |  |  | from pathlib import Path | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import slugify | 
					
						
							|  |  |  | from fastapi import FastAPI | 
					
						
							|  |  |  | from humps import camelize | 
					
						
							|  |  |  | from jinja2 import Template | 
					
						
							|  |  |  | from mealie.app import app | 
					
						
							|  |  |  | from pydantic import BaseModel | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CWD = Path(__file__).parent | 
					
						
							|  |  |  | OUT_DIR = CWD / "output" | 
					
						
							|  |  |  | OUT_FILE = OUT_DIR / "app_routes.py" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | JS_DIR = OUT_DIR / "javascriptAPI" | 
					
						
							|  |  |  | JS_OUT_FILE = JS_DIR / "apiRoutes.js" | 
					
						
							|  |  |  | TEMPLATES_DIR = CWD / "templates" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PYTEST_TEMPLATE = TEMPLATES_DIR / "pytest_routes.j2" | 
					
						
							|  |  |  | JS_REQUESTS = TEMPLATES_DIR / "js_requests.j2" | 
					
						
							|  |  |  | JS_ROUTES = TEMPLATES_DIR / "js_routes.j2" | 
					
						
							|  |  |  | JS_INDEX = TEMPLATES_DIR / "js_index.j2" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | JS_DIR.mkdir(exist_ok=True, parents=True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class RouteObject: | 
					
						
							|  |  |  |     def __init__(self, route_string) -> None: | 
					
						
							|  |  |  |         self.prefix = "/" + route_string.split("/")[1] | 
					
						
							| 
									
										
										
										
											2021-06-16 20:31:09 +02:00
										 |  |  |         self.route = "/" + route_string.split("/", 2)[2] | 
					
						
							| 
									
										
										
										
											2021-05-22 21:04:19 -08:00
										 |  |  |         self.js_route = self.route.replace("{", "${") | 
					
						
							|  |  |  |         self.parts = route_string.split("/")[1:] | 
					
						
							|  |  |  |         self.var = re.findall(r"\{(.*?)\}", route_string) | 
					
						
							|  |  |  |         self.is_function = "{" in self.route | 
					
						
							|  |  |  |         self.router_slug = slugify.slugify("_".join(self.parts[1:]), separator="_") | 
					
						
							|  |  |  |         self.router_camel = camelize(self.router_slug) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self) -> str: | 
					
						
							|  |  |  |         return f"""Route: {self.route}
 | 
					
						
							|  |  |  | Parts: {self.parts} | 
					
						
							|  |  |  | Function: {self.is_function} | 
					
						
							|  |  |  | Var: {self.var} | 
					
						
							|  |  |  | Slug: {self.router_slug} | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class RequestType(str, Enum): | 
					
						
							|  |  |  |     get = "get" | 
					
						
							|  |  |  |     put = "put" | 
					
						
							|  |  |  |     post = "post" | 
					
						
							|  |  |  |     patch = "patch" | 
					
						
							|  |  |  |     delete = "delete" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class HTTPRequest(BaseModel): | 
					
						
							|  |  |  |     request_type: RequestType | 
					
						
							|  |  |  |     description: str = "" | 
					
						
							|  |  |  |     summary: str | 
					
						
							|  |  |  |     tags: list[str] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def summary_camel(self): | 
					
						
							|  |  |  |         return camelize(self.summary) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def js_docs(self): | 
					
						
							|  |  |  |         return self.description.replace("\n", "  \n  * ") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PathObject(BaseModel): | 
					
						
							|  |  |  |     route_object: RouteObject | 
					
						
							|  |  |  |     http_verbs: list[HTTPRequest] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class Config: | 
					
						
							|  |  |  |         arbitrary_types_allowed = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_path_objects(app: FastAPI): | 
					
						
							|  |  |  |     paths = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for key, value in app.openapi().items(): | 
					
						
							|  |  |  |         if key == "paths": | 
					
						
							|  |  |  |             for key, value in value.items(): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 paths.append( | 
					
						
							|  |  |  |                     PathObject( | 
					
						
							|  |  |  |                         route_object=RouteObject(key), | 
					
						
							|  |  |  |                         http_verbs=[HTTPRequest(request_type=k, **v) for k, v in value.items()], | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return paths | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def read_template(file: Path): | 
					
						
							|  |  |  |     with open(file, "r") as f: | 
					
						
							|  |  |  |         return f.read() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def generate_template(app): | 
					
						
							|  |  |  |     paths = get_path_objects(app) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     static_paths = [x.route_object for x in paths if not x.route_object.is_function] | 
					
						
							|  |  |  |     function_paths = [x.route_object for x in paths if x.route_object.is_function] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     static_paths.sort(key=lambda x: x.router_slug) | 
					
						
							|  |  |  |     function_paths.sort(key=lambda x: x.router_slug) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     template = Template(read_template(PYTEST_TEMPLATE)) | 
					
						
							|  |  |  |     content = template.render(paths={"prefix": "/api", "static_paths": static_paths, "function_paths": function_paths}) | 
					
						
							|  |  |  |     with open(OUT_FILE, "w") as f: | 
					
						
							|  |  |  |         f.write(content) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     template = Template(read_template(JS_ROUTES)) | 
					
						
							|  |  |  |     content = template.render( | 
					
						
							|  |  |  |         paths={"prefix": "/api", "static_paths": static_paths, "function_paths": function_paths, "all_paths": paths} | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     with open(JS_OUT_FILE, "w") as f: | 
					
						
							|  |  |  |         f.write(content) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     all_tags = [] | 
					
						
							|  |  |  |     for k, g in groupby(paths, lambda x: x.http_verbs[0].tags[0]): | 
					
						
							|  |  |  |         template = Template(read_template(JS_REQUESTS)) | 
					
						
							|  |  |  |         content = template.render(paths={"all_paths": list(g), "export_name": camelize(k)}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         all_tags.append(camelize(k)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with open(JS_DIR.joinpath(camelize(k) + ".js"), "w") as f: | 
					
						
							|  |  |  |             f.write(content) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     template = Template(read_template(JS_INDEX)) | 
					
						
							|  |  |  |     content = template.render(files={"files": all_tags}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     with open(JS_DIR.joinpath("index.js"), "w") as f: | 
					
						
							|  |  |  |         f.write(content) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     generate_template(app) |