fastapi-voyager 0.9.5__py3-none-any.whl → 0.10.2__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/render.py +5 -5
- fastapi_voyager/server.py +4 -2
- fastapi_voyager/version.py +1 -1
- fastapi_voyager/voyager.py +83 -58
- fastapi_voyager/web/index.html +36 -7
- fastapi_voyager/web/vue-main.js +59 -3
- {fastapi_voyager-0.9.5.dist-info → fastapi_voyager-0.10.2.dist-info}/METADATA +20 -17
- {fastapi_voyager-0.9.5.dist-info → fastapi_voyager-0.10.2.dist-info}/RECORD +11 -11
- {fastapi_voyager-0.9.5.dist-info → fastapi_voyager-0.10.2.dist-info}/WHEEL +0 -0
- {fastapi_voyager-0.9.5.dist-info → fastapi_voyager-0.10.2.dist-info}/entry_points.txt +0 -0
- {fastapi_voyager-0.9.5.dist-info → fastapi_voyager-0.10.2.dist-info}/licenses/LICENSE +0 -0
fastapi_voyager/render.py
CHANGED
|
@@ -62,9 +62,8 @@ class Renderer:
|
|
|
62
62
|
else:
|
|
63
63
|
raise ValueError(f'Unknown link type: {link.type}')
|
|
64
64
|
|
|
65
|
-
def
|
|
65
|
+
def render_module_schema_content(self, mods: list[ModuleNode]) -> str:
|
|
66
66
|
module_color_flag = set(self.module_color.keys())
|
|
67
|
-
print(module_color_flag)
|
|
68
67
|
|
|
69
68
|
def render_module_schema(mod: ModuleNode) -> str:
|
|
70
69
|
color: Optional[str] = None
|
|
@@ -104,7 +103,7 @@ class Renderer:
|
|
|
104
103
|
inner_nodes = [
|
|
105
104
|
f'''
|
|
106
105
|
"{r.id}" [
|
|
107
|
-
label = " {r.name}
|
|
106
|
+
label = " {r.name} | {r.response_schema} "
|
|
108
107
|
margin="0.5,0.1"
|
|
109
108
|
shape = "record"
|
|
110
109
|
];''' for r in mod.routes
|
|
@@ -122,7 +121,7 @@ class Renderer:
|
|
|
122
121
|
{child_str}
|
|
123
122
|
}}'''
|
|
124
123
|
|
|
125
|
-
def render_dot(self, tags: list[Tag], routes: list[Route], nodes: list[SchemaNode], links: list[Link]) -> str:
|
|
124
|
+
def render_dot(self, tags: list[Tag], routes: list[Route], nodes: list[SchemaNode], links: list[Link], spline_line=False) -> str:
|
|
126
125
|
module_schemas = build_module_schema_tree(nodes)
|
|
127
126
|
module_routes = build_module_route_tree(routes)
|
|
128
127
|
|
|
@@ -136,7 +135,7 @@ class Renderer:
|
|
|
136
135
|
])
|
|
137
136
|
|
|
138
137
|
|
|
139
|
-
module_schemas_str = self.
|
|
138
|
+
module_schemas_str = self.render_module_schema_content(module_schemas)
|
|
140
139
|
module_routes_str = '\n'.join(self.render_module_route(m) for m in module_routes)
|
|
141
140
|
link_str = '\n'.join(self.render_link(link) for link in links)
|
|
142
141
|
|
|
@@ -144,6 +143,7 @@ class Renderer:
|
|
|
144
143
|
digraph world {{
|
|
145
144
|
pad="0.5"
|
|
146
145
|
nodesep=0.8
|
|
146
|
+
{'splines=line' if spline_line else ''}
|
|
147
147
|
fontname="Helvetica,Arial,sans-serif"
|
|
148
148
|
node [fontname="Helvetica,Arial,sans-serif"]
|
|
149
149
|
edge [
|
fastapi_voyager/server.py
CHANGED
|
@@ -9,6 +9,7 @@ from fastapi_voyager.voyager import Voyager
|
|
|
9
9
|
from fastapi_voyager.type import Tag, FieldInfo, CoreData, SchemaNode
|
|
10
10
|
from fastapi_voyager.render import Renderer
|
|
11
11
|
from fastapi_voyager.type_helper import get_source, get_vscode_link
|
|
12
|
+
from fastapi_voyager.version import __version__
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
WEB_DIR = Path(__file__).parent / "web"
|
|
@@ -19,6 +20,8 @@ class OptionParam(BaseModel):
|
|
|
19
20
|
tags: list[Tag]
|
|
20
21
|
schemas: list[SchemaNode]
|
|
21
22
|
dot: str
|
|
23
|
+
enable_brief_mode: bool
|
|
24
|
+
version: str
|
|
22
25
|
|
|
23
26
|
class Payload(BaseModel):
|
|
24
27
|
tags: Optional[list[str]] = None
|
|
@@ -53,11 +56,10 @@ def create_route(
|
|
|
53
56
|
schemas = voyager.nodes[:]
|
|
54
57
|
schemas.sort(key=lambda s: s.name)
|
|
55
58
|
|
|
56
|
-
return OptionParam(tags=tags, schemas=schemas, dot=dot)
|
|
59
|
+
return OptionParam(tags=tags, schemas=schemas, dot=dot, enable_brief_mode=bool(module_prefix), version=__version__)
|
|
57
60
|
|
|
58
61
|
@router.post("/dot", response_class=PlainTextResponse)
|
|
59
62
|
def get_filtered_dot(payload: Payload) -> str:
|
|
60
|
-
print(payload)
|
|
61
63
|
voyager = Voyager(
|
|
62
64
|
include_tags=payload.tags,
|
|
63
65
|
schema=payload.schema_name,
|
fastapi_voyager/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__all__ = ["__version__"]
|
|
2
|
-
__version__ = "0.
|
|
2
|
+
__version__ = "0.10.2"
|
fastapi_voyager/voyager.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
from pydantic import BaseModel
|
|
2
2
|
from fastapi import FastAPI, routing
|
|
3
3
|
from fastapi_voyager.type_helper import (
|
|
4
4
|
get_core_types,
|
|
@@ -54,9 +54,14 @@ class Voyager:
|
|
|
54
54
|
|
|
55
55
|
def _get_available_route(self, app: FastAPI):
|
|
56
56
|
for route in app.routes:
|
|
57
|
-
if isinstance(route, routing.APIRoute)
|
|
57
|
+
if isinstance(route, routing.APIRoute):
|
|
58
58
|
yield route
|
|
59
59
|
|
|
60
|
+
def analysis_route(self, route: routing.APIRoute):
|
|
61
|
+
...
|
|
62
|
+
|
|
63
|
+
def analysis_tags(self, tag: str):
|
|
64
|
+
...
|
|
60
65
|
|
|
61
66
|
def analysis(self, app: FastAPI):
|
|
62
67
|
"""
|
|
@@ -68,67 +73,76 @@ class Voyager:
|
|
|
68
73
|
"""
|
|
69
74
|
schemas: list[type[BaseModel]] = []
|
|
70
75
|
|
|
76
|
+
# First, group all routes by tag
|
|
77
|
+
routes_by_tag: dict[str, list] = {}
|
|
71
78
|
for route in self._get_available_route(app):
|
|
72
|
-
# check tags
|
|
73
79
|
tags = getattr(route, 'tags', None)
|
|
74
|
-
route_tag = tags[0] if tags else '__default__'
|
|
75
|
-
if self.include_tags and route_tag not in self.include_tags:
|
|
76
|
-
continue
|
|
77
80
|
|
|
78
|
-
#
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
tag_obj = Tag(id=tag_id, name=route_tag, routes=[])
|
|
82
|
-
self.tag_set[tag_id] = tag_obj
|
|
83
|
-
self.tags.append(tag_obj)
|
|
84
|
-
|
|
85
|
-
# add route and create links
|
|
86
|
-
route_id = full_class_name(route.endpoint)
|
|
87
|
-
route_name = route.endpoint.__name__
|
|
88
|
-
route_module = route.endpoint.__module__
|
|
89
|
-
|
|
90
|
-
# filter by route_name (route.id) if provided
|
|
91
|
-
if self.route_name is not None and route_id != self.route_name:
|
|
92
|
-
continue
|
|
81
|
+
# using multiple tags is harmful, it's not recommended and will not be supported
|
|
82
|
+
route_tag = tags[0] if tags else '__default__'
|
|
83
|
+
routes_by_tag.setdefault(route_tag, []).append(route)
|
|
93
84
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
85
|
+
# Then filter by include_tags if provided
|
|
86
|
+
if self.include_tags:
|
|
87
|
+
filtered_routes_by_tag = {tag: routes for tag, routes in routes_by_tag.items()
|
|
88
|
+
if tag in self.include_tags}
|
|
89
|
+
else:
|
|
90
|
+
filtered_routes_by_tag = routes_by_tag
|
|
98
91
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
source_origin=tag_id,
|
|
102
|
-
target=route_id,
|
|
103
|
-
target_origin=route_id,
|
|
104
|
-
type='tag_route'
|
|
105
|
-
))
|
|
92
|
+
# Process filtered routes
|
|
93
|
+
for route_tag, routes in filtered_routes_by_tag.items():
|
|
106
94
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
95
|
+
tag_id = f'tag__{route_tag}'
|
|
96
|
+
tag_obj = Tag(id=tag_id, name=route_tag, routes=[])
|
|
97
|
+
self.tags.append(tag_obj)
|
|
98
|
+
|
|
99
|
+
for route in routes:
|
|
100
|
+
# add route and create links
|
|
101
|
+
route_id = full_class_name(route.endpoint)
|
|
102
|
+
route_name = route.endpoint.__name__
|
|
103
|
+
route_module = route.endpoint.__module__
|
|
104
|
+
|
|
105
|
+
# filter by route_name (route.id) if provided
|
|
106
|
+
if self.route_name is not None and route_id != self.route_name:
|
|
107
|
+
continue
|
|
108
|
+
|
|
109
|
+
is_primitive_response = is_non_pydantic_type(route.response_model)
|
|
110
|
+
# filter primitive route if needed
|
|
111
|
+
if self.hide_primitive_route and is_primitive_response:
|
|
112
|
+
continue
|
|
113
|
+
|
|
114
|
+
self.links.append(Link(
|
|
115
|
+
source=tag_id,
|
|
116
|
+
source_origin=tag_id,
|
|
117
|
+
target=route_id,
|
|
118
|
+
target_origin=route_id,
|
|
119
|
+
type='tag_route'
|
|
120
|
+
))
|
|
121
|
+
|
|
122
|
+
route_obj = Route(
|
|
123
|
+
id=route_id,
|
|
124
|
+
name=route_name,
|
|
125
|
+
module=route_module,
|
|
126
|
+
response_schema=get_type_name(route.response_model),
|
|
127
|
+
is_primitive=is_primitive_response
|
|
128
|
+
)
|
|
129
|
+
self.routes.append(route_obj)
|
|
130
|
+
tag_obj.routes.append(route_obj)
|
|
131
|
+
|
|
132
|
+
# add response_models and create links from route -> response_model
|
|
133
|
+
for schema in get_core_types(route.response_model):
|
|
134
|
+
if schema and issubclass(schema, BaseModel):
|
|
135
|
+
is_primitive_response = False
|
|
136
|
+
target_name = full_class_name(schema)
|
|
137
|
+
self.links.append(Link(
|
|
138
|
+
source=route_id,
|
|
139
|
+
source_origin=route_id,
|
|
140
|
+
target=self.generate_node_head(target_name),
|
|
141
|
+
target_origin=target_name,
|
|
142
|
+
type='route_to_schema'
|
|
143
|
+
))
|
|
144
|
+
|
|
145
|
+
schemas.append(schema)
|
|
132
146
|
|
|
133
147
|
for s in schemas:
|
|
134
148
|
self.analysis_schemas(s)
|
|
@@ -271,6 +285,12 @@ class Voyager:
|
|
|
271
285
|
schema=self.schema
|
|
272
286
|
)
|
|
273
287
|
|
|
288
|
+
def handle_hide(self, tags, routes, links):
|
|
289
|
+
if self.include_tags:
|
|
290
|
+
return [], routes, [lk for lk in links if lk.type != 'tag_route']
|
|
291
|
+
else:
|
|
292
|
+
return tags, routes, links
|
|
293
|
+
|
|
274
294
|
def render_dot(self):
|
|
275
295
|
_tags, _routes, _nodes, _links = filter_graph(
|
|
276
296
|
schema=self.schema,
|
|
@@ -281,7 +301,10 @@ class Voyager:
|
|
|
281
301
|
links=self.links,
|
|
282
302
|
node_set=self.node_set,
|
|
283
303
|
)
|
|
304
|
+
|
|
284
305
|
renderer = Renderer(show_fields=self.show_fields, module_color=self.module_color, schema=self.schema)
|
|
306
|
+
|
|
307
|
+
_tags, _routes, _links = self.handle_hide(_tags, _routes, _links)
|
|
285
308
|
return renderer.render_dot(_tags, _routes, _nodes, _links)
|
|
286
309
|
|
|
287
310
|
def render_brief_dot(self, module_prefix: str | None = None):
|
|
@@ -293,4 +316,6 @@ class Voyager:
|
|
|
293
316
|
links=self.links,
|
|
294
317
|
)
|
|
295
318
|
renderer = Renderer(show_fields=self.show_fields, module_color=self.module_color, schema=None)
|
|
296
|
-
|
|
319
|
+
|
|
320
|
+
_tags, _routes, _links = self.handle_hide(_tags, _routes, _links)
|
|
321
|
+
return renderer.render_dot(_tags, _routes, _nodes, _links, True)
|
fastapi_voyager/web/index.html
CHANGED
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
.adjust-fit {
|
|
60
|
-
height: calc(
|
|
60
|
+
height: calc(100vh - 54px);
|
|
61
61
|
}
|
|
62
62
|
</style>
|
|
63
63
|
<body>
|
|
@@ -67,10 +67,11 @@
|
|
|
67
67
|
<q-toolbar class="row text-grey-9 bg-white" style="width: 100%">
|
|
68
68
|
<div
|
|
69
69
|
class="col-auto text-primary"
|
|
70
|
-
style="font-size:
|
|
70
|
+
style="font-size: 18px; font-weight: bold; display: flex; align-items: baseline;"
|
|
71
71
|
>
|
|
72
72
|
<q-icon class="q-mr-sm" name="satellite_alt"></q-icon>
|
|
73
73
|
<span> FastAPI Voyager </span>
|
|
74
|
+
<span v-if="state.version" style="font-size: 12px; margin-left: 8px; font-weight: normal;">{{ state.version }}</span>
|
|
74
75
|
</div>
|
|
75
76
|
<div class="col-auto" style="font-size: 16px">
|
|
76
77
|
<q-option-group
|
|
@@ -86,6 +87,7 @@
|
|
|
86
87
|
</div>
|
|
87
88
|
<div class="col-auto q-ml-auto">
|
|
88
89
|
<q-toggle
|
|
90
|
+
v-if="state.enableBriefMode"
|
|
89
91
|
class="q-mr-md"
|
|
90
92
|
v-model="state.brief"
|
|
91
93
|
label="Brief Mode"
|
|
@@ -163,12 +165,29 @@
|
|
|
163
165
|
|
|
164
166
|
<q-drawer
|
|
165
167
|
v-model="state.detailDrawer"
|
|
166
|
-
width="
|
|
168
|
+
:width="state.drawerWidth"
|
|
167
169
|
side="right"
|
|
170
|
+
style="border-left: 1px solid #888;"
|
|
168
171
|
overlay
|
|
169
172
|
bordered
|
|
170
173
|
>
|
|
171
|
-
|
|
174
|
+
<!-- 可拖拽的调整栏 -->
|
|
175
|
+
<div
|
|
176
|
+
@mousedown="startDragDrawer"
|
|
177
|
+
style="
|
|
178
|
+
position: absolute;
|
|
179
|
+
left: -3px;
|
|
180
|
+
top: 0;
|
|
181
|
+
width: 6px;
|
|
182
|
+
height: 100%;
|
|
183
|
+
cursor: col-resize;
|
|
184
|
+
background: transparent;
|
|
185
|
+
z-index: 10;
|
|
186
|
+
"
|
|
187
|
+
title="drag to resize"
|
|
188
|
+
></div>
|
|
189
|
+
|
|
190
|
+
<div style="z-index: 11; position: absolute; left: -17px; top: 9px">
|
|
172
191
|
<q-btn
|
|
173
192
|
@click="state.detailDrawer = !state.detailDrawer"
|
|
174
193
|
round
|
|
@@ -208,14 +227,14 @@
|
|
|
208
227
|
v-for="tag in state.rawTags"
|
|
209
228
|
:key="tag.name"
|
|
210
229
|
expand-separator
|
|
211
|
-
:model-value="state.
|
|
230
|
+
:model-value="state._tag === tag.name"
|
|
212
231
|
@update:model-value="(val) => toggleTag(tag.name, val)"
|
|
213
232
|
:header-class="state.tag === tag.name ? 'text-primary text-bold' : ''"
|
|
214
233
|
content-class="q-pa-none"
|
|
215
234
|
>
|
|
216
235
|
<template #header>
|
|
217
236
|
<div class="row items-center" style="width: 100%">
|
|
218
|
-
<div class="row items-
|
|
237
|
+
<div class="row items-center">
|
|
219
238
|
<q-icon
|
|
220
239
|
class="q-mr-sm"
|
|
221
240
|
:name="state.tag == tag.name ? 'folder' : 'folder_open'"
|
|
@@ -260,7 +279,17 @@
|
|
|
260
279
|
</template>
|
|
261
280
|
|
|
262
281
|
<template #after>
|
|
263
|
-
<div
|
|
282
|
+
<div style="position: relative; width: 100%; height: 100%;">
|
|
283
|
+
<div id="graph" class="adjust-fit"></div>
|
|
284
|
+
<q-toggle
|
|
285
|
+
v-model="state.focus"
|
|
286
|
+
v-show="schemaCodeName"
|
|
287
|
+
@update:model-value="val => onFocusChange(val)"
|
|
288
|
+
label="Focus"
|
|
289
|
+
style="position: absolute; left: 8px; top: 8px; z-index: 10; background: rgba(255,255,255,0.85); border-radius: 4px; padding: 2px 8px;"
|
|
290
|
+
size="sm"
|
|
291
|
+
/>
|
|
292
|
+
</div>
|
|
264
293
|
</template>
|
|
265
294
|
</q-splitter>
|
|
266
295
|
</q-page-container>
|
fastapi_voyager/web/vue-main.js
CHANGED
|
@@ -10,6 +10,7 @@ const app = createApp({
|
|
|
10
10
|
const state = reactive({
|
|
11
11
|
// options and selections
|
|
12
12
|
tag: null, // picked tag
|
|
13
|
+
_tag: null, // display tag
|
|
13
14
|
routeId: null, // picked route
|
|
14
15
|
schemaId: null, // picked schema
|
|
15
16
|
showFields: "object",
|
|
@@ -18,7 +19,9 @@ const app = createApp({
|
|
|
18
19
|
{ label: "Object fields", value: "object" },
|
|
19
20
|
{ label: "All fields", value: "all" },
|
|
20
21
|
],
|
|
22
|
+
enableBriefMode: false,
|
|
21
23
|
brief: false,
|
|
24
|
+
focus: false,
|
|
22
25
|
hidePrimitiveRoute: false,
|
|
23
26
|
generating: false,
|
|
24
27
|
rawTags: [], // [{ name, routes: [{ id, name }] }]
|
|
@@ -28,6 +31,8 @@ const app = createApp({
|
|
|
28
31
|
// Splitter size (left panel width in px)
|
|
29
32
|
splitter: 300,
|
|
30
33
|
detailDrawer: false,
|
|
34
|
+
drawerWidth: 300, // drawer 宽度
|
|
35
|
+
version: "", // version from backend
|
|
31
36
|
});
|
|
32
37
|
|
|
33
38
|
const showDetail = ref(false);
|
|
@@ -70,6 +75,8 @@ const app = createApp({
|
|
|
70
75
|
acc[r.id] = r;
|
|
71
76
|
return acc;
|
|
72
77
|
}, {});
|
|
78
|
+
state.enableBriefMode = data.enable_brief_mode || false;
|
|
79
|
+
state.version = data.version || "";
|
|
73
80
|
|
|
74
81
|
// default route options placeholder
|
|
75
82
|
} catch (e) {
|
|
@@ -79,18 +86,30 @@ const app = createApp({
|
|
|
79
86
|
}
|
|
80
87
|
}
|
|
81
88
|
|
|
82
|
-
async function
|
|
89
|
+
async function onFocusChange(val) {
|
|
90
|
+
if (val) {
|
|
91
|
+
await onGenerate(false, schemaCodeName.value)
|
|
92
|
+
} else {
|
|
93
|
+
await onGenerate(false, null)
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
const ele = $(`[data-name='${schemaCodeName.value}'] polygon`)
|
|
96
|
+
debugger
|
|
97
|
+
ele.click()
|
|
98
|
+
}, 1)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function onGenerate(resetZoom = true, schema_name = null) {
|
|
83
103
|
state.generating = true;
|
|
84
104
|
try {
|
|
85
105
|
const payload = {
|
|
86
106
|
tags: state.tag ? [state.tag] : null,
|
|
87
|
-
schema_name:
|
|
107
|
+
schema_name: schema_name || null,
|
|
88
108
|
route_name: state.routeId || null,
|
|
89
109
|
show_fields: state.showFields,
|
|
90
110
|
brief: state.brief,
|
|
91
111
|
hide_primitive_route: state.hidePrimitiveRoute,
|
|
92
112
|
};
|
|
93
|
-
|
|
94
113
|
const res = await fetch("dot", {
|
|
95
114
|
method: "POST",
|
|
96
115
|
headers: { "Content-Type": "application/json" },
|
|
@@ -119,6 +138,7 @@ const app = createApp({
|
|
|
119
138
|
resetCb: () => {
|
|
120
139
|
state.detailDrawer = false;
|
|
121
140
|
showRouteDetail.value = false;
|
|
141
|
+
schemaCodeName.value = ''
|
|
122
142
|
}
|
|
123
143
|
});
|
|
124
144
|
|
|
@@ -198,15 +218,23 @@ const app = createApp({
|
|
|
198
218
|
state.schemaId = null;
|
|
199
219
|
// state.showFields = "object";
|
|
200
220
|
state.brief = false;
|
|
221
|
+
state.focus = false
|
|
222
|
+
schemaCodeName.value = ''
|
|
201
223
|
onGenerate();
|
|
202
224
|
}
|
|
203
225
|
|
|
204
226
|
function toggleTag(tagName, expanded = null) {
|
|
205
227
|
if (expanded === true) {
|
|
228
|
+
state._tag = tagName;
|
|
206
229
|
state.tag = tagName;
|
|
207
230
|
state.routeId = "";
|
|
231
|
+
state.focus = false
|
|
232
|
+
schemaCodeName.value = ''
|
|
208
233
|
onGenerate();
|
|
234
|
+
} else {
|
|
235
|
+
state._tag = null
|
|
209
236
|
}
|
|
237
|
+
|
|
210
238
|
state.detailDrawer = false;
|
|
211
239
|
showRouteDetail.value = false;
|
|
212
240
|
}
|
|
@@ -219,6 +247,8 @@ const app = createApp({
|
|
|
219
247
|
}
|
|
220
248
|
state.detailDrawer = false;
|
|
221
249
|
showRouteDetail.value = false;
|
|
250
|
+
state.focus = false
|
|
251
|
+
schemaCodeName.value = ''
|
|
222
252
|
onGenerate();
|
|
223
253
|
}
|
|
224
254
|
|
|
@@ -237,6 +267,30 @@ const app = createApp({
|
|
|
237
267
|
onGenerate(false);
|
|
238
268
|
}
|
|
239
269
|
|
|
270
|
+
function startDragDrawer(e) {
|
|
271
|
+
const startX = e.clientX;
|
|
272
|
+
const startWidth = state.drawerWidth;
|
|
273
|
+
|
|
274
|
+
function onMouseMove(moveEvent) {
|
|
275
|
+
const deltaX = startX - moveEvent.clientX;
|
|
276
|
+
const newWidth = Math.max(300, Math.min(800, startWidth + deltaX));
|
|
277
|
+
state.drawerWidth = newWidth;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function onMouseUp() {
|
|
281
|
+
document.removeEventListener('mousemove', onMouseMove);
|
|
282
|
+
document.removeEventListener('mouseup', onMouseUp);
|
|
283
|
+
document.body.style.cursor = '';
|
|
284
|
+
document.body.style.userSelect = '';
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
document.addEventListener('mousemove', onMouseMove);
|
|
288
|
+
document.addEventListener('mouseup', onMouseUp);
|
|
289
|
+
document.body.style.cursor = 'col-resize';
|
|
290
|
+
document.body.style.userSelect = 'none';
|
|
291
|
+
e.preventDefault();
|
|
292
|
+
}
|
|
293
|
+
|
|
240
294
|
onMounted(async () => {
|
|
241
295
|
await loadInitial();
|
|
242
296
|
});
|
|
@@ -272,6 +326,8 @@ const app = createApp({
|
|
|
272
326
|
showRenderGraph,
|
|
273
327
|
renderCoreData,
|
|
274
328
|
toggleShowField,
|
|
329
|
+
startDragDrawer,
|
|
330
|
+
onFocusChange
|
|
275
331
|
};
|
|
276
332
|
},
|
|
277
333
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-voyager
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.2
|
|
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
|
|
@@ -167,14 +167,15 @@ or you can open router_viz.dot with vscode extension `graphviz interactive previ
|
|
|
167
167
|
- [ ] add extra info for schema
|
|
168
168
|
- [ ] display standard ER diagram `hard`
|
|
169
169
|
- [ ] display potential invalid links
|
|
170
|
-
- [ ]
|
|
170
|
+
- [ ] optimize static resource (allow manually config url)
|
|
171
|
+
- [ ] improve search dialog
|
|
172
|
+
- [ ] add route/tag list
|
|
171
173
|
|
|
172
174
|
### in analysis
|
|
173
175
|
- [ ] click field to highlight links
|
|
174
176
|
- [ ] animation effect for edges
|
|
175
177
|
- [ ] customrized right click panel
|
|
176
178
|
- [ ] show own dependencies
|
|
177
|
-
- [ ] clean up fe code
|
|
178
179
|
|
|
179
180
|
### plan:
|
|
180
181
|
#### <0.9:
|
|
@@ -225,23 +226,24 @@ or you can open router_viz.dot with vscode extension `graphviz interactive previ
|
|
|
225
226
|
- [x] route list should have a max height
|
|
226
227
|
|
|
227
228
|
#### 0.10
|
|
228
|
-
-
|
|
229
|
-
- [
|
|
229
|
+
- 0.10.1
|
|
230
|
+
- [x] refactor voyager.py tag -> route structure
|
|
231
|
+
- [x] fix missing route (tag has only one route which return primitive value)
|
|
232
|
+
- [x] make right panel resizable by dragging
|
|
233
|
+
- [x] allow closing tag expansion item
|
|
234
|
+
- [x] hide brief mode if not configured
|
|
235
|
+
- [x] add focus button to only show related nodes under current route/tag graph in dialog
|
|
236
|
+
- 0.10.2
|
|
237
|
+
- [x] fix graph height
|
|
238
|
+
- [x] show version in title
|
|
239
|
+
|
|
240
|
+
#### 0.11
|
|
241
|
+
- [ ] enable/disable module cluster (to save space)
|
|
242
|
+
- [ ] fix layout issue when rendering huge graph
|
|
243
|
+
- [ ] logging information
|
|
230
244
|
- [ ] support opening route in swagger
|
|
231
245
|
- config docs path
|
|
232
|
-
- [ ] add http method for route
|
|
233
|
-
- [ ] enable/disable module cluster (may save space)
|
|
234
|
-
- [ ] logging information
|
|
235
246
|
- [ ] add tests
|
|
236
|
-
- [ ] hide brief mode if not configured
|
|
237
|
-
- [ ] optimize static resource (allow manually config url)
|
|
238
|
-
- [ ] show route count in tag expansion item
|
|
239
|
-
- [ ] route list should have a max height to trigger scrollable
|
|
240
|
-
- [ ] fix layout issue when rendering huge graph
|
|
241
|
-
- [ ] fix missing route (tag has only one route which return primitive value)
|
|
242
|
-
|
|
243
|
-
#### 0.11
|
|
244
|
-
- [ ] improve search dialog
|
|
245
247
|
|
|
246
248
|
#### 0.12
|
|
247
249
|
- [ ] integration with pydantic-resolve
|
|
@@ -251,6 +253,7 @@ or you can open router_viz.dot with vscode extension `graphviz interactive previ
|
|
|
251
253
|
#### 0.13
|
|
252
254
|
- [ ] config release pipeline
|
|
253
255
|
|
|
256
|
+
|
|
254
257
|
## Using with pydantic-resolve
|
|
255
258
|
|
|
256
259
|
WIP: ...
|
|
@@ -2,19 +2,19 @@ fastapi_voyager/__init__.py,sha256=tZy0Nkj8kTaMgbvHy-mGxVcFGVX0Km-36dnzsAIG2uk,2
|
|
|
2
2
|
fastapi_voyager/cli.py,sha256=kQb4g6JEGZR99e5r8LyFFEeb_-uT-n_gp_sDoYG3R7k,11118
|
|
3
3
|
fastapi_voyager/filter.py,sha256=2Yt37o8mhqSqleafO4YRrumh_ExYUqzXFOxQRPuTbAc,8078
|
|
4
4
|
fastapi_voyager/module.py,sha256=Z2QHNmiLk6ZAJlm2nSmO875Q33TweSg8UxZSzIpU9zY,3499
|
|
5
|
-
fastapi_voyager/render.py,sha256=
|
|
6
|
-
fastapi_voyager/server.py,sha256=
|
|
5
|
+
fastapi_voyager/render.py,sha256=sR1oUh8d3hhDalQhUcrL-k2ZYAWbLDGpGDOB0dDaqRQ,7804
|
|
6
|
+
fastapi_voyager/server.py,sha256=6kCj906N4PVpKbUI8eq7bJ4RoET1kIQgUQUf-oMFdSY,6326
|
|
7
7
|
fastapi_voyager/type.py,sha256=pWYKmgb9e0W_JeD7k54Mr2lxUZV_Ir9TNpewGRwHyHQ,1629
|
|
8
8
|
fastapi_voyager/type_helper.py,sha256=hjBC4E0tgBpQDlYxGg74uK07SXjsrAgictEETJfIpYM,9231
|
|
9
|
-
fastapi_voyager/version.py,sha256=
|
|
10
|
-
fastapi_voyager/voyager.py,sha256=
|
|
9
|
+
fastapi_voyager/version.py,sha256=KhkBU7axnprYVOcYbZBAgdkdrD7vU8iriPE75NCnHgI,49
|
|
10
|
+
fastapi_voyager/voyager.py,sha256=DIc93SFPVwrsk1Nga74ZZyc4L0J50OAESE9j3fnEty0,12053
|
|
11
11
|
fastapi_voyager/web/graph-ui.js,sha256=FmKA3eeHMEeUDNuay3f54_fv4eGHT7MT0TaGqXhyMWQ,4978
|
|
12
12
|
fastapi_voyager/web/graphviz.svg.css,sha256=zDCjjpT0Idufu5YOiZI76PL70-avP3vTyzGPh9M85Do,1563
|
|
13
13
|
fastapi_voyager/web/graphviz.svg.js,sha256=lvAdbjHc-lMSk4GQp-iqYA2PCFX4RKnW7dFaoe0LUHs,16005
|
|
14
|
-
fastapi_voyager/web/index.html,sha256=
|
|
14
|
+
fastapi_voyager/web/index.html,sha256=3cvaqXgwGdTkNSdvrw8Grn0xbhjWPvlo_A12ltjs5j0,15164
|
|
15
15
|
fastapi_voyager/web/quasar.min.css,sha256=F5jQe7X2XT54VlvAaa2V3GsBFdVD-vxDZeaPLf6U9CU,203145
|
|
16
16
|
fastapi_voyager/web/quasar.min.js,sha256=h0ftyPMW_CRiyzeVfQqiup0vrVt4_QWojpqmpnpn07E,502974
|
|
17
|
-
fastapi_voyager/web/vue-main.js,sha256=
|
|
17
|
+
fastapi_voyager/web/vue-main.js,sha256=pEk6CKtsL-CBIWi6vdLvfa-BxYgIW2OFIvBVFNAWdjI,10149
|
|
18
18
|
fastapi_voyager/web/component/render-graph.js,sha256=e8Xgh2Kl-nYU0P1gstEmAepCgFnk2J6UdxW8TlMafGs,2322
|
|
19
19
|
fastapi_voyager/web/component/route-code-display.js,sha256=8NJPPjNRUC21gjpY8XYEQs4RBbhX1pCiqEhJp39ku6k,3678
|
|
20
20
|
fastapi_voyager/web/component/schema-code-display.js,sha256=UgFotzvqSuhnPXNOr6w_r1fV2_savRiCdokEvferutE,6244
|
|
@@ -26,8 +26,8 @@ fastapi_voyager/web/icon/favicon-16x16.png,sha256=JC07jEzfIYxBIoQn_FHXvyHuxESdhW
|
|
|
26
26
|
fastapi_voyager/web/icon/favicon-32x32.png,sha256=C7v1h58cfWOsiLp9yOIZtlx-dLasBcq3NqpHVGRmpt4,1859
|
|
27
27
|
fastapi_voyager/web/icon/favicon.ico,sha256=tZolYIXkkBcFiYl1A8ksaXN2VjGamzcSdes838dLvNc,15406
|
|
28
28
|
fastapi_voyager/web/icon/site.webmanifest,sha256=ep4Hzh9zhmiZF2At3Fp1dQrYQuYF_3ZPZxc1KcGBvwQ,263
|
|
29
|
-
fastapi_voyager-0.
|
|
30
|
-
fastapi_voyager-0.
|
|
31
|
-
fastapi_voyager-0.
|
|
32
|
-
fastapi_voyager-0.
|
|
33
|
-
fastapi_voyager-0.
|
|
29
|
+
fastapi_voyager-0.10.2.dist-info/METADATA,sha256=XzmFjR4-CyMlrJUO8mVze18hbRrrAcUmT8wWEnNTrXY,8895
|
|
30
|
+
fastapi_voyager-0.10.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
31
|
+
fastapi_voyager-0.10.2.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
|
|
32
|
+
fastapi_voyager-0.10.2.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
|
|
33
|
+
fastapi_voyager-0.10.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|