fastapi-voyager 0.12.12__py3-none-any.whl → 0.13.0__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/er_diagram.py +119 -0
- fastapi_voyager/server.py +22 -1
- fastapi_voyager/version.py +1 -1
- fastapi_voyager/voyager.py +0 -5
- fastapi_voyager/web/index.html +16 -3
- fastapi_voyager/web/store.js +6 -1
- fastapi_voyager/web/vue-main.js +105 -33
- {fastapi_voyager-0.12.12.dist-info → fastapi_voyager-0.13.0.dist-info}/METADATA +2 -2
- {fastapi_voyager-0.12.12.dist-info → fastapi_voyager-0.13.0.dist-info}/RECORD +12 -11
- {fastapi_voyager-0.12.12.dist-info → fastapi_voyager-0.13.0.dist-info}/WHEEL +0 -0
- {fastapi_voyager-0.12.12.dist-info → fastapi_voyager-0.13.0.dist-info}/entry_points.txt +0 -0
- {fastapi_voyager-0.12.12.dist-info → fastapi_voyager-0.13.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from fastapi_voyager.type import PK, FieldType, Link, LinkType, SchemaNode
|
|
4
|
+
from fastapi_voyager.type_helper import (
|
|
5
|
+
update_forward_refs,
|
|
6
|
+
full_class_name,
|
|
7
|
+
get_core_types,
|
|
8
|
+
get_type_name
|
|
9
|
+
)
|
|
10
|
+
from fastapi_voyager.render import Renderer
|
|
11
|
+
from fastapi_voyager.type import FieldInfo
|
|
12
|
+
from pydantic import BaseModel
|
|
13
|
+
from pydantic_resolve import ErDiagram, Entity
|
|
14
|
+
|
|
15
|
+
class VoyagerErDiagram:
|
|
16
|
+
def __init__(self,
|
|
17
|
+
er_diagram: ErDiagram,
|
|
18
|
+
show_fields: FieldType = 'single',
|
|
19
|
+
show_module: bool = False):
|
|
20
|
+
self.er_diagram = er_diagram
|
|
21
|
+
self.nodes: list[SchemaNode] = []
|
|
22
|
+
self.node_set: dict[str, SchemaNode] = {}
|
|
23
|
+
|
|
24
|
+
self.links: list[Link] = []
|
|
25
|
+
self.link_set: set[tuple[str, str]] = set()
|
|
26
|
+
|
|
27
|
+
self.fk_set: dict[str, set[str]] = {}
|
|
28
|
+
|
|
29
|
+
self.show_field = show_fields
|
|
30
|
+
self.show_module = show_module
|
|
31
|
+
|
|
32
|
+
def generate_node_head(self, link_name: str):
|
|
33
|
+
return f'{link_name}::{PK}'
|
|
34
|
+
|
|
35
|
+
def analysis_entity(self, entity: Entity):
|
|
36
|
+
schema = entity.kls
|
|
37
|
+
update_forward_refs(schema)
|
|
38
|
+
self.add_to_node_set(schema, fk_set=self.fk_set.get(full_class_name(schema)))
|
|
39
|
+
|
|
40
|
+
for relationship in entity.relationships:
|
|
41
|
+
annos = get_core_types(relationship.target_kls)
|
|
42
|
+
for anno in annos:
|
|
43
|
+
self.add_to_node_set(anno, fk_set=self.fk_set.get(full_class_name(anno)))
|
|
44
|
+
source_name = f'{full_class_name(schema)}::f{relationship.field}'
|
|
45
|
+
self.add_to_link_set(
|
|
46
|
+
source=source_name,
|
|
47
|
+
source_origin=full_class_name(schema),
|
|
48
|
+
target=self.generate_node_head(full_class_name(anno)),
|
|
49
|
+
target_origin=full_class_name(anno),
|
|
50
|
+
type='schema')
|
|
51
|
+
|
|
52
|
+
def add_to_node_set(self, schema, fk_set: set[str] | None = None) -> str:
|
|
53
|
+
"""
|
|
54
|
+
1. calc full_path, add to node_set
|
|
55
|
+
2. if duplicated, do nothing, else insert
|
|
56
|
+
2. return the full_path
|
|
57
|
+
"""
|
|
58
|
+
full_name = full_class_name(schema)
|
|
59
|
+
|
|
60
|
+
if full_name not in self.node_set:
|
|
61
|
+
# skip meta info for normal queries
|
|
62
|
+
self.node_set[full_name] = SchemaNode(
|
|
63
|
+
id=full_name,
|
|
64
|
+
module=schema.__module__,
|
|
65
|
+
name=schema.__name__,
|
|
66
|
+
fields=get_fields(schema, fk_set)
|
|
67
|
+
)
|
|
68
|
+
return full_name
|
|
69
|
+
|
|
70
|
+
def add_to_link_set(
|
|
71
|
+
self,
|
|
72
|
+
source: str,
|
|
73
|
+
source_origin: str,
|
|
74
|
+
target: str,
|
|
75
|
+
target_origin: str,
|
|
76
|
+
type: LinkType
|
|
77
|
+
) -> bool:
|
|
78
|
+
"""
|
|
79
|
+
1. add link to link_set
|
|
80
|
+
2. if duplicated, do nothing, else insert
|
|
81
|
+
"""
|
|
82
|
+
pair = (source, target)
|
|
83
|
+
if result := pair not in self.link_set:
|
|
84
|
+
self.link_set.add(pair)
|
|
85
|
+
self.links.append(Link(
|
|
86
|
+
source=source,
|
|
87
|
+
source_origin=source_origin,
|
|
88
|
+
target=target,
|
|
89
|
+
target_origin=target_origin,
|
|
90
|
+
type=type
|
|
91
|
+
))
|
|
92
|
+
return result
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def render_dot(self):
|
|
96
|
+
self.fk_set = {
|
|
97
|
+
full_class_name(entity.kls): set([rel.field for rel in entity.relationships])
|
|
98
|
+
for entity in self.er_diagram.configs
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
for entity in self.er_diagram.configs:
|
|
102
|
+
self.analysis_entity(entity)
|
|
103
|
+
renderer = Renderer(show_fields=self.show_field, show_module=self.show_module)
|
|
104
|
+
return renderer.render_dot([], [], list(self.node_set.values()), self.links)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_fields(schema: type[BaseModel], fk_set: set[str] | None = None) -> list[FieldInfo]:
|
|
108
|
+
|
|
109
|
+
fields: list[FieldInfo] = []
|
|
110
|
+
for k, v in schema.model_fields.items():
|
|
111
|
+
anno = v.annotation
|
|
112
|
+
fields.append(FieldInfo(
|
|
113
|
+
is_object=k in fk_set if fk_set is not None else False,
|
|
114
|
+
name=k,
|
|
115
|
+
from_base=False,
|
|
116
|
+
type_name=get_type_name(anno),
|
|
117
|
+
is_exclude=bool(v.exclude)
|
|
118
|
+
))
|
|
119
|
+
return fields
|
fastapi_voyager/server.py
CHANGED
|
@@ -12,6 +12,8 @@ from fastapi_voyager.type import CoreData, SchemaNode, Tag
|
|
|
12
12
|
from fastapi_voyager.type_helper import get_source, get_vscode_link
|
|
13
13
|
from fastapi_voyager.version import __version__
|
|
14
14
|
from fastapi_voyager.voyager import Voyager
|
|
15
|
+
from pydantic_resolve import ErDiagram
|
|
16
|
+
from fastapi_voyager.er_diagram import VoyagerErDiagram
|
|
15
17
|
|
|
16
18
|
WEB_DIR = Path(__file__).parent / "web"
|
|
17
19
|
WEB_DIR.mkdir(exist_ok=True)
|
|
@@ -44,6 +46,7 @@ class OptionParam(BaseModel):
|
|
|
44
46
|
version: str
|
|
45
47
|
initial_page_policy: INITIAL_PAGE_POLICY
|
|
46
48
|
swagger_url: str | None = None
|
|
49
|
+
has_er_diagram: bool = False
|
|
47
50
|
|
|
48
51
|
class Payload(BaseModel):
|
|
49
52
|
tags: list[str] | None = None
|
|
@@ -67,6 +70,12 @@ class SchemaSearchPayload(BaseModel): # leave tag, route out
|
|
|
67
70
|
hide_primitive_route: bool = False
|
|
68
71
|
show_module: bool = True
|
|
69
72
|
|
|
73
|
+
|
|
74
|
+
# ---------- er diagram ----------
|
|
75
|
+
class ErDiagramPayload(BaseModel):
|
|
76
|
+
show_fields: str = 'object'
|
|
77
|
+
show_module: bool = True
|
|
78
|
+
|
|
70
79
|
def create_voyager(
|
|
71
80
|
target_app: FastAPI,
|
|
72
81
|
module_color: dict[str, str] | None = None,
|
|
@@ -76,9 +85,19 @@ def create_voyager(
|
|
|
76
85
|
online_repo_url: str | None = None,
|
|
77
86
|
initial_page_policy: INITIAL_PAGE_POLICY = 'first',
|
|
78
87
|
ga_id: str | None = None,
|
|
88
|
+
er_diagram: ErDiagram | None = None,
|
|
79
89
|
) -> FastAPI:
|
|
80
90
|
router = APIRouter(tags=['fastapi-voyager'])
|
|
81
91
|
|
|
92
|
+
@router.post("/er-diagram", response_class=PlainTextResponse)
|
|
93
|
+
def get_er_diagram(payload: ErDiagramPayload) -> str:
|
|
94
|
+
if er_diagram:
|
|
95
|
+
return VoyagerErDiagram(
|
|
96
|
+
er_diagram,
|
|
97
|
+
show_fields=payload.show_fields,
|
|
98
|
+
show_module=payload.show_module ).render_dot()
|
|
99
|
+
return ''
|
|
100
|
+
|
|
82
101
|
@router.get("/dot", response_model=OptionParam)
|
|
83
102
|
def get_dot() -> str:
|
|
84
103
|
voyager = Voyager(module_color=module_color)
|
|
@@ -101,7 +120,9 @@ def create_voyager(
|
|
|
101
120
|
enable_brief_mode=bool(module_prefix),
|
|
102
121
|
version=__version__,
|
|
103
122
|
swagger_url=swagger_url,
|
|
104
|
-
initial_page_policy=initial_page_policy
|
|
123
|
+
initial_page_policy=initial_page_policy,
|
|
124
|
+
has_er_diagram=er_diagram is not None)
|
|
125
|
+
|
|
105
126
|
|
|
106
127
|
@router.post("/dot-search", response_model=SearchResultOptionParam)
|
|
107
128
|
def get_search_dot(payload: SchemaSearchPayload):
|
fastapi_voyager/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__all__ = ["__version__"]
|
|
2
|
-
__version__ = "0.
|
|
2
|
+
__version__ = "0.13.0"
|
fastapi_voyager/voyager.py
CHANGED
fastapi_voyager/web/index.html
CHANGED
|
@@ -181,6 +181,18 @@
|
|
|
181
181
|
label="Select field (optional)"
|
|
182
182
|
></q-select>
|
|
183
183
|
</div>
|
|
184
|
+
<div class="col-auto" v-if="store.state.config.has_er_diagram">
|
|
185
|
+
<q-btn-toggle
|
|
186
|
+
class="q-ml-lg"
|
|
187
|
+
v-model="store.state.mode"
|
|
188
|
+
push
|
|
189
|
+
toggle-color="primary"
|
|
190
|
+
:options="[
|
|
191
|
+
{label: 'Voyager', value: 'voyager'},
|
|
192
|
+
{label: 'ER diagram', value: 'er-diagram'},
|
|
193
|
+
]"
|
|
194
|
+
/>
|
|
195
|
+
</div>
|
|
184
196
|
<div class="col-auto">
|
|
185
197
|
<q-btn
|
|
186
198
|
dense
|
|
@@ -259,7 +271,7 @@
|
|
|
259
271
|
<q-splitter
|
|
260
272
|
v-model="store.state.leftPanel.width"
|
|
261
273
|
unit="px"
|
|
262
|
-
:limits="[200, 800]"
|
|
274
|
+
:limits="store.state.mode === 'voyager' ? [200, 800] : [0, 0]"
|
|
263
275
|
class="adjust-fit"
|
|
264
276
|
>
|
|
265
277
|
<template #before>
|
|
@@ -272,6 +284,7 @@
|
|
|
272
284
|
minHeight: 0,
|
|
273
285
|
height: '100%'
|
|
274
286
|
}"
|
|
287
|
+
v-show="store.state.mode === 'voyager'"
|
|
275
288
|
>
|
|
276
289
|
<q-scroll-area class="fit">
|
|
277
290
|
<q-list dense separator>
|
|
@@ -339,7 +352,7 @@
|
|
|
339
352
|
<div style="position: relative; width: 100%; height: 100%;">
|
|
340
353
|
<div id="graph" class="adjust-fit"></div>
|
|
341
354
|
<div style="position: absolute; left: 8px; top: 8px; z-index: 10; background: rgba(255,255,255,0.85); border-radius: 4px; padding: 2px 8px;">
|
|
342
|
-
<div class="q-mt-sm" v-if="store.state.modeControl.briefModeEnabled && store.state.search.mode === false">
|
|
355
|
+
<div class="q-mt-sm" v-if="store.state.modeControl.briefModeEnabled && store.state.search.mode === false && store.state.mode === 'voyager'">
|
|
343
356
|
<q-toggle
|
|
344
357
|
v-if="store.state.modeControl.briefModeEnabled"
|
|
345
358
|
dense
|
|
@@ -349,7 +362,7 @@
|
|
|
349
362
|
title="skip middle classes, config module_prefix to enable it"
|
|
350
363
|
/>
|
|
351
364
|
</div>
|
|
352
|
-
<div class="q-mt-sm" v-if="store.state.
|
|
365
|
+
<div class="q-mt-sm" v-if="store.state.search.mode === false && store.state.mode === 'voyager'">
|
|
353
366
|
<q-toggle
|
|
354
367
|
v-model="store.state.filter.hidePrimitiveRoute"
|
|
355
368
|
@update:model-value="(val) => toggleHidePrimitiveRoute(val)"
|
fastapi_voyager/web/store.js
CHANGED
|
@@ -7,8 +7,11 @@ const state = reactive({
|
|
|
7
7
|
|
|
8
8
|
version: '',
|
|
9
9
|
config: {
|
|
10
|
-
initial_page_policy: 'first'
|
|
10
|
+
initial_page_policy: 'first',
|
|
11
|
+
has_er_diagram: false,
|
|
11
12
|
},
|
|
13
|
+
|
|
14
|
+
mode: 'voyager', // voyager / er-diagram
|
|
12
15
|
|
|
13
16
|
previousTagRoute: { // for shift + click, store previous tag/route, and populate back when needed
|
|
14
17
|
hasValue: false,
|
|
@@ -34,6 +37,7 @@ const state = reactive({
|
|
|
34
37
|
// tags and routes
|
|
35
38
|
leftPanel: {
|
|
36
39
|
width: 300,
|
|
40
|
+
previousWidth: 300,
|
|
37
41
|
tags: null,
|
|
38
42
|
tag: null,
|
|
39
43
|
_tag: null,
|
|
@@ -98,6 +102,7 @@ const state = reactive({
|
|
|
98
102
|
|
|
99
103
|
})
|
|
100
104
|
|
|
105
|
+
|
|
101
106
|
const mutations = {
|
|
102
107
|
increment() {
|
|
103
108
|
state.item.count += 1
|
fastapi_voyager/web/vue-main.js
CHANGED
|
@@ -11,6 +11,40 @@ const app = createApp({
|
|
|
11
11
|
setup() {
|
|
12
12
|
let graphUI = null;
|
|
13
13
|
const allSchemaOptions = ref([]);
|
|
14
|
+
const erDiagramLoading = ref(false);
|
|
15
|
+
const erDiagramCache = ref("");
|
|
16
|
+
|
|
17
|
+
function initGraphUI() {
|
|
18
|
+
if (graphUI) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
graphUI = new GraphUI("#graph", {
|
|
22
|
+
onSchemaShiftClick: (id) => {
|
|
23
|
+
if (store.state.graph.schemaKeys.has(id)) {
|
|
24
|
+
store.state.previousTagRoute.tag = store.state.leftPanel.tag;
|
|
25
|
+
store.state.previousTagRoute.routeId = store.state.leftPanel.routeId;
|
|
26
|
+
store.state.previousTagRoute.hasValue = true;
|
|
27
|
+
store.state.search.mode = true;
|
|
28
|
+
store.state.search.schemaName = id;
|
|
29
|
+
onSearch();
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
onSchemaClick: (id) => {
|
|
33
|
+
resetDetailPanels();
|
|
34
|
+
if (store.state.graph.schemaKeys.has(id)) {
|
|
35
|
+
store.state.schemaDetail.schemaCodeName = id;
|
|
36
|
+
store.state.rightDrawer.drawer = true;
|
|
37
|
+
}
|
|
38
|
+
if (id in store.state.graph.routeItems) {
|
|
39
|
+
store.state.routeDetail.routeCodeId = id;
|
|
40
|
+
store.state.routeDetail.show = true;
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
resetCb: () => {
|
|
44
|
+
resetDetailPanels();
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
14
48
|
|
|
15
49
|
function rebuildSchemaOptions() {
|
|
16
50
|
const dict = store.state.graph.schemaMap || {};
|
|
@@ -209,6 +243,7 @@ const app = createApp({
|
|
|
209
243
|
data.enable_brief_mode || false;
|
|
210
244
|
store.state.version = data.version || "";
|
|
211
245
|
store.state.swagger.url = data.swagger_url || null;
|
|
246
|
+
store.state.config.has_er_diagram = data.has_er_diagram || false;
|
|
212
247
|
|
|
213
248
|
rebuildSchemaOptions();
|
|
214
249
|
|
|
@@ -251,6 +286,17 @@ const app = createApp({
|
|
|
251
286
|
}
|
|
252
287
|
|
|
253
288
|
async function onGenerate(resetZoom = true) {
|
|
289
|
+
switch (store.state.mode) {
|
|
290
|
+
case "voyager":
|
|
291
|
+
await renderVoyager(resetZoom);
|
|
292
|
+
break;
|
|
293
|
+
case "er-diagram":
|
|
294
|
+
await renderErDiagram(resetZoom);
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async function renderVoyager(resetZoom = true) {
|
|
254
300
|
const activeSchema = store.state.search.mode
|
|
255
301
|
? store.state.search.schemaName
|
|
256
302
|
: null;
|
|
@@ -269,6 +315,7 @@ const app = createApp({
|
|
|
269
315
|
hide_primitive_route: store.state.filter.hidePrimitiveRoute,
|
|
270
316
|
show_module: store.state.filter.showModule,
|
|
271
317
|
};
|
|
318
|
+
initGraphUI();
|
|
272
319
|
const res = await fetch("dot", {
|
|
273
320
|
method: "POST",
|
|
274
321
|
headers: { "Content-Type": "application/json" },
|
|
@@ -276,39 +323,6 @@ const app = createApp({
|
|
|
276
323
|
});
|
|
277
324
|
const dotText = await res.text();
|
|
278
325
|
|
|
279
|
-
// create graph instance once
|
|
280
|
-
if (!graphUI) {
|
|
281
|
-
graphUI = new GraphUI("#graph", {
|
|
282
|
-
onSchemaShiftClick: (id) => {
|
|
283
|
-
if (store.state.graph.schemaKeys.has(id)) {
|
|
284
|
-
|
|
285
|
-
console.log(store.state.leftPanel)
|
|
286
|
-
store.state.previousTagRoute.tag = store.state.leftPanel.tag;
|
|
287
|
-
store.state.previousTagRoute.routeId = store.state.leftPanel.routeId;
|
|
288
|
-
store.state.previousTagRoute.hasValue = true;
|
|
289
|
-
|
|
290
|
-
store.state.search.mode = true;
|
|
291
|
-
store.state.search.schemaName = id;
|
|
292
|
-
onSearch();
|
|
293
|
-
}
|
|
294
|
-
},
|
|
295
|
-
onSchemaClick: (id) => {
|
|
296
|
-
console.log("schema clicked:", id);
|
|
297
|
-
resetDetailPanels();
|
|
298
|
-
if (store.state.graph.schemaKeys.has(id)) {
|
|
299
|
-
store.state.schemaDetail.schemaCodeName = id;
|
|
300
|
-
store.state.rightDrawer.drawer = true;
|
|
301
|
-
}
|
|
302
|
-
if (id in store.state.graph.routeItems) {
|
|
303
|
-
store.state.routeDetail.routeCodeId = id;
|
|
304
|
-
store.state.routeDetail.show = true;
|
|
305
|
-
}
|
|
306
|
-
},
|
|
307
|
-
resetCb: () => {
|
|
308
|
-
resetDetailPanels();
|
|
309
|
-
},
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
326
|
await graphUI.render(dotText, resetZoom);
|
|
313
327
|
} catch (e) {
|
|
314
328
|
console.error("Generate failed", e);
|
|
@@ -331,6 +345,46 @@ const app = createApp({
|
|
|
331
345
|
onGenerate()
|
|
332
346
|
}
|
|
333
347
|
|
|
348
|
+
async function renderErDiagram(resetZoom = true) {
|
|
349
|
+
initGraphUI();
|
|
350
|
+
erDiagramLoading.value = true;
|
|
351
|
+
const payload = {
|
|
352
|
+
show_fields: store.state.filter.showFields,
|
|
353
|
+
show_module: store.state.filter.showModule,
|
|
354
|
+
};
|
|
355
|
+
try {
|
|
356
|
+
const res = await fetch("er-diagram", {
|
|
357
|
+
method: "POST",
|
|
358
|
+
headers: { "Content-Type": "application/json" },
|
|
359
|
+
body: JSON.stringify(payload),
|
|
360
|
+
});
|
|
361
|
+
if (!res.ok) {
|
|
362
|
+
throw new Error(`failed with status ${res.status}`);
|
|
363
|
+
}
|
|
364
|
+
const dot = await res.text();
|
|
365
|
+
erDiagramCache.value = dot;
|
|
366
|
+
await graphUI.render(dot, resetZoom);
|
|
367
|
+
} catch (err) {
|
|
368
|
+
console.error(err)
|
|
369
|
+
} finally {
|
|
370
|
+
erDiagramLoading.value = false;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async function onModeChange(val) {
|
|
375
|
+
if (val === "er-diagram") {
|
|
376
|
+
if (store.state.leftPanel.width > 0) {
|
|
377
|
+
store.state.leftPanel.previousWidth = store.state.leftPanel.width;
|
|
378
|
+
}
|
|
379
|
+
store.state.leftPanel.width = 0;
|
|
380
|
+
await renderErDiagram();
|
|
381
|
+
} else {
|
|
382
|
+
const fallbackWidth = store.state.leftPanel.previousWidth || 300;
|
|
383
|
+
store.state.leftPanel.width = fallbackWidth;
|
|
384
|
+
await onGenerate();
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
334
388
|
function toggleTag(tagName, expanded = null) {
|
|
335
389
|
if (expanded === true || store.state.search.mode === true) {
|
|
336
390
|
store.state.leftPanel._tag = tagName;
|
|
@@ -421,6 +475,22 @@ const app = createApp({
|
|
|
421
475
|
{ deep: false }
|
|
422
476
|
);
|
|
423
477
|
|
|
478
|
+
watch(
|
|
479
|
+
() => store.state.leftPanel.width,
|
|
480
|
+
(val) => {
|
|
481
|
+
if (store.state.mode === "voyager" && typeof val === "number" && val > 0) {
|
|
482
|
+
store.state.leftPanel.previousWidth = val;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
watch(
|
|
488
|
+
() => store.state.mode,
|
|
489
|
+
(mode) => {
|
|
490
|
+
onModeChange(mode);
|
|
491
|
+
}
|
|
492
|
+
);
|
|
493
|
+
|
|
424
494
|
watch(
|
|
425
495
|
() => store.state.search.schemaName,
|
|
426
496
|
(schemaId) => {
|
|
@@ -453,6 +523,8 @@ const app = createApp({
|
|
|
453
523
|
toggleShowField,
|
|
454
524
|
startDragDrawer,
|
|
455
525
|
toggleShowModule,
|
|
526
|
+
onModeChange,
|
|
527
|
+
renderErDiagram,
|
|
456
528
|
};
|
|
457
529
|
},
|
|
458
530
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-voyager
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0
|
|
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
|
|
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.14
|
|
20
20
|
Requires-Python: >=3.10
|
|
21
21
|
Requires-Dist: fastapi>=0.110
|
|
22
|
-
Requires-Dist: pydantic-resolve>=
|
|
22
|
+
Requires-Dist: pydantic-resolve>=2.2.3
|
|
23
23
|
Provides-Extra: dev
|
|
24
24
|
Requires-Dist: pytest; extra == 'dev'
|
|
25
25
|
Requires-Dist: ruff; extra == 'dev'
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
fastapi_voyager/__init__.py,sha256=kqwzThE1YhmQ_7jPKGlnWvqRGC-hFrRqq_lKhKaleYU,229
|
|
2
2
|
fastapi_voyager/cli.py,sha256=td3yIIigEomhSdDO-Xkh-CgpEwCafwlwnpvxnT9QsBo,10488
|
|
3
|
+
fastapi_voyager/er_diagram.py,sha256=GoTuriRTBk0Th_-ireuLHdzncjbk3WbIns8FVzeJz28,4064
|
|
3
4
|
fastapi_voyager/filter.py,sha256=AN_HIu8-DtKisIq5mFt7CnqRHtxKewedNGyyaI82hSY,11529
|
|
4
5
|
fastapi_voyager/module.py,sha256=h9YR3BpS-CAcJW9WCdVkF4opqwY32w9T67g9GfdLytk,3425
|
|
5
6
|
fastapi_voyager/render.py,sha256=O_HR8ypOrFhjejkBpKIH_8foB78DgzH0hvO-CWeYt0w,9976
|
|
6
|
-
fastapi_voyager/server.py,sha256=
|
|
7
|
+
fastapi_voyager/server.py,sha256=UZi-VdsurjDnqDgf3l5LfgWfZ4OxzilC_A_Ju6x9jQc,8592
|
|
7
8
|
fastapi_voyager/type.py,sha256=7EL1zaIwKVRGpLig7fqaOrZGN5k0Rm31C9COfck3CSs,1750
|
|
8
9
|
fastapi_voyager/type_helper.py,sha256=UTCFWluFeGdGkJX3wiE_bZ2EgZsu4JkmqHjsJVdG81Q,9953
|
|
9
|
-
fastapi_voyager/version.py,sha256=
|
|
10
|
-
fastapi_voyager/voyager.py,sha256=
|
|
10
|
+
fastapi_voyager/version.py,sha256=jHHU_F0-L0N4sHMgRz3qTQ0PPdki_7FNU9kR-UybYwM,49
|
|
11
|
+
fastapi_voyager/voyager.py,sha256=iWt-_QsoKavhb9ZawhU3W8gv3vTwn8PWTevg8BooyV8,13923
|
|
11
12
|
fastapi_voyager/web/graph-ui.js,sha256=hTsZO1Ly1JuoRg0kZWQ62jeLiD2kbnzACfbSPd0F95U,6634
|
|
12
13
|
fastapi_voyager/web/graphviz.svg.css,sha256=zDCjjpT0Idufu5YOiZI76PL70-avP3vTyzGPh9M85Do,1563
|
|
13
14
|
fastapi_voyager/web/graphviz.svg.js,sha256=wZwz_lBztoXmujEN21P0w-HMpdmbqPwTQQ6Ebxd9rGo,18569
|
|
14
|
-
fastapi_voyager/web/index.html,sha256=
|
|
15
|
+
fastapi_voyager/web/index.html,sha256=_VaTvHNnuu_KcG2lLYB_bk7MGkG-Ok0-WgAAdFFGB2s,19336
|
|
15
16
|
fastapi_voyager/web/quasar.min.css,sha256=F5jQe7X2XT54VlvAaa2V3GsBFdVD-vxDZeaPLf6U9CU,203145
|
|
16
17
|
fastapi_voyager/web/quasar.min.js,sha256=h0ftyPMW_CRiyzeVfQqiup0vrVt4_QWojpqmpnpn07E,502974
|
|
17
|
-
fastapi_voyager/web/store.js,sha256=
|
|
18
|
-
fastapi_voyager/web/vue-main.js,sha256=
|
|
18
|
+
fastapi_voyager/web/store.js,sha256=wMApI0p3C9rBAEi4cfgU__bYwBm2cGO-cwIKdOvQaEk,2065
|
|
19
|
+
fastapi_voyager/web/vue-main.js,sha256=BqFfjKRj1b2_nmR0Jmd2pL59Zk6qesrTNjRPpDRYMc4,16942
|
|
19
20
|
fastapi_voyager/web/component/demo.js,sha256=bQb16Un4XZ3Mf8qL6gvyrXe_mmA3V3mSIRMQAWg2MNk,352
|
|
20
21
|
fastapi_voyager/web/component/render-graph.js,sha256=e8Xgh2Kl-nYU0P1gstEmAepCgFnk2J6UdxW8TlMafGs,2322
|
|
21
22
|
fastapi_voyager/web/component/route-code-display.js,sha256=8NJPPjNRUC21gjpY8XYEQs4RBbhX1pCiqEhJp39ku6k,3678
|
|
@@ -27,8 +28,8 @@ fastapi_voyager/web/icon/favicon-16x16.png,sha256=JC07jEzfIYxBIoQn_FHXvyHuxESdhW
|
|
|
27
28
|
fastapi_voyager/web/icon/favicon-32x32.png,sha256=C7v1h58cfWOsiLp9yOIZtlx-dLasBcq3NqpHVGRmpt4,1859
|
|
28
29
|
fastapi_voyager/web/icon/favicon.ico,sha256=tZolYIXkkBcFiYl1A8ksaXN2VjGamzcSdes838dLvNc,15406
|
|
29
30
|
fastapi_voyager/web/icon/site.webmanifest,sha256=ep4Hzh9zhmiZF2At3Fp1dQrYQuYF_3ZPZxc1KcGBvwQ,263
|
|
30
|
-
fastapi_voyager-0.
|
|
31
|
-
fastapi_voyager-0.
|
|
32
|
-
fastapi_voyager-0.
|
|
33
|
-
fastapi_voyager-0.
|
|
34
|
-
fastapi_voyager-0.
|
|
31
|
+
fastapi_voyager-0.13.0.dist-info/METADATA,sha256=uKfxGwFJQZC7LY3Es7RuWhccR5OBPm8s1m7kmrjtokI,6521
|
|
32
|
+
fastapi_voyager-0.13.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
33
|
+
fastapi_voyager-0.13.0.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
|
|
34
|
+
fastapi_voyager-0.13.0.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
|
|
35
|
+
fastapi_voyager-0.13.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|