fastapi-voyager 0.12.2__py3-none-any.whl → 0.12.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastapi_voyager/__init__.py +1 -2
- fastapi_voyager/cli.py +3 -3
- fastapi_voyager/filter.py +6 -5
- fastapi_voyager/module.py +11 -10
- fastapi_voyager/render.py +21 -9
- fastapi_voyager/server.py +19 -18
- fastapi_voyager/type.py +5 -3
- fastapi_voyager/type_helper.py +13 -7
- fastapi_voyager/version.py +1 -1
- fastapi_voyager/voyager.py +14 -10
- fastapi_voyager/web/component/demo.js +17 -0
- fastapi_voyager/web/graph-ui.js +0 -1
- fastapi_voyager/web/index.html +14 -10
- fastapi_voyager/web/store.js +17 -0
- fastapi_voyager/web/vue-main.js +15 -4
- {fastapi_voyager-0.12.2.dist-info → fastapi_voyager-0.12.4.dist-info}/METADATA +20 -4
- fastapi_voyager-0.12.4.dist-info/RECORD +35 -0
- {fastapi_voyager-0.12.2.dist-info → fastapi_voyager-0.12.4.dist-info}/WHEEL +1 -1
- fastapi_voyager-0.12.2.dist-info/RECORD +0 -33
- {fastapi_voyager-0.12.2.dist-info → fastapi_voyager-0.12.4.dist-info}/entry_points.txt +0 -0
- {fastapi_voyager-0.12.2.dist-info → fastapi_voyager-0.12.4.dist-info}/licenses/LICENSE +0 -0
fastapi_voyager/__init__.py
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Utilities to introspect a FastAPI application and visualize its routing tree.
|
|
4
4
|
"""
|
|
5
|
-
from .version import __version__ # noqa: F401
|
|
6
|
-
|
|
7
5
|
from .server import create_voyager
|
|
6
|
+
from .version import __version__ # noqa: F401
|
|
8
7
|
|
|
9
8
|
__all__ = ["__version__", "create_voyager"]
|
fastapi_voyager/cli.py
CHANGED
|
@@ -5,9 +5,9 @@ import importlib.util
|
|
|
5
5
|
import logging
|
|
6
6
|
import os
|
|
7
7
|
import sys
|
|
8
|
-
from typing import Optional
|
|
9
8
|
|
|
10
9
|
from fastapi import FastAPI
|
|
10
|
+
|
|
11
11
|
from fastapi_voyager import server as viz_server
|
|
12
12
|
from fastapi_voyager.version import __version__
|
|
13
13
|
from fastapi_voyager.voyager import Voyager
|
|
@@ -15,7 +15,7 @@ from fastapi_voyager.voyager import Voyager
|
|
|
15
15
|
logger = logging.getLogger(__name__)
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def load_fastapi_app_from_file(module_path: str, app_name: str = "app") ->
|
|
18
|
+
def load_fastapi_app_from_file(module_path: str, app_name: str = "app") -> FastAPI | None:
|
|
19
19
|
"""Load FastAPI app from a Python module file."""
|
|
20
20
|
try:
|
|
21
21
|
# Convert relative path to absolute path
|
|
@@ -47,7 +47,7 @@ def load_fastapi_app_from_file(module_path: str, app_name: str = "app") -> Optio
|
|
|
47
47
|
return None
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
def load_fastapi_app_from_module(module_name: str, app_name: str = "app") ->
|
|
50
|
+
def load_fastapi_app_from_module(module_name: str, app_name: str = "app") -> FastAPI | None:
|
|
51
51
|
"""Load FastAPI app from a Python module name."""
|
|
52
52
|
try:
|
|
53
53
|
# Temporarily add the current working directory to sys.path
|
fastapi_voyager/filter.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
from collections import deque
|
|
3
|
-
|
|
4
|
-
from fastapi_voyager.type import
|
|
4
|
+
|
|
5
|
+
from fastapi_voyager.type import PK, Link, Route, SchemaNode, Tag
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
def filter_graph(
|
|
@@ -13,7 +14,7 @@ def filter_graph(
|
|
|
13
14
|
nodes: list[SchemaNode],
|
|
14
15
|
links: list[Link],
|
|
15
16
|
node_set: dict[str, SchemaNode],
|
|
16
|
-
) ->
|
|
17
|
+
) -> tuple[list[Tag], list[Route], list[SchemaNode], list[Link]]:
|
|
17
18
|
"""Filter tags, routes, schema nodes and links based on a target schema and optional field.
|
|
18
19
|
|
|
19
20
|
Behaviour summary (mirrors previous Analytics.filter_nodes_and_schemas_based_on_schemas):
|
|
@@ -113,7 +114,7 @@ def filter_subgraph_by_module_prefix(
|
|
|
113
114
|
links: list[Link],
|
|
114
115
|
nodes: list[SchemaNode],
|
|
115
116
|
module_prefix: str
|
|
116
|
-
) ->
|
|
117
|
+
) -> tuple[list[Tag], list[Route], list[SchemaNode], list[Link]]:
|
|
117
118
|
"""Collapse schema graph so routes link directly to nodes whose module matches ``module_prefix``.
|
|
118
119
|
|
|
119
120
|
The routine keeps tag→route links untouched, prunes schema nodes whose module does not start
|
|
@@ -202,7 +203,7 @@ def filter_subgraph_from_tag_to_schema_by_module_prefix(
|
|
|
202
203
|
links: list[Link],
|
|
203
204
|
nodes: list[SchemaNode],
|
|
204
205
|
module_prefix: str
|
|
205
|
-
) ->
|
|
206
|
+
) -> tuple[list[Tag], list[Route], list[SchemaNode], list[Link]]:
|
|
206
207
|
"""Collapse schema graph so routes link directly to nodes whose module matches ``module_prefix``.
|
|
207
208
|
|
|
208
209
|
The routine keeps tag→route links untouched, prunes schema nodes whose module does not start
|
fastapi_voyager/module.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from typing import Any, TypeVar
|
|
3
3
|
|
|
4
|
+
from fastapi_voyager.type import ModuleNode, ModuleRoute, Route, SchemaNode
|
|
4
5
|
|
|
5
6
|
N = TypeVar('N') # Node type: ModuleNode or ModuleRoute
|
|
6
7
|
I = TypeVar('I') # Item type: SchemaNode or Route
|
|
@@ -10,7 +11,7 @@ def _build_module_tree(
|
|
|
10
11
|
items: list[I],
|
|
11
12
|
*,
|
|
12
13
|
get_module_path: Callable[[I], str | None],
|
|
13
|
-
NodeClass:
|
|
14
|
+
NodeClass: type[N],
|
|
14
15
|
item_list_attr: str,
|
|
15
16
|
) -> list[N]:
|
|
16
17
|
"""
|
|
@@ -34,13 +35,13 @@ def _build_module_tree(
|
|
|
34
35
|
return NodeClass(**kwargs) # type: ignore[arg-type]
|
|
35
36
|
|
|
36
37
|
def get_or_create(child_name: str, parent: N) -> N:
|
|
37
|
-
for m in
|
|
38
|
+
for m in parent.modules:
|
|
38
39
|
if m.name == child_name:
|
|
39
40
|
return m
|
|
40
|
-
parent_full =
|
|
41
|
+
parent_full = parent.fullname
|
|
41
42
|
fullname = child_name if not parent_full or parent_full == "__root__" else f"{parent_full}.{child_name}"
|
|
42
43
|
new_node = make_node(child_name, fullname)
|
|
43
|
-
|
|
44
|
+
parent.modules.append(new_node)
|
|
44
45
|
return new_node
|
|
45
46
|
|
|
46
47
|
# Build the tree
|
|
@@ -65,13 +66,13 @@ def _build_module_tree(
|
|
|
65
66
|
|
|
66
67
|
# Collapse linear chains: no items on node and exactly one child module
|
|
67
68
|
def collapse(node: N) -> None:
|
|
68
|
-
while len(
|
|
69
|
-
child =
|
|
69
|
+
while len(node.modules) == 1 and len(getattr(node, item_list_attr)) == 0:
|
|
70
|
+
child = node.modules[0]
|
|
70
71
|
node.name = f"{node.name}.{child.name}"
|
|
71
72
|
node.fullname = child.fullname
|
|
72
73
|
setattr(node, item_list_attr, getattr(child, item_list_attr))
|
|
73
|
-
|
|
74
|
-
for m in
|
|
74
|
+
node.modules = child.modules
|
|
75
|
+
for m in node.modules:
|
|
75
76
|
collapse(m)
|
|
76
77
|
|
|
77
78
|
for top in result:
|
fastapi_voyager/render.py
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
from fastapi_voyager.type import SchemaNode, ModuleNode, Link, Tag, Route, FieldType, PK, ModuleRoute
|
|
3
|
-
from fastapi_voyager.module import build_module_schema_tree, build_module_route_tree
|
|
4
1
|
from logging import getLogger
|
|
5
2
|
|
|
3
|
+
from fastapi_voyager.module import build_module_route_tree, build_module_schema_tree
|
|
4
|
+
from fastapi_voyager.type import (
|
|
5
|
+
PK,
|
|
6
|
+
FieldType,
|
|
7
|
+
Link,
|
|
8
|
+
ModuleNode,
|
|
9
|
+
ModuleRoute,
|
|
10
|
+
Route,
|
|
11
|
+
SchemaNode,
|
|
12
|
+
Tag,
|
|
13
|
+
)
|
|
14
|
+
|
|
6
15
|
logger = getLogger(__name__)
|
|
7
16
|
|
|
8
17
|
|
|
@@ -23,7 +32,7 @@ class Renderer:
|
|
|
23
32
|
logger.info(f'show_module: {self.show_module}')
|
|
24
33
|
logger.info(f'module_color: {self.module_color}')
|
|
25
34
|
|
|
26
|
-
def render_schema_label(self, node: SchemaNode, color:
|
|
35
|
+
def render_schema_label(self, node: SchemaNode, color: str | None=None) -> str:
|
|
27
36
|
has_base_fields = any(f.from_base for f in node.fields)
|
|
28
37
|
fields = [n for n in node.fields if n.from_base is False]
|
|
29
38
|
|
|
@@ -74,7 +83,7 @@ class Renderer:
|
|
|
74
83
|
raise ValueError(f'Unknown link type: {link.type}')
|
|
75
84
|
|
|
76
85
|
def render_module_schema_content(self, nodes: list[SchemaNode]) -> str:
|
|
77
|
-
def render_node(node: SchemaNode, color:
|
|
86
|
+
def render_node(node: SchemaNode, color: str | None=None) -> str:
|
|
78
87
|
return f'''
|
|
79
88
|
"{node.id}" [
|
|
80
89
|
label = {self.render_schema_label(node, color)}
|
|
@@ -82,8 +91,10 @@ class Renderer:
|
|
|
82
91
|
margin="0.5,0.1"
|
|
83
92
|
];'''
|
|
84
93
|
|
|
85
|
-
def render_module_schema(mod: ModuleNode, inherit_color:
|
|
86
|
-
color:
|
|
94
|
+
def render_module_schema(mod: ModuleNode, inherit_color: str | None=None, show_cluster:bool=True) -> str:
|
|
95
|
+
color: str | None = inherit_color
|
|
96
|
+
cluster_color: str | None = None
|
|
97
|
+
|
|
87
98
|
|
|
88
99
|
# recursively vist module from short to long: 'a', 'a.b', 'a.b.c'
|
|
89
100
|
# color_flag: {'a', 'a.b.c'}
|
|
@@ -94,6 +105,7 @@ class Renderer:
|
|
|
94
105
|
if mod.fullname.startswith(k):
|
|
95
106
|
module_color_flag.remove(k)
|
|
96
107
|
color = self.module_color[k]
|
|
108
|
+
cluster_color = color if color != inherit_color else None
|
|
97
109
|
break
|
|
98
110
|
|
|
99
111
|
inner_nodes = [ render_node(node, color) for node in mod.schema_nodes ]
|
|
@@ -108,8 +120,8 @@ class Renderer:
|
|
|
108
120
|
style="rounded"
|
|
109
121
|
label = " {mod.name}"
|
|
110
122
|
labeljust = "l"
|
|
111
|
-
{(f'pencolor = "{
|
|
112
|
-
{(
|
|
123
|
+
{(f'pencolor = "{cluster_color}"' if cluster_color else 'pencolor="#ccc"')}
|
|
124
|
+
{('penwidth = 3' if color else 'penwidth=""')}
|
|
113
125
|
{inner_nodes_str}
|
|
114
126
|
{child_str}
|
|
115
127
|
}}'''
|
fastapi_voyager/server.py
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from typing import
|
|
3
|
-
|
|
4
|
-
from
|
|
5
|
-
from
|
|
6
|
-
from fastapi.responses import HTMLResponse, PlainTextResponse, JSONResponse
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
from fastapi import APIRouter, FastAPI
|
|
5
|
+
from fastapi.responses import HTMLResponse, JSONResponse, PlainTextResponse
|
|
7
6
|
from fastapi.staticfiles import StaticFiles
|
|
8
|
-
from
|
|
9
|
-
from
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
from starlette.middleware.gzip import GZipMiddleware
|
|
9
|
+
|
|
10
10
|
from fastapi_voyager.render import Renderer
|
|
11
|
+
from fastapi_voyager.type import CoreData, SchemaNode, Tag
|
|
11
12
|
from fastapi_voyager.type_helper import get_source, get_vscode_link
|
|
12
13
|
from fastapi_voyager.version import __version__
|
|
13
|
-
|
|
14
|
+
from fastapi_voyager.voyager import Voyager
|
|
14
15
|
|
|
15
16
|
WEB_DIR = Path(__file__).parent / "web"
|
|
16
17
|
WEB_DIR.mkdir(exist_ok=True)
|
|
17
18
|
|
|
18
19
|
GA_PLACEHOLDER = "<!-- GA_SNIPPET -->"
|
|
19
20
|
|
|
20
|
-
def _build_ga_snippet(ga_id:
|
|
21
|
+
def _build_ga_snippet(ga_id: str | None) -> str:
|
|
21
22
|
if not ga_id:
|
|
22
23
|
return ""
|
|
23
24
|
|
|
@@ -40,13 +41,13 @@ class OptionParam(BaseModel):
|
|
|
40
41
|
enable_brief_mode: bool
|
|
41
42
|
version: str
|
|
42
43
|
initial_page_policy: INITIAL_PAGE_POLICY
|
|
43
|
-
swagger_url:
|
|
44
|
+
swagger_url: str | None = None
|
|
44
45
|
|
|
45
46
|
class Payload(BaseModel):
|
|
46
|
-
tags:
|
|
47
|
-
schema_name:
|
|
48
|
-
schema_field:
|
|
49
|
-
route_name:
|
|
47
|
+
tags: list[str] | None = None
|
|
48
|
+
schema_name: str | None = None
|
|
49
|
+
schema_field: str | None = None
|
|
50
|
+
route_name: str | None = None
|
|
50
51
|
show_fields: str = 'object'
|
|
51
52
|
show_meta: bool = False
|
|
52
53
|
brief: bool = False
|
|
@@ -58,11 +59,11 @@ def create_voyager(
|
|
|
58
59
|
target_app: FastAPI,
|
|
59
60
|
module_color: dict[str, str] | None = None,
|
|
60
61
|
gzip_minimum_size: int | None = 500,
|
|
61
|
-
module_prefix:
|
|
62
|
-
swagger_url:
|
|
63
|
-
online_repo_url:
|
|
62
|
+
module_prefix: str | None = None,
|
|
63
|
+
swagger_url: str | None = None,
|
|
64
|
+
online_repo_url: str | None = None,
|
|
64
65
|
initial_page_policy: INITIAL_PAGE_POLICY = 'first',
|
|
65
|
-
ga_id:
|
|
66
|
+
ga_id: str | None = None,
|
|
66
67
|
) -> FastAPI:
|
|
67
68
|
router = APIRouter(tags=['fastapi-voyager'])
|
|
68
69
|
|
fastapi_voyager/type.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from dataclasses import field
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
2
4
|
from pydantic.dataclasses import dataclass
|
|
3
|
-
|
|
5
|
+
|
|
4
6
|
|
|
5
7
|
@dataclass
|
|
6
8
|
class NodeBase:
|
|
@@ -76,5 +78,5 @@ class CoreData:
|
|
|
76
78
|
nodes: list[SchemaNode]
|
|
77
79
|
links: list[Link]
|
|
78
80
|
show_fields: FieldType
|
|
79
|
-
module_color:
|
|
80
|
-
schema:
|
|
81
|
+
module_color: dict[str, str] | None = None
|
|
82
|
+
schema: str | None = None
|
fastapi_voyager/type_helper.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import inspect
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
|
-
from pydantic import BaseModel
|
|
5
|
-
from typing import get_origin, get_args, Union, Annotated, Any, Type, Generic, Optional
|
|
6
|
-
from fastapi_voyager.type import FieldInfo
|
|
7
4
|
from types import UnionType
|
|
5
|
+
from typing import Annotated, Any, Generic, Union, get_args, get_origin
|
|
6
|
+
|
|
8
7
|
import pydantic_resolve.constant as const
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
|
|
10
|
+
from fastapi_voyager.type import FieldInfo
|
|
9
11
|
|
|
10
12
|
logger = logging.getLogger(__name__)
|
|
11
13
|
|
|
@@ -185,7 +187,7 @@ def get_pydantic_fields(schema: type[BaseModel], bases_fields: set[str]) -> list
|
|
|
185
187
|
return fields
|
|
186
188
|
|
|
187
189
|
|
|
188
|
-
def get_vscode_link(kls, online_repo_url:
|
|
190
|
+
def get_vscode_link(kls, online_repo_url: str | None = None) -> str:
|
|
189
191
|
"""Build a VSCode deep link to the class definition.
|
|
190
192
|
|
|
191
193
|
Priority:
|
|
@@ -241,7 +243,7 @@ def safe_issubclass(kls, target_kls):
|
|
|
241
243
|
|
|
242
244
|
def update_forward_refs(kls):
|
|
243
245
|
# TODO: refactor
|
|
244
|
-
def update_pydantic_forward_refs(pydantic_kls:
|
|
246
|
+
def update_pydantic_forward_refs(pydantic_kls: type[BaseModel]):
|
|
245
247
|
"""
|
|
246
248
|
recursively update refs.
|
|
247
249
|
"""
|
|
@@ -254,7 +256,11 @@ def update_forward_refs(kls):
|
|
|
254
256
|
update_forward_refs(field.annotation)
|
|
255
257
|
|
|
256
258
|
for shelled_type in get_core_types(kls):
|
|
257
|
-
if
|
|
259
|
+
# Only treat as updated if the flag is set on the class itself, not via inheritance
|
|
260
|
+
|
|
261
|
+
local_attrs = getattr(shelled_type, '__dict__', {})
|
|
262
|
+
if local_attrs.get(const.PYDANTIC_FORWARD_REF_UPDATED, False):
|
|
263
|
+
logger.debug("%s visited", shelled_type.__qualname__)
|
|
258
264
|
continue
|
|
259
265
|
if safe_issubclass(shelled_type, BaseModel):
|
|
260
266
|
update_pydantic_forward_refs(shelled_type)
|
|
@@ -287,7 +293,7 @@ def is_non_pydantic_type(tp):
|
|
|
287
293
|
return True
|
|
288
294
|
|
|
289
295
|
if __name__ == "__main__":
|
|
290
|
-
from tests.demo_anno import
|
|
296
|
+
from tests.demo_anno import PageOverall, PageSprint
|
|
291
297
|
|
|
292
298
|
update_forward_refs(PageOverall)
|
|
293
299
|
update_forward_refs(PageSprint)
|
fastapi_voyager/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__all__ = ["__version__"]
|
|
2
|
-
__version__ = "0.12.
|
|
2
|
+
__version__ = "0.12.4"
|
fastapi_voyager/voyager.py
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
import pydantic_resolve.constant as const
|
|
2
3
|
from fastapi import FastAPI, routing
|
|
3
|
-
from
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
from fastapi_voyager.filter import (
|
|
7
|
+
filter_graph,
|
|
8
|
+
filter_subgraph_by_module_prefix,
|
|
9
|
+
filter_subgraph_from_tag_to_schema_by_module_prefix,
|
|
10
|
+
)
|
|
11
|
+
from fastapi_voyager.render import Renderer
|
|
12
|
+
from fastapi_voyager.type import PK, CoreData, FieldType, Link, LinkType, Route, SchemaNode, Tag
|
|
4
13
|
from fastapi_voyager.type_helper import (
|
|
5
|
-
get_core_types,
|
|
6
14
|
full_class_name,
|
|
7
15
|
get_bases_fields,
|
|
8
|
-
|
|
16
|
+
get_core_types,
|
|
9
17
|
get_pydantic_fields,
|
|
10
18
|
get_type_name,
|
|
19
|
+
is_inheritance_of_pydantic_base,
|
|
20
|
+
is_non_pydantic_type,
|
|
11
21
|
update_forward_refs,
|
|
12
|
-
is_non_pydantic_type
|
|
13
22
|
)
|
|
14
|
-
from pydantic import BaseModel
|
|
15
|
-
from fastapi_voyager.type import Route, SchemaNode, Link, Tag, LinkType, FieldType, PK, CoreData
|
|
16
|
-
from fastapi_voyager.filter import filter_graph, filter_subgraph_from_tag_to_schema_by_module_prefix, filter_subgraph_by_module_prefix
|
|
17
|
-
from fastapi_voyager.render import Renderer
|
|
18
|
-
import pydantic_resolve.constant as const
|
|
19
23
|
|
|
20
24
|
|
|
21
25
|
class Voyager:
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const { defineComponent, computed } = window.Vue;
|
|
2
|
+
|
|
3
|
+
import { store } from '../store.js'
|
|
4
|
+
|
|
5
|
+
export default defineComponent({
|
|
6
|
+
name: "Demo",
|
|
7
|
+
emits: ["close"],
|
|
8
|
+
setup() {
|
|
9
|
+
return { store };
|
|
10
|
+
},
|
|
11
|
+
template: `
|
|
12
|
+
<div>
|
|
13
|
+
<p>Count: {{ store.state.count }}</p>
|
|
14
|
+
<button @click="store.mutations.increment()">Add</button>
|
|
15
|
+
</div>
|
|
16
|
+
`
|
|
17
|
+
});
|
fastapi_voyager/web/graph-ui.js
CHANGED
fastapi_voyager/web/index.html
CHANGED
|
@@ -298,7 +298,7 @@
|
|
|
298
298
|
<template #after>
|
|
299
299
|
<div style="position: relative; width: 100%; height: 100%;">
|
|
300
300
|
<div id="graph" class="adjust-fit"></div>
|
|
301
|
-
<div style="position: absolute; left: 8px;
|
|
301
|
+
<div style="position: absolute; left: 8px; top: 8px; z-index: 10; background: rgba(255,255,255,0.85); border-radius: 4px; padding: 2px 8px;">
|
|
302
302
|
<div class="q-mt-sm">
|
|
303
303
|
<q-toggle
|
|
304
304
|
v-model="state.focus"
|
|
@@ -337,21 +337,25 @@
|
|
|
337
337
|
title="show module cluster"
|
|
338
338
|
/>
|
|
339
339
|
</div>
|
|
340
|
+
<div class="q-mt-sm">
|
|
341
|
+
<q-toggle
|
|
342
|
+
v-model="state.focus"
|
|
343
|
+
v-show="schemaCodeName"
|
|
344
|
+
@update:model-value="val => onFocusChange(val)"
|
|
345
|
+
label="Focus"
|
|
346
|
+
dense
|
|
347
|
+
title="pick a schema and toggle focus on to display related nodes only"
|
|
348
|
+
/>
|
|
349
|
+
</div>
|
|
350
|
+
<!-- <div class="q-mt-sm">
|
|
351
|
+
<demo-component></demo-component>
|
|
352
|
+
</div> -->
|
|
340
353
|
</div>
|
|
341
354
|
</div>
|
|
342
355
|
</template>
|
|
343
356
|
</q-splitter>
|
|
344
357
|
</q-page-container>
|
|
345
358
|
</q-layout>
|
|
346
|
-
<!-- Detail Dialog -->
|
|
347
|
-
<q-dialog v-model="showDetail" :persistent="true" :maximized="true">
|
|
348
|
-
<detail-dialog
|
|
349
|
-
:schema-name="schemaName"
|
|
350
|
-
:show-fields="state.showFields"
|
|
351
|
-
:model-value="showDetail"
|
|
352
|
-
@close="closeDetail"
|
|
353
|
-
/>
|
|
354
|
-
</q-dialog>
|
|
355
359
|
|
|
356
360
|
<!-- Schema Field Filter Dialog -->
|
|
357
361
|
<q-dialog
|
fastapi_voyager/web/vue-main.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import SchemaFieldFilter from "./component/schema-field-filter.js";
|
|
2
2
|
import SchemaCodeDisplay from "./component/schema-code-display.js";
|
|
3
3
|
import RouteCodeDisplay from "./component/route-code-display.js";
|
|
4
|
+
import Demo from './component/demo.js'
|
|
4
5
|
import RenderGraph from "./component/render-graph.js";
|
|
5
6
|
import { GraphUI } from "./graph-ui.js";
|
|
6
7
|
const { createApp, reactive, onMounted, watch, ref } = window.Vue;
|
|
@@ -39,17 +40,22 @@ const app = createApp({
|
|
|
39
40
|
|
|
40
41
|
const showDetail = ref(false);
|
|
41
42
|
const showSchemaFieldFilter = ref(false);
|
|
43
|
+
|
|
42
44
|
const showDumpDialog = ref(false);
|
|
43
45
|
const dumpJson = ref("");
|
|
44
46
|
const showImportDialog = ref(false);
|
|
45
47
|
const importJsonText = ref("");
|
|
48
|
+
|
|
46
49
|
const showRenderGraph = ref(false);
|
|
50
|
+
|
|
47
51
|
const renderCoreData = ref(null);
|
|
52
|
+
|
|
48
53
|
const schemaName = ref(""); // used by detail dialog
|
|
49
54
|
const schemaFieldFilterSchema = ref(null); // external schemaName for schema-field-filter
|
|
50
55
|
const schemaCodeName = ref("");
|
|
51
56
|
const routeCodeId = ref("");
|
|
52
57
|
const showRouteDetail = ref(false);
|
|
58
|
+
|
|
53
59
|
let graphUI = null;
|
|
54
60
|
|
|
55
61
|
function openDetail() {
|
|
@@ -435,13 +441,18 @@ const app = createApp({
|
|
|
435
441
|
};
|
|
436
442
|
},
|
|
437
443
|
});
|
|
444
|
+
|
|
438
445
|
app.use(window.Quasar);
|
|
446
|
+
|
|
439
447
|
// Set Quasar primary theme color to green
|
|
440
448
|
if (window.Quasar && typeof window.Quasar.setCssVar === "function") {
|
|
441
449
|
window.Quasar.setCssVar("primary", "#009485");
|
|
442
450
|
}
|
|
443
|
-
|
|
444
|
-
app.component("schema-
|
|
445
|
-
app.component("
|
|
446
|
-
app.component("
|
|
451
|
+
|
|
452
|
+
app.component("schema-field-filter", SchemaFieldFilter); // shift click and see relationships
|
|
453
|
+
app.component("schema-code-display", SchemaCodeDisplay); // double click to see node details
|
|
454
|
+
app.component("route-code-display", RouteCodeDisplay); // double click to see route details
|
|
455
|
+
app.component("render-graph", RenderGraph); // for debug, render pasted dot content
|
|
456
|
+
app.component('demo-component', Demo)
|
|
457
|
+
|
|
447
458
|
app.mount("#q-app");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-voyager
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.4
|
|
4
4
|
Summary: Visualize FastAPI application's routing tree and dependencies
|
|
5
5
|
Project-URL: Homepage, https://github.com/allmonday/fastapi-voyager
|
|
6
6
|
Project-URL: Source, https://github.com/allmonday/fastapi-voyager
|
|
@@ -12,7 +12,6 @@ Classifier: Framework :: FastAPI
|
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -32,14 +31,31 @@ Description-Content-Type: text/markdown
|
|
|
32
31
|
[](https://pepy.tech/projects/fastapi-voyager)
|
|
33
32
|
|
|
34
33
|
|
|
35
|
-
> This repo is still in early stage, it supports pydantic v2 only
|
|
36
34
|
|
|
37
35
|
Visualize your FastAPI endpoints, and explore them interactively.
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
> This repo is still in early stage, it supports pydantic v2 only
|
|
38
|
+
|
|
39
|
+
[live demo](https://www.newsyeah.fun/voyager/) of project: [composition oriented development pattern](https://github.com/allmonday/composition-oriented-development-pattern)
|
|
40
40
|
|
|
41
41
|
<img width="1600" height="986" alt="image" src="https://github.com/user-attachments/assets/8829cda0-f42d-4c84-be2f-b019bb5fe7e1" />
|
|
42
42
|
|
|
43
|
+
with configuration:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
app.mount('/voyager',
|
|
47
|
+
create_voyager(
|
|
48
|
+
app,
|
|
49
|
+
module_color={'src.services': 'tomato'},
|
|
50
|
+
module_prefix='src.services',
|
|
51
|
+
swagger_url="/docs",
|
|
52
|
+
ga_id="G-R64S7Q49VL",
|
|
53
|
+
initial_page_policy='first',
|
|
54
|
+
online_repo_url='https://github.com/allmonday/composition-oriented-development-pattern/blob/master'))
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
https://github.com/allmonday/composition-oriented-development-pattern/blob/master/src/main.py#L48
|
|
58
|
+
|
|
43
59
|
## Plan & Raodmap
|
|
44
60
|
- [ideas](./docs/idea.md)
|
|
45
61
|
- [changelog & roadmap](./docs/changelog.md)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
fastapi_voyager/__init__.py,sha256=kqwzThE1YhmQ_7jPKGlnWvqRGC-hFrRqq_lKhKaleYU,229
|
|
2
|
+
fastapi_voyager/cli.py,sha256=td3yIIigEomhSdDO-Xkh-CgpEwCafwlwnpvxnT9QsBo,10488
|
|
3
|
+
fastapi_voyager/filter.py,sha256=AN_HIu8-DtKisIq5mFt7CnqRHtxKewedNGyyaI82hSY,11529
|
|
4
|
+
fastapi_voyager/module.py,sha256=h9YR3BpS-CAcJW9WCdVkF4opqwY32w9T67g9GfdLytk,3425
|
|
5
|
+
fastapi_voyager/render.py,sha256=7jSChISqTV2agaO55thhwyrOhqVOMux4x7k8rSQROnU,9960
|
|
6
|
+
fastapi_voyager/server.py,sha256=MZNRpcXor2q8Rj3OSf6EH8NkgDChxfzUtnIY8ilRkaY,7053
|
|
7
|
+
fastapi_voyager/type.py,sha256=7EL1zaIwKVRGpLig7fqaOrZGN5k0Rm31C9COfck3CSs,1750
|
|
8
|
+
fastapi_voyager/type_helper.py,sha256=JXD_OE_xTARkGjWDsnO_xfvyZ0vcwViYyqCp6oEHBTM,9719
|
|
9
|
+
fastapi_voyager/version.py,sha256=_-7Zp-y6CfERg3seD-eKm5cYiJTOf-VWfF-6iV8hzms,49
|
|
10
|
+
fastapi_voyager/voyager.py,sha256=LiRUb0ZG2cfnyY_pwRqoeZjxb6Pu6xy_lqPiMupxoKM,13510
|
|
11
|
+
fastapi_voyager/web/graph-ui.js,sha256=7ynB-o8Dse6WHWlp4bmuEfa1pUZJ2YFLJKKXjr4R6wU,5733
|
|
12
|
+
fastapi_voyager/web/graphviz.svg.css,sha256=zDCjjpT0Idufu5YOiZI76PL70-avP3vTyzGPh9M85Do,1563
|
|
13
|
+
fastapi_voyager/web/graphviz.svg.js,sha256=lvAdbjHc-lMSk4GQp-iqYA2PCFX4RKnW7dFaoe0LUHs,16005
|
|
14
|
+
fastapi_voyager/web/index.html,sha256=C8gqka2omSHqx40C5_CCSMKHP-VJSPV3UFTrqQHGRtU,19635
|
|
15
|
+
fastapi_voyager/web/quasar.min.css,sha256=F5jQe7X2XT54VlvAaa2V3GsBFdVD-vxDZeaPLf6U9CU,203145
|
|
16
|
+
fastapi_voyager/web/quasar.min.js,sha256=h0ftyPMW_CRiyzeVfQqiup0vrVt4_QWojpqmpnpn07E,502974
|
|
17
|
+
fastapi_voyager/web/store.js,sha256=LPJdFxyFn8j7vOI41FKtWoB8agoqIUrR3tN5HRS_5e4,210
|
|
18
|
+
fastapi_voyager/web/vue-main.js,sha256=qTPJ2CTT5T9kRR2h0DbroOGJVePRIF3e24u7JexqjPQ,13516
|
|
19
|
+
fastapi_voyager/web/component/demo.js,sha256=FUwZqyMYjwf2J_YAzUBZ-gW9RFdOrz5zbxogBBYiYYE,347
|
|
20
|
+
fastapi_voyager/web/component/render-graph.js,sha256=e8Xgh2Kl-nYU0P1gstEmAepCgFnk2J6UdxW8TlMafGs,2322
|
|
21
|
+
fastapi_voyager/web/component/route-code-display.js,sha256=8NJPPjNRUC21gjpY8XYEQs4RBbhX1pCiqEhJp39ku6k,3678
|
|
22
|
+
fastapi_voyager/web/component/schema-code-display.js,sha256=qKUMV2RFQzR8deof2iC4vyp65UaWadtVsDAXjY-i3vE,7042
|
|
23
|
+
fastapi_voyager/web/component/schema-field-filter.js,sha256=c--XiXJrhIS7sYo1x8ZwMoqak0k9xLkNYTWoli-zd38,6253
|
|
24
|
+
fastapi_voyager/web/icon/android-chrome-192x192.png,sha256=35sBy6jmUFJCcquStaafHH1qClZIbd-X3PIKSeLkrNo,37285
|
|
25
|
+
fastapi_voyager/web/icon/android-chrome-512x512.png,sha256=eb2eDjCwIruc05029_0L9hcrkVkv8KceLn1DJMYU0zY,210789
|
|
26
|
+
fastapi_voyager/web/icon/apple-touch-icon.png,sha256=gnWK46tPnvSw1-oYZjgI02wpoO4OrIzsVzGHC5oKWO0,33187
|
|
27
|
+
fastapi_voyager/web/icon/favicon-16x16.png,sha256=JC07jEzfIYxBIoQn_FHXvyHuxESdhWNKLtjWURfTSFQ,686
|
|
28
|
+
fastapi_voyager/web/icon/favicon-32x32.png,sha256=C7v1h58cfWOsiLp9yOIZtlx-dLasBcq3NqpHVGRmpt4,1859
|
|
29
|
+
fastapi_voyager/web/icon/favicon.ico,sha256=tZolYIXkkBcFiYl1A8ksaXN2VjGamzcSdes838dLvNc,15406
|
|
30
|
+
fastapi_voyager/web/icon/site.webmanifest,sha256=ep4Hzh9zhmiZF2At3Fp1dQrYQuYF_3ZPZxc1KcGBvwQ,263
|
|
31
|
+
fastapi_voyager-0.12.4.dist-info/METADATA,sha256=9VUoRQ9sMRLqvukIAlC7kUi1Bcf2raXR9v9JRx7Ltbk,6523
|
|
32
|
+
fastapi_voyager-0.12.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
33
|
+
fastapi_voyager-0.12.4.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
|
|
34
|
+
fastapi_voyager-0.12.4.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
|
|
35
|
+
fastapi_voyager-0.12.4.dist-info/RECORD,,
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
fastapi_voyager/__init__.py,sha256=tZy0Nkj8kTaMgbvHy-mGxVcFGVX0Km-36dnzsAIG2uk,230
|
|
2
|
-
fastapi_voyager/cli.py,sha256=xK8DT-m2qP38FK2dGhLP-sHEuS29SBw6ACrnX9w85P0,10521
|
|
3
|
-
fastapi_voyager/filter.py,sha256=9Y-NepveIiCLOI-5eXk6DNK9H3dr5_h4xUbWYHkbo7M,11552
|
|
4
|
-
fastapi_voyager/module.py,sha256=Z2QHNmiLk6ZAJlm2nSmO875Q33TweSg8UxZSzIpU9zY,3499
|
|
5
|
-
fastapi_voyager/render.py,sha256=8hVsEDQi2-aP3QKN6KI3RnNz0uG-FuDr_k4D7QNsdQQ,9823
|
|
6
|
-
fastapi_voyager/server.py,sha256=fVKruGccBJ039z3EbYRjqUYHbbw5HOlo5jzK_n76ids,7092
|
|
7
|
-
fastapi_voyager/type.py,sha256=VmcTB1G-LOT70EWCzi4LU_FUkSGWUIBJX15T_J5HnOo,1764
|
|
8
|
-
fastapi_voyager/type_helper.py,sha256=TqtYP2_54aar_iQjD0XhjJPXYhfi6icnPPrxkj0a4sk,9523
|
|
9
|
-
fastapi_voyager/version.py,sha256=jnAp1zbYtBp4Jx35wH9VM8Hi_uyCLhudDOycCg10IfY,49
|
|
10
|
-
fastapi_voyager/voyager.py,sha256=nioo56oFDeZ8nwwPWDtaQbkpe4pVssFoBVHCWFhs0K4,13549
|
|
11
|
-
fastapi_voyager/web/graph-ui.js,sha256=9ONPxQHvk4HxYq6KtKc_2VbJmUgd-gh7i3Biv1rkqC4,5734
|
|
12
|
-
fastapi_voyager/web/graphviz.svg.css,sha256=zDCjjpT0Idufu5YOiZI76PL70-avP3vTyzGPh9M85Do,1563
|
|
13
|
-
fastapi_voyager/web/graphviz.svg.js,sha256=lvAdbjHc-lMSk4GQp-iqYA2PCFX4RKnW7dFaoe0LUHs,16005
|
|
14
|
-
fastapi_voyager/web/index.html,sha256=8cmlwQzE5tonHj_QozdKcr3z-7JFsvE7cjf70dye0y8,19377
|
|
15
|
-
fastapi_voyager/web/quasar.min.css,sha256=F5jQe7X2XT54VlvAaa2V3GsBFdVD-vxDZeaPLf6U9CU,203145
|
|
16
|
-
fastapi_voyager/web/quasar.min.js,sha256=h0ftyPMW_CRiyzeVfQqiup0vrVt4_QWojpqmpnpn07E,502974
|
|
17
|
-
fastapi_voyager/web/vue-main.js,sha256=m6U24ythzjQuAYXUm9BsTdFrApFNqW26B0Bf7TsybqQ,13275
|
|
18
|
-
fastapi_voyager/web/component/render-graph.js,sha256=e8Xgh2Kl-nYU0P1gstEmAepCgFnk2J6UdxW8TlMafGs,2322
|
|
19
|
-
fastapi_voyager/web/component/route-code-display.js,sha256=8NJPPjNRUC21gjpY8XYEQs4RBbhX1pCiqEhJp39ku6k,3678
|
|
20
|
-
fastapi_voyager/web/component/schema-code-display.js,sha256=qKUMV2RFQzR8deof2iC4vyp65UaWadtVsDAXjY-i3vE,7042
|
|
21
|
-
fastapi_voyager/web/component/schema-field-filter.js,sha256=c--XiXJrhIS7sYo1x8ZwMoqak0k9xLkNYTWoli-zd38,6253
|
|
22
|
-
fastapi_voyager/web/icon/android-chrome-192x192.png,sha256=35sBy6jmUFJCcquStaafHH1qClZIbd-X3PIKSeLkrNo,37285
|
|
23
|
-
fastapi_voyager/web/icon/android-chrome-512x512.png,sha256=eb2eDjCwIruc05029_0L9hcrkVkv8KceLn1DJMYU0zY,210789
|
|
24
|
-
fastapi_voyager/web/icon/apple-touch-icon.png,sha256=gnWK46tPnvSw1-oYZjgI02wpoO4OrIzsVzGHC5oKWO0,33187
|
|
25
|
-
fastapi_voyager/web/icon/favicon-16x16.png,sha256=JC07jEzfIYxBIoQn_FHXvyHuxESdhWNKLtjWURfTSFQ,686
|
|
26
|
-
fastapi_voyager/web/icon/favicon-32x32.png,sha256=C7v1h58cfWOsiLp9yOIZtlx-dLasBcq3NqpHVGRmpt4,1859
|
|
27
|
-
fastapi_voyager/web/icon/favicon.ico,sha256=tZolYIXkkBcFiYl1A8ksaXN2VjGamzcSdes838dLvNc,15406
|
|
28
|
-
fastapi_voyager/web/icon/site.webmanifest,sha256=ep4Hzh9zhmiZF2At3Fp1dQrYQuYF_3ZPZxc1KcGBvwQ,263
|
|
29
|
-
fastapi_voyager-0.12.2.dist-info/METADATA,sha256=hXiqtPfKvni9gv4HxyjGdQTQFaDIAGJ69qE4WzsSzus,6059
|
|
30
|
-
fastapi_voyager-0.12.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
31
|
-
fastapi_voyager-0.12.2.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
|
|
32
|
-
fastapi_voyager-0.12.2.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
|
|
33
|
-
fastapi_voyager-0.12.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|