fastapi-voyager 0.15.3__py3-none-any.whl → 0.15.5__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/server.py +4 -1
- fastapi_voyager/version.py +1 -1
- fastapi_voyager/web/graph-ui.js +38 -3
- fastapi_voyager/web/index.html +21 -9
- fastapi_voyager/web/store.js +27 -6
- fastapi_voyager/web/vue-main.js +2 -0
- fastapi_voyager-0.15.5.dist-info/METADATA +303 -0
- {fastapi_voyager-0.15.3.dist-info → fastapi_voyager-0.15.5.dist-info}/RECORD +11 -11
- fastapi_voyager-0.15.3.dist-info/METADATA +0 -256
- {fastapi_voyager-0.15.3.dist-info → fastapi_voyager-0.15.5.dist-info}/WHEEL +0 -0
- {fastapi_voyager-0.15.3.dist-info → fastapi_voyager-0.15.5.dist-info}/entry_points.txt +0 -0
- {fastapi_voyager-0.15.3.dist-info → fastapi_voyager-0.15.5.dist-info}/licenses/LICENSE +0 -0
fastapi_voyager/server.py
CHANGED
|
@@ -19,6 +19,7 @@ WEB_DIR = Path(__file__).parent / "web"
|
|
|
19
19
|
WEB_DIR.mkdir(exist_ok=True)
|
|
20
20
|
|
|
21
21
|
GA_PLACEHOLDER = "<!-- GA_SNIPPET -->"
|
|
22
|
+
VERSION_PLACEHOLDER = "<!-- VERSION_PLACEHOLDER -->"
|
|
22
23
|
|
|
23
24
|
def _build_ga_snippet(ga_id: str | None) -> str:
|
|
24
25
|
if not ga_id:
|
|
@@ -197,7 +198,9 @@ def create_voyager(
|
|
|
197
198
|
index_file = WEB_DIR / "index.html"
|
|
198
199
|
if index_file.exists():
|
|
199
200
|
content = index_file.read_text(encoding="utf-8")
|
|
200
|
-
|
|
201
|
+
content = content.replace(GA_PLACEHOLDER, _build_ga_snippet(ga_id))
|
|
202
|
+
content = content.replace(VERSION_PLACEHOLDER, f"?v={__version__}")
|
|
203
|
+
return content
|
|
201
204
|
# fallback simple page if index.html missing
|
|
202
205
|
return """
|
|
203
206
|
<!doctype html>
|
fastapi_voyager/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__all__ = ["__version__"]
|
|
2
|
-
__version__ = "0.15.
|
|
2
|
+
__version__ = "0.15.5"
|
fastapi_voyager/web/graph-ui.js
CHANGED
|
@@ -69,10 +69,45 @@ export class GraphUI {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
highlightSchemaBanner(node) {
|
|
72
|
+
// Get all polygons in the node
|
|
72
73
|
const polygons = node.querySelectorAll("polygon")
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
|
|
75
|
+
// The first polygon is typically the outer frame of the entire node
|
|
76
|
+
const outerFrame = polygons[0]
|
|
77
|
+
// The second polygon is typically the title background
|
|
78
|
+
const titleBg = polygons[1]
|
|
79
|
+
|
|
80
|
+
if (outerFrame) {
|
|
81
|
+
// Save original attributes for potential restoration
|
|
82
|
+
if (!outerFrame.hasAttribute("data-original-stroke")) {
|
|
83
|
+
outerFrame.setAttribute("data-original-stroke", outerFrame.getAttribute("stroke") || "")
|
|
84
|
+
outerFrame.setAttribute(
|
|
85
|
+
"data-original-stroke-width",
|
|
86
|
+
outerFrame.getAttribute("stroke-width") || "1"
|
|
87
|
+
)
|
|
88
|
+
outerFrame.setAttribute("data-original-fill", outerFrame.getAttribute("fill") || "")
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Apply bold purple border to the outer frame
|
|
92
|
+
outerFrame.setAttribute("stroke", "#822dba")
|
|
93
|
+
outerFrame.setAttribute("stroke-width", "3.0")
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (titleBg) {
|
|
97
|
+
// Save original attributes
|
|
98
|
+
if (!titleBg.hasAttribute("data-original-stroke")) {
|
|
99
|
+
titleBg.setAttribute("data-original-stroke", titleBg.getAttribute("stroke") || "")
|
|
100
|
+
titleBg.setAttribute(
|
|
101
|
+
"data-original-stroke-width",
|
|
102
|
+
titleBg.getAttribute("stroke-width") || "1"
|
|
103
|
+
)
|
|
104
|
+
titleBg.setAttribute("data-original-fill", titleBg.getAttribute("fill") || "")
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Apply purple background to title
|
|
108
|
+
titleBg.setAttribute("fill", "#822dba")
|
|
109
|
+
// Also update the stroke to match
|
|
110
|
+
titleBg.setAttribute("stroke", "#822dba")
|
|
76
111
|
}
|
|
77
112
|
}
|
|
78
113
|
|
fastapi_voyager/web/index.html
CHANGED
|
@@ -2,8 +2,14 @@
|
|
|
2
2
|
<head>
|
|
3
3
|
<title>FastAPI Voyager</title>
|
|
4
4
|
<meta name="theme-color" content="#ffffff" />
|
|
5
|
-
<link
|
|
6
|
-
|
|
5
|
+
<link
|
|
6
|
+
rel="stylesheet"
|
|
7
|
+
href="fastapi-voyager-static/graphviz.svg.css<!-- VERSION_PLACEHOLDER -->"
|
|
8
|
+
/>
|
|
9
|
+
<link
|
|
10
|
+
rel="stylesheet"
|
|
11
|
+
href="fastapi-voyager-static/quasar.min.css<!-- VERSION_PLACEHOLDER -->"
|
|
12
|
+
/>
|
|
7
13
|
<!-- App Icons / Favicons -->
|
|
8
14
|
<link
|
|
9
15
|
rel="apple-touch-icon"
|
|
@@ -111,8 +117,6 @@
|
|
|
111
117
|
border-radius: 50%;
|
|
112
118
|
background: #009485;
|
|
113
119
|
color: white;
|
|
114
|
-
border: 2px solid white;
|
|
115
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
116
120
|
cursor: pointer;
|
|
117
121
|
display: flex;
|
|
118
122
|
align-items: center;
|
|
@@ -284,10 +288,15 @@
|
|
|
284
288
|
aria-label="Help"
|
|
285
289
|
style="margin-right: 50px; margin-left: 20px"
|
|
286
290
|
>
|
|
287
|
-
<q-tooltip
|
|
291
|
+
<q-tooltip
|
|
292
|
+
anchor="bottom middle"
|
|
293
|
+
self="top middle"
|
|
294
|
+
:offset="[0,8]"
|
|
295
|
+
style="background-color: #f5f5f5; color: black; border: 1px solid #666"
|
|
296
|
+
>
|
|
288
297
|
<div
|
|
289
298
|
class="column items-start text-left"
|
|
290
|
-
style="line-height: 1.4; font-size:
|
|
299
|
+
style="line-height: 1.4; font-size: 14px"
|
|
291
300
|
>
|
|
292
301
|
<ul>
|
|
293
302
|
<li>scroll to zoom in/out</li>
|
|
@@ -571,7 +580,7 @@
|
|
|
571
580
|
</q-dialog>
|
|
572
581
|
</div>
|
|
573
582
|
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
|
|
574
|
-
<script src="fastapi-voyager-static/quasar.min.js"></script>
|
|
583
|
+
<script src="fastapi-voyager-static/quasar.min.js<!-- VERSION_PLACEHOLDER -->"></script>
|
|
575
584
|
<script
|
|
576
585
|
src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"
|
|
577
586
|
integrity="sha512-egJ/Y+22P9NQ9aIyVCh0VCOsfydyn8eNmqBy+y2CnJG+fpRIxXMS6jbWP8tVKp0jp+NO5n8WtMUAnNnGoJKi4w=="
|
|
@@ -593,7 +602,7 @@
|
|
|
593
602
|
></script>
|
|
594
603
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.13/jquery.mousewheel.min.js"></script>
|
|
595
604
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-color/2.1.2/jquery.color.min.js"></script>
|
|
596
|
-
<script src="fastapi-voyager-static/graphviz.svg.js"></script>
|
|
605
|
+
<script src="fastapi-voyager-static/graphviz.svg.js<!-- VERSION_PLACEHOLDER -->"></script>
|
|
597
606
|
<!-- highlight.js minimal ES module load (python only) -->
|
|
598
607
|
<link
|
|
599
608
|
rel="stylesheet"
|
|
@@ -615,7 +624,10 @@
|
|
|
615
624
|
}
|
|
616
625
|
})
|
|
617
626
|
</script>
|
|
618
|
-
<script
|
|
627
|
+
<script
|
|
628
|
+
type="module"
|
|
629
|
+
src="fastapi-voyager-static/vue-main.js<!-- VERSION_PLACEHOLDER -->"
|
|
630
|
+
></script>
|
|
619
631
|
|
|
620
632
|
<!-- GA_SNIPPET -->
|
|
621
633
|
</body>
|
fastapi_voyager/web/store.js
CHANGED
|
@@ -120,22 +120,23 @@ const getters = {
|
|
|
120
120
|
|
|
121
121
|
const actions = {
|
|
122
122
|
/**
|
|
123
|
-
* Read tag and
|
|
124
|
-
* @returns {{ tag: string|null, route: string|null }}
|
|
123
|
+
* Read tag, route and mode from URL query parameters
|
|
124
|
+
* @returns {{ tag: string|null, route: string|null, mode: string|null }}
|
|
125
125
|
*/
|
|
126
126
|
readQuerySelection() {
|
|
127
127
|
if (typeof window === "undefined") {
|
|
128
|
-
return { tag: null, route: null }
|
|
128
|
+
return { tag: null, route: null, mode: null }
|
|
129
129
|
}
|
|
130
130
|
const params = new URLSearchParams(window.location.search)
|
|
131
131
|
return {
|
|
132
132
|
tag: params.get("tag") || null,
|
|
133
133
|
route: params.get("route") || null,
|
|
134
|
+
mode: params.get("mode") || null,
|
|
134
135
|
}
|
|
135
136
|
},
|
|
136
137
|
|
|
137
138
|
/**
|
|
138
|
-
* Sync current tag and
|
|
139
|
+
* Sync current tag, route and mode selection to URL
|
|
139
140
|
* Updates browser URL without reloading the page
|
|
140
141
|
*/
|
|
141
142
|
syncSelectionToUrl() {
|
|
@@ -153,6 +154,12 @@ const actions = {
|
|
|
153
154
|
} else {
|
|
154
155
|
params.delete("route")
|
|
155
156
|
}
|
|
157
|
+
// Always sync mode to URL for consistency
|
|
158
|
+
if (state.mode) {
|
|
159
|
+
params.set("mode", state.mode)
|
|
160
|
+
} else {
|
|
161
|
+
params.delete("mode")
|
|
162
|
+
}
|
|
156
163
|
const hash = window.location.hash || ""
|
|
157
164
|
const search = params.toString()
|
|
158
165
|
const base = window.location.pathname
|
|
@@ -162,7 +169,7 @@ const actions = {
|
|
|
162
169
|
|
|
163
170
|
/**
|
|
164
171
|
* Apply selection from URL query parameters to state
|
|
165
|
-
* @param {{ tag: string|null, route: string|null }} selection
|
|
172
|
+
* @param {{ tag: string|null, route: string|null, mode: string|null }} selection
|
|
166
173
|
* @returns {boolean} - true if any selection was applied
|
|
167
174
|
*/
|
|
168
175
|
applySelectionFromQuery(selection) {
|
|
@@ -181,6 +188,11 @@ const actions = {
|
|
|
181
188
|
state.leftPanel._tag = inferredTag
|
|
182
189
|
}
|
|
183
190
|
}
|
|
191
|
+
// Apply mode from URL if it's valid
|
|
192
|
+
if (selection.mode === "voyager" || selection.mode === "er-diagram") {
|
|
193
|
+
state.mode = selection.mode
|
|
194
|
+
applied = true
|
|
195
|
+
}
|
|
184
196
|
return applied
|
|
185
197
|
},
|
|
186
198
|
|
|
@@ -304,7 +316,16 @@ const actions = {
|
|
|
304
316
|
return
|
|
305
317
|
} else {
|
|
306
318
|
state.config.initial_page_policy = data.initial_page_policy
|
|
307
|
-
|
|
319
|
+
// Check if mode was applied from URL even if tag/route wasn't
|
|
320
|
+
if (
|
|
321
|
+
querySelection.mode &&
|
|
322
|
+
(querySelection.mode === "voyager" || querySelection.mode === "er-diagram")
|
|
323
|
+
) {
|
|
324
|
+
this.syncSelectionToUrl()
|
|
325
|
+
onGenerate()
|
|
326
|
+
return
|
|
327
|
+
}
|
|
328
|
+
renderBasedOnInitialPolicy(onGenerate)
|
|
308
329
|
}
|
|
309
330
|
|
|
310
331
|
// default route options placeholder
|
fastapi_voyager/web/vue-main.js
CHANGED
|
@@ -163,12 +163,14 @@ const app = createApp({
|
|
|
163
163
|
store.state.leftPanel.previousWidth = store.state.leftPanel.width
|
|
164
164
|
}
|
|
165
165
|
store.state.leftPanel.width = 0
|
|
166
|
+
store.actions.syncSelectionToUrl()
|
|
166
167
|
await renderErDiagram()
|
|
167
168
|
} else {
|
|
168
169
|
store.state.search.invisible = false
|
|
169
170
|
|
|
170
171
|
const fallbackWidth = store.state.leftPanel.previousWidth || 300
|
|
171
172
|
store.state.leftPanel.width = fallbackWidth
|
|
173
|
+
store.actions.syncSelectionToUrl()
|
|
172
174
|
await onGenerate()
|
|
173
175
|
}
|
|
174
176
|
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastapi-voyager
|
|
3
|
+
Version: 0.15.5
|
|
4
|
+
Summary: Visualize FastAPI application's routing tree and dependencies
|
|
5
|
+
Project-URL: Homepage, https://github.com/allmonday/fastapi-voyager
|
|
6
|
+
Project-URL: Source, https://github.com/allmonday/fastapi-voyager
|
|
7
|
+
Author-email: Tangkikodo <allmonday@126.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: fastapi,openapi,routing,visualization
|
|
11
|
+
Classifier: Framework :: FastAPI
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: fastapi>=0.110
|
|
22
|
+
Requires-Dist: jinja2>=3.0.0
|
|
23
|
+
Requires-Dist: pydantic-resolve>=2.4.3
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
26
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
27
|
+
Requires-Dist: uvicorn; extra == 'dev'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
[](https://pypi.python.org/pypi/fastapi-voyager)
|
|
31
|
+

|
|
32
|
+
[](https://pepy.tech/projects/fastapi-voyager)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# FastAPI Voyager
|
|
36
|
+
|
|
37
|
+
Visualize your FastAPI endpoints and explore them interactively.
|
|
38
|
+
|
|
39
|
+
Its vision is to make code easier to read and understand, serving as an ideal documentation tool.
|
|
40
|
+
|
|
41
|
+
> This repo is still in early stage, it supports Pydantic v2 only.
|
|
42
|
+
|
|
43
|
+
- **Live Demo**: https://www.newsyeah.fun/voyager/
|
|
44
|
+
- **Example Source**: [composition-oriented-development-pattern](https://github.com/allmonday/composition-oriented-development-pattern)
|
|
45
|
+
|
|
46
|
+
<img width="1597" height="933" alt="fastapi-voyager overview" src="https://github.com/user-attachments/assets/020bf5b2-6c69-44bf-ba1f-39389d388d27" />
|
|
47
|
+
|
|
48
|
+
## Table of Contents
|
|
49
|
+
|
|
50
|
+
- [Quick Start](#quick-start)
|
|
51
|
+
- [Installation](#installation)
|
|
52
|
+
- [Features](#features)
|
|
53
|
+
- [Command Line Usage](#command-line-usage)
|
|
54
|
+
- [About pydantic-resolve](#about-pydantic-resolve)
|
|
55
|
+
- [Development](#development)
|
|
56
|
+
- [Dependencies](#dependencies)
|
|
57
|
+
- [Credits](#credits)
|
|
58
|
+
|
|
59
|
+
## Quick Start
|
|
60
|
+
|
|
61
|
+
With simple configuration, fastapi-voyager can be embedded into FastAPI:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from fastapi import FastAPI
|
|
65
|
+
from fastapi_voyager import create_voyager
|
|
66
|
+
|
|
67
|
+
app = FastAPI()
|
|
68
|
+
|
|
69
|
+
app.mount('/voyager',
|
|
70
|
+
create_voyager(
|
|
71
|
+
app,
|
|
72
|
+
module_color={'src.services': 'tomato'},
|
|
73
|
+
module_prefix='src.services',
|
|
74
|
+
swagger_url="/docs",
|
|
75
|
+
ga_id="G-XXXXXXXXVL",
|
|
76
|
+
initial_page_policy='first',
|
|
77
|
+
online_repo_url='https://github.com/allmonday/composition-oriented-development-pattern/blob/master',
|
|
78
|
+
enable_pydantic_resolve_meta=True))
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Visit `http://localhost:8000/voyager` to explore your API visually.
|
|
82
|
+
|
|
83
|
+
[View full example](https://github.com/allmonday/composition-oriented-development-pattern/blob/master/src/main.py#L48)
|
|
84
|
+
|
|
85
|
+
## Installation
|
|
86
|
+
|
|
87
|
+
### Install via pip
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install fastapi-voyager
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Install via uv
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
uv add fastapi-voyager
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Run with CLI
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
voyager -m path.to.your.app.module --server
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
For sub-application scenarios (e.g., `app.mount("/api", api)`), specify the app name:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
voyager -m path.to.your.app.module --server --app api
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
> **Note**: [Sub-Application mounts](https://fastapi.tiangolo.com/advanced/sub-applications/) are not supported yet, but you can specify the name of the FastAPI application with `--app`. Only a single application (default: `app`) can be selected.
|
|
112
|
+
|
|
113
|
+
## Features
|
|
114
|
+
|
|
115
|
+
fastapi-voyager is designed for scenarios using FastAPI as internal API integration endpoints. It helps visualize dependencies and serves as an architecture tool to identify implementation issues such as wrong relationships, overfetching, and more.
|
|
116
|
+
|
|
117
|
+
**Best Practice**: When building view models following the ER model pattern, fastapi-voyager can fully realize its potential - quickly identifying which APIs use specific entities and vice versa.
|
|
118
|
+
|
|
119
|
+
### Highlight Nodes and Links
|
|
120
|
+
|
|
121
|
+
Click a node to highlight its upstream and downstream nodes. Figure out the related models of one page, or how many pages are related with one model.
|
|
122
|
+
|
|
123
|
+
<img width="1100" height="700" alt="highlight nodes and dependencies" src="https://github.com/user-attachments/assets/3e0369ea-5fa4-469a-82c1-ed57d407e53d" />
|
|
124
|
+
|
|
125
|
+
### View Source Code
|
|
126
|
+
|
|
127
|
+
Double-click a node or route to show source code or open the file in VSCode.
|
|
128
|
+
|
|
129
|
+
<img width="1297" height="940" alt="view source code" src="https://github.com/user-attachments/assets/c8bb2e7d-b727-42a6-8c9e-64dce297d2d8" />
|
|
130
|
+
|
|
131
|
+
### Quick Search
|
|
132
|
+
|
|
133
|
+
Search schemas by name and display their upstream and downstream dependencies. Use `Shift + Click` on any node to quickly search for it.
|
|
134
|
+
|
|
135
|
+
<img width="1587" height="873" alt="quick search functionality" src="https://github.com/user-attachments/assets/ee4716f3-233d-418f-bc0e-3b214d1498f7" />
|
|
136
|
+
|
|
137
|
+
### Display ER Diagram
|
|
138
|
+
|
|
139
|
+
ER diagram is a feature from pydantic-resolve which provides a solid expression for business descriptions. You can visualize application-level entity relationship diagrams.
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from pydantic_resolve import ErDiagram, Entity, Relationship
|
|
143
|
+
|
|
144
|
+
diagram = ErDiagram(
|
|
145
|
+
configs=[
|
|
146
|
+
Entity(
|
|
147
|
+
kls=Team,
|
|
148
|
+
relationships=[
|
|
149
|
+
Relationship(field='id', target_kls=list[Sprint], loader=sprint_loader.team_to_sprint_loader),
|
|
150
|
+
Relationship(field='id', target_kls=list[User], loader=user_loader.team_to_user_loader)
|
|
151
|
+
]
|
|
152
|
+
),
|
|
153
|
+
Entity(
|
|
154
|
+
kls=Sprint,
|
|
155
|
+
relationships=[
|
|
156
|
+
Relationship(field='id', target_kls=list[Story], loader=story_loader.sprint_to_story_loader)
|
|
157
|
+
]
|
|
158
|
+
),
|
|
159
|
+
Entity(
|
|
160
|
+
kls=Story,
|
|
161
|
+
relationships=[
|
|
162
|
+
Relationship(field='id', target_kls=list[Task], loader=task_loader.story_to_task_loader),
|
|
163
|
+
Relationship(field='owner_id', target_kls=User, loader=user_loader.user_batch_loader)
|
|
164
|
+
]
|
|
165
|
+
),
|
|
166
|
+
Entity(
|
|
167
|
+
kls=Task,
|
|
168
|
+
relationships=[
|
|
169
|
+
Relationship(field='owner_id', target_kls=User, loader=user_loader.user_batch_loader)
|
|
170
|
+
]
|
|
171
|
+
)
|
|
172
|
+
]
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Display in voyager
|
|
176
|
+
app.mount('/voyager',
|
|
177
|
+
create_voyager(
|
|
178
|
+
app,
|
|
179
|
+
er_diagram=diagram
|
|
180
|
+
))
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
<img width="1276" height="613" alt="ER diagram visualization" src="https://github.com/user-attachments/assets/ea0091bb-ee11-4f71-8be3-7129d956c910" />
|
|
184
|
+
|
|
185
|
+
### Show Pydantic Resolve Meta Info
|
|
186
|
+
|
|
187
|
+
Set `enable_pydantic_resolve_meta=True` in `create_voyager`, then toggle the "pydantic resolve meta" button to visualize resolve/post/expose/collect operations.
|
|
188
|
+
|
|
189
|
+
<img width="1604" height="535" alt="pydantic resolve meta information" src="https://github.com/user-attachments/assets/d1639555-af41-4a08-9970-4b8ef314596a" />
|
|
190
|
+
|
|
191
|
+
## Command Line Usage
|
|
192
|
+
|
|
193
|
+
### Start Server
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
# Open in browser (default port 8000)
|
|
197
|
+
voyager -m tests.demo --server
|
|
198
|
+
|
|
199
|
+
# Custom port
|
|
200
|
+
voyager -m tests.demo --server --port=8002
|
|
201
|
+
|
|
202
|
+
# Specify app name
|
|
203
|
+
voyager -m tests.demo --server --app my_app
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Generate DOT File
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
# Generate .dot file
|
|
210
|
+
voyager -m tests.demo
|
|
211
|
+
|
|
212
|
+
# Specify app
|
|
213
|
+
voyager -m tests.demo --app my_app
|
|
214
|
+
|
|
215
|
+
# Filter by schema
|
|
216
|
+
voyager -m tests.demo --schema Task
|
|
217
|
+
|
|
218
|
+
# Show all fields
|
|
219
|
+
voyager -m tests.demo --show_fields all
|
|
220
|
+
|
|
221
|
+
# Custom module colors
|
|
222
|
+
voyager -m tests.demo --module_color=tests.demo:red --module_color=tests.service:tomato
|
|
223
|
+
|
|
224
|
+
# Output to file
|
|
225
|
+
voyager -m tests.demo -o my_visualization.dot
|
|
226
|
+
|
|
227
|
+
# Version and help
|
|
228
|
+
voyager --version
|
|
229
|
+
voyager --help
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## About pydantic-resolve
|
|
233
|
+
|
|
234
|
+
pydantic-resolve is a lightweight tool designed to build complex, nested data in a simple, declarative way. In v2, it introduced an important feature: **ER Diagram**, and fastapi-voyager has supported this feature, allowing for a clearer understanding of business relationships.
|
|
235
|
+
|
|
236
|
+
The ~~`@ensure_subset` decorator~~ `DefineSubset` metaclass helps safely pick fields from the 'source class' while **indicating the reference** from the current class to the base class.
|
|
237
|
+
|
|
238
|
+
Developers can use fastapi-voyager without needing to know anything about pydantic-resolve, but I still highly recommend everyone to give it a try.
|
|
239
|
+
|
|
240
|
+
## Development
|
|
241
|
+
|
|
242
|
+
### Setup Development Environment
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
# Fork and clone the repository
|
|
246
|
+
git clone https://github.com/your-username/fastapi-voyager.git
|
|
247
|
+
cd fastapi-voyager
|
|
248
|
+
|
|
249
|
+
# Install uv
|
|
250
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
251
|
+
|
|
252
|
+
# Create virtual environment and install dependencies
|
|
253
|
+
uv venv
|
|
254
|
+
source .venv/bin/activate
|
|
255
|
+
uv pip install ".[dev]"
|
|
256
|
+
|
|
257
|
+
# Run development server
|
|
258
|
+
uvicorn tests.programatic:app --reload
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Visit `http://localhost:8000/voyager` to see changes.
|
|
262
|
+
|
|
263
|
+
### Setup Git Hooks (Optional)
|
|
264
|
+
|
|
265
|
+
Enable automatic code formatting before commits:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
./setup-hooks.sh
|
|
269
|
+
# or manually:
|
|
270
|
+
git config core.hooksPath .githooks
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
This will run Prettier automatically before each commit. See [`.githooks/README.md`](./.githooks/README.md) for details.
|
|
274
|
+
|
|
275
|
+
### Project Structure
|
|
276
|
+
|
|
277
|
+
**Frontend:**
|
|
278
|
+
- `src/fastapi_voyager/web/vue-main.js` - Main JavaScript entry
|
|
279
|
+
|
|
280
|
+
**Backend:**
|
|
281
|
+
- `voyager.py` - Main entry point
|
|
282
|
+
- `render.py` - Generate DOT files
|
|
283
|
+
- `server.py` - Server mode
|
|
284
|
+
|
|
285
|
+
## Roadmap
|
|
286
|
+
|
|
287
|
+
- [Ideas](./docs/idea.md)
|
|
288
|
+
- [Changelog & Roadmap](./docs/changelog.md)
|
|
289
|
+
|
|
290
|
+
## Dependencies
|
|
291
|
+
|
|
292
|
+
- [FastAPI](https://fastapi.tiangolo.com/)
|
|
293
|
+
- [pydantic-resolve](https://github.com/allmonday/pydantic-resolve)
|
|
294
|
+
- [Quasar Framework](https://quasar.dev/)
|
|
295
|
+
|
|
296
|
+
## Credits
|
|
297
|
+
|
|
298
|
+
- [graphql-voyager](https://apis.guru/graphql-voyager/) - Thanks for inspiration
|
|
299
|
+
- [vscode-interactive-graphviz](https://github.com/tintinweb/vscode-interactive-graphviz) - Thanks for web visualization
|
|
300
|
+
|
|
301
|
+
## License
|
|
302
|
+
|
|
303
|
+
MIT License
|
|
@@ -6,10 +6,10 @@ fastapi_voyager/module.py,sha256=h9YR3BpS-CAcJW9WCdVkF4opqwY32w9T67g9GfdLytk,342
|
|
|
6
6
|
fastapi_voyager/pydantic_resolve_util.py,sha256=r4Rq7BtBcFOMV7O2Ab9TwLyRNL1yNDiQlGUVybf-sXs,3524
|
|
7
7
|
fastapi_voyager/render.py,sha256=5tTuvvCCUwFCq3WJGT1rfTSW41mSDoVyJwMyQGrmhiQ,17271
|
|
8
8
|
fastapi_voyager/render_style.py,sha256=mPOuChEl71-3agCbPwkMt2sFmax2AEKDI6dK90eFPRc,2552
|
|
9
|
-
fastapi_voyager/server.py,sha256=
|
|
9
|
+
fastapi_voyager/server.py,sha256=E0gGU7D1pBvnV7gFLBUvnkwtiBFbU1PbxEXHHSIA1oA,9115
|
|
10
10
|
fastapi_voyager/type.py,sha256=zluWvh5vpnjXJ9aAmyNJTSmXZPjAHCvgRT5oQRAjHrg,2104
|
|
11
11
|
fastapi_voyager/type_helper.py,sha256=FmfrZAI3Z4uDdh3sH_kH7UGoY6yNVPapneSN86qY_wo,10209
|
|
12
|
-
fastapi_voyager/version.py,sha256=
|
|
12
|
+
fastapi_voyager/version.py,sha256=7F_t4pHiI_38oJ-3IRvYl5xfn2qV8LfVfFPlM9BacI0,49
|
|
13
13
|
fastapi_voyager/voyager.py,sha256=4vonmL-xt54C5San-DRBq4mjoV8Q96eoWRy68MJ1IJw,14169
|
|
14
14
|
fastapi_voyager/templates/dot/cluster.j2,sha256=I2z9KkfCzmAtqXe0gXBnxnOfBXUSpdlATs3uf-O8_B8,307
|
|
15
15
|
fastapi_voyager/templates/dot/cluster_container.j2,sha256=2tH1mOJvPoVKE_aHVMR3t06TfH_dYa9OeH6DBqSHt_A,204
|
|
@@ -24,14 +24,14 @@ fastapi_voyager/templates/html/pydantic_meta.j2,sha256=_tsSqjucs_QrAlPIVRy9u6I2-
|
|
|
24
24
|
fastapi_voyager/templates/html/schema_field_row.j2,sha256=KfKexHO_QJV-OIJS0eiY_7fqA8031fWpD2g2wTv4BuE,111
|
|
25
25
|
fastapi_voyager/templates/html/schema_header.j2,sha256=9WpuHLy3Zbv5GHG08qqaj5Xf-gaR-79ErBYuANZp7iA,179
|
|
26
26
|
fastapi_voyager/templates/html/schema_table.j2,sha256=rzphiGk1il7uv4Gr2p_HLPHqyLZk63vLrGAmIduTdSE,117
|
|
27
|
-
fastapi_voyager/web/graph-ui.js,sha256=
|
|
27
|
+
fastapi_voyager/web/graph-ui.js,sha256=4gEkXTgbA6CouD4IDMW5yKYfJTxHN2vL9G0CAr6w4qA,7662
|
|
28
28
|
fastapi_voyager/web/graphviz.svg.css,sha256=K218ov_mdSe3ga4KwhiBB92ynVvm5zaAk9_D9a3d8hE,1546
|
|
29
29
|
fastapi_voyager/web/graphviz.svg.js,sha256=deI815RgxpZ3_MpELeV-TBYy2MVuUvZtQOHfS3aeXHY,18203
|
|
30
|
-
fastapi_voyager/web/index.html,sha256=
|
|
30
|
+
fastapi_voyager/web/index.html,sha256=wM9vJ_UfHR8p98F6SEMCKKjJcBEl0EyosWuPqVZYXvA,23496
|
|
31
31
|
fastapi_voyager/web/quasar.min.css,sha256=F5jQe7X2XT54VlvAaa2V3GsBFdVD-vxDZeaPLf6U9CU,203145
|
|
32
32
|
fastapi_voyager/web/quasar.min.js,sha256=h0ftyPMW_CRiyzeVfQqiup0vrVt4_QWojpqmpnpn07E,502974
|
|
33
|
-
fastapi_voyager/web/store.js,sha256=
|
|
34
|
-
fastapi_voyager/web/vue-main.js,sha256=
|
|
33
|
+
fastapi_voyager/web/store.js,sha256=vwiqeLw7DfTOUDu9oN7b6nSBw8KG6W-5Dc-voQ1leL8,15113
|
|
34
|
+
fastapi_voyager/web/vue-main.js,sha256=4lJi6ADrcaOzjXcQePkp7CyiCkAnvhnO-nkF3E6G3s4,10650
|
|
35
35
|
fastapi_voyager/web/component/demo.js,sha256=sAklFGhKGmMy9-ofgOw2oPIidAoIOgHu6yvV51L_MAA,350
|
|
36
36
|
fastapi_voyager/web/component/render-graph.js,sha256=9wnO70n3eyPKTpa744idgs5PSwgvzbfv4InZ68eEOKs,2454
|
|
37
37
|
fastapi_voyager/web/component/route-code-display.js,sha256=a823nBz3EEjutW2pfi73rcF3hodCBmgYNmuZi94sXE4,3615
|
|
@@ -43,8 +43,8 @@ fastapi_voyager/web/icon/favicon-16x16.png,sha256=JC07jEzfIYxBIoQn_FHXvyHuxESdhW
|
|
|
43
43
|
fastapi_voyager/web/icon/favicon-32x32.png,sha256=C7v1h58cfWOsiLp9yOIZtlx-dLasBcq3NqpHVGRmpt4,1859
|
|
44
44
|
fastapi_voyager/web/icon/favicon.ico,sha256=tZolYIXkkBcFiYl1A8ksaXN2VjGamzcSdes838dLvNc,15406
|
|
45
45
|
fastapi_voyager/web/icon/site.webmanifest,sha256=GRozZ5suTykYcPMap1QhjrAB8PLW0mbT_phhzw_utvQ,316
|
|
46
|
-
fastapi_voyager-0.15.
|
|
47
|
-
fastapi_voyager-0.15.
|
|
48
|
-
fastapi_voyager-0.15.
|
|
49
|
-
fastapi_voyager-0.15.
|
|
50
|
-
fastapi_voyager-0.15.
|
|
46
|
+
fastapi_voyager-0.15.5.dist-info/METADATA,sha256=nvrvye9JChzW2WQLNq2S3wzPrDU67ZEFoOMYkNcXtuA,9870
|
|
47
|
+
fastapi_voyager-0.15.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
48
|
+
fastapi_voyager-0.15.5.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
|
|
49
|
+
fastapi_voyager-0.15.5.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
|
|
50
|
+
fastapi_voyager-0.15.5.dist-info/RECORD,,
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: fastapi-voyager
|
|
3
|
-
Version: 0.15.3
|
|
4
|
-
Summary: Visualize FastAPI application's routing tree and dependencies
|
|
5
|
-
Project-URL: Homepage, https://github.com/allmonday/fastapi-voyager
|
|
6
|
-
Project-URL: Source, https://github.com/allmonday/fastapi-voyager
|
|
7
|
-
Author-email: Tangkikodo <allmonday@126.com>
|
|
8
|
-
License: MIT
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Keywords: fastapi,openapi,routing,visualization
|
|
11
|
-
Classifier: Framework :: FastAPI
|
|
12
|
-
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
-
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
-
Requires-Python: >=3.10
|
|
21
|
-
Requires-Dist: fastapi>=0.110
|
|
22
|
-
Requires-Dist: jinja2>=3.0.0
|
|
23
|
-
Requires-Dist: pydantic-resolve>=2.4.3
|
|
24
|
-
Provides-Extra: dev
|
|
25
|
-
Requires-Dist: pytest; extra == 'dev'
|
|
26
|
-
Requires-Dist: ruff; extra == 'dev'
|
|
27
|
-
Requires-Dist: uvicorn; extra == 'dev'
|
|
28
|
-
Description-Content-Type: text/markdown
|
|
29
|
-
|
|
30
|
-
[](https://pypi.python.org/pypi/fastapi-voyager)
|
|
31
|
-

|
|
32
|
-
[](https://pepy.tech/projects/fastapi-voyager)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Visualize your FastAPI endpoints, and explore them interactively.
|
|
36
|
-
|
|
37
|
-
Its vision is to make code easier to read and understand, serving as an ideal documentation tool.
|
|
38
|
-
|
|
39
|
-
> This repo is still in early stage, it supports pydantic v2 only
|
|
40
|
-
|
|
41
|
-
visit [live demo](https://www.newsyeah.fun/voyager/)
|
|
42
|
-
source code:[composition oriented development pattern](https://github.com/allmonday/composition-oriented-development-pattern)
|
|
43
|
-
|
|
44
|
-
<img width="1597" height="933" alt="image" src="https://github.com/user-attachments/assets/020bf5b2-6c69-44bf-ba1f-39389d388d27" />
|
|
45
|
-
|
|
46
|
-
with simple configuration it can be embedded into FastAPI.
|
|
47
|
-
|
|
48
|
-
```python
|
|
49
|
-
app.mount('/voyager',
|
|
50
|
-
create_voyager(
|
|
51
|
-
app,
|
|
52
|
-
module_color={'src.services': 'tomato'},
|
|
53
|
-
module_prefix='src.services',
|
|
54
|
-
swagger_url="/docs",
|
|
55
|
-
ga_id="G-XXXXXXXXVL",
|
|
56
|
-
initial_page_policy='first',
|
|
57
|
-
online_repo_url='https://github.com/allmonday/composition-oriented-development-pattern/blob/master',
|
|
58
|
-
enable_pydantic_resolve_meta=True))
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
https://github.com/allmonday/composition-oriented-development-pattern/blob/master/src/main.py#L48
|
|
62
|
-
|
|
63
|
-
## Plan & Raodmap
|
|
64
|
-
- [ideas](./docs/idea.md)
|
|
65
|
-
- [changelog & roadmap](./docs/changelog.md)
|
|
66
|
-
|
|
67
|
-
## Installation
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
pip install fastapi-voyager
|
|
71
|
-
# or
|
|
72
|
-
uv add fastapi-voyager
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
run with cli:
|
|
76
|
-
|
|
77
|
-
```shell
|
|
78
|
-
voyager -m path.to.your.app.module --server
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
> [Sub-Application mounts](https://fastapi.tiangolo.com/advanced/sub-applications/) are not supported yet, but you can specify the name of the FastAPI application used with `--app`. Only a single application (default: 'app') can be selected, but in a scenario where `api` is attached through `app.mount("/api", api)`, you can select `api` like this:
|
|
82
|
-
|
|
83
|
-
```shell
|
|
84
|
-
voyager -m path.to.your.app.module --server --app api
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
## Features
|
|
89
|
-
|
|
90
|
-
For scenarios of using FastAPI as internal API integration endpoints, `fastapi-voyager` helps to visualize the dependencies.
|
|
91
|
-
|
|
92
|
-
It is also an architecture tool that can identify issues inside implementation, finding out wrong relationships, overfetchs, or anything else.
|
|
93
|
-
|
|
94
|
-
**If the process of building the view model follows the ER model**, the full potential of fastapi-voyager can be realized. It allows for quick identification of APIs that use entities, as well as which entities are used by a specific API
|
|
95
|
-
|
|
96
|
-
Given ErDiagram defined by pydantic-resolve, application level entity relationship diagram can be visualized too.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
### highlight nodes and links
|
|
100
|
-
click a node to highlight it's upperstream and downstream nodes. figure out the related models of one page, or homw many pages are related with one model.
|
|
101
|
-
|
|
102
|
-
<img width="1100" height="700" alt="image" src="https://github.com/user-attachments/assets/3e0369ea-5fa4-469a-82c1-ed57d407e53d" />
|
|
103
|
-
|
|
104
|
-
### view source code
|
|
105
|
-
|
|
106
|
-
double click a node or route to show source code or open file in vscode.
|
|
107
|
-
|
|
108
|
-
<img width="1297" height="940" alt="image" src="https://github.com/user-attachments/assets/c8bb2e7d-b727-42a6-8c9e-64dce297d2d8" />
|
|
109
|
-
|
|
110
|
-
### quick search
|
|
111
|
-
|
|
112
|
-
seach schemas by name and dispaly it's upstream and downstreams.
|
|
113
|
-
|
|
114
|
-
shift + click can quickly search current one
|
|
115
|
-
|
|
116
|
-
<img width="1587" height="873" alt="image" src="https://github.com/user-attachments/assets/ee4716f3-233d-418f-bc0e-3b214d1498f7" />
|
|
117
|
-
|
|
118
|
-
### display ER diagram
|
|
119
|
-
|
|
120
|
-
ER diagram is a new feature from pydantic-resolve which provide a solid expression for business descritpions.
|
|
121
|
-
|
|
122
|
-
```python
|
|
123
|
-
diagram = ErDiagram(
|
|
124
|
-
configs=[
|
|
125
|
-
Entity(
|
|
126
|
-
kls=Team,
|
|
127
|
-
relationships=[
|
|
128
|
-
Relationship( field='id', target_kls=list[Sprint], loader=sprint_loader.team_to_sprint_loader),
|
|
129
|
-
Relationship( field='id', target_kls=list[User], loader=user_loader.team_to_user_loader)
|
|
130
|
-
]
|
|
131
|
-
),
|
|
132
|
-
Entity(
|
|
133
|
-
kls=Sprint,
|
|
134
|
-
relationships=[
|
|
135
|
-
Relationship( field='id', target_kls=list[Story], loader=story_loader.sprint_to_story_loader)
|
|
136
|
-
]
|
|
137
|
-
),
|
|
138
|
-
Entity(
|
|
139
|
-
kls=Story,
|
|
140
|
-
relationships=[
|
|
141
|
-
Relationship( field='id', target_kls=list[Task], loader=task_loader.story_to_task_loader),
|
|
142
|
-
Relationship( field='owner_id', target_kls=User, loader=user_loader.user_batch_loader)
|
|
143
|
-
]
|
|
144
|
-
),
|
|
145
|
-
Entity(
|
|
146
|
-
kls=Task,
|
|
147
|
-
relationships=[
|
|
148
|
-
Relationship( field='owner_id', target_kls=User, loader=user_loader.user_batch_loader)
|
|
149
|
-
]
|
|
150
|
-
)
|
|
151
|
-
]
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
# display in voyager
|
|
155
|
-
app.mount('/voyager',
|
|
156
|
-
create_voyager(
|
|
157
|
-
app,
|
|
158
|
-
er_diagram=diagram)
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
<img width="1276" height="613" alt="image" src="https://github.com/user-attachments/assets/ea0091bb-ee11-4f71-8be3-7129d956c910" />
|
|
162
|
-
|
|
163
|
-
### Show pydantic resolve meta info
|
|
164
|
-
|
|
165
|
-
setting `enable_pydantic_resolve_meta=True` in `create_voyager`, toggle `pydantic resolve meta`.
|
|
166
|
-
|
|
167
|
-
<img width="1604" height="535" alt="image" src="https://github.com/user-attachments/assets/d1639555-af41-4a08-9970-4b8ef314596a" />
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
## Command Line Usage
|
|
171
|
-
|
|
172
|
-
### open in browser
|
|
173
|
-
|
|
174
|
-
```bash
|
|
175
|
-
# open in browser
|
|
176
|
-
voyager -m tests.demo --server
|
|
177
|
-
|
|
178
|
-
voyager -m tests.demo --server --port=8002
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### generate the dot file
|
|
182
|
-
```bash
|
|
183
|
-
# generate .dot file
|
|
184
|
-
voyager -m tests.demo
|
|
185
|
-
|
|
186
|
-
voyager -m tests.demo --app my_app
|
|
187
|
-
|
|
188
|
-
voyager -m tests.demo --schema Task
|
|
189
|
-
|
|
190
|
-
voyager -m tests.demo --show_fields all
|
|
191
|
-
|
|
192
|
-
voyager -m tests.demo --module_color=tests.demo:red --module_color=tests.service:tomato
|
|
193
|
-
|
|
194
|
-
voyager -m tests.demo -o my_visualization.dot
|
|
195
|
-
|
|
196
|
-
voyager --version
|
|
197
|
-
|
|
198
|
-
voyager --help
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
## About pydantic-resolve
|
|
202
|
-
|
|
203
|
-
pydantic-resolve is a lightweight tool designed to build complex, nested data in a simple, declarative way. In v2 it introduced an important feature: ER Diagram, and fastapi-voyager has supported this feature, allowing for a clearer understanding of the business relationships.
|
|
204
|
-
|
|
205
|
-
pydantic-resolve's ~~`@ensure_subset` decorator~~ `DefineSubset` metaclass helps safely pick fields from the 'source class' while **indicating the reference** from the current class to the base class.
|
|
206
|
-
|
|
207
|
-
Developers can use fastapi-voyager without needing to know anything about pydantic-resolve, but I still highly recommend everyone to give it a try.
|
|
208
|
-
|
|
209
|
-
## Dependencies
|
|
210
|
-
|
|
211
|
-
- FastAPI
|
|
212
|
-
- [pydantic-resolve](https://github.com/allmonday/pydantic-resolve)
|
|
213
|
-
- Quasar
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
## Credits
|
|
217
|
-
|
|
218
|
-
- https://apis.guru/graphql-voyager/, thanks for inspiration.
|
|
219
|
-
- https://github.com/tintinweb/vscode-interactive-graphviz, thanks for web visualization.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
## How to develop & contribute?
|
|
223
|
-
|
|
224
|
-
fork, clone.
|
|
225
|
-
|
|
226
|
-
install uv.
|
|
227
|
-
|
|
228
|
-
```shell
|
|
229
|
-
uv venv
|
|
230
|
-
source .venv/bin/activate
|
|
231
|
-
uv pip install ".[dev]"
|
|
232
|
-
uvicorn tests.programatic:app --reload
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
### Setup Git Hooks (Optional)
|
|
236
|
-
|
|
237
|
-
Enable automatic code formatting before commits:
|
|
238
|
-
|
|
239
|
-
```shell
|
|
240
|
-
./setup-hooks.sh
|
|
241
|
-
# or manually:
|
|
242
|
-
git config core.hooksPath .githooks
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
This will run Prettier automatically before each commit. See [`.githooks/README.md`](./.githooks/README.md) for details.
|
|
246
|
-
|
|
247
|
-
open `localhost:8000/voyager`
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
frontend:
|
|
251
|
-
- `src/fastapi_voyager/web/vue-main.js`: main js
|
|
252
|
-
|
|
253
|
-
backend:
|
|
254
|
-
- `voyager.py`: main entry
|
|
255
|
-
- `render.py`: generate dot file
|
|
256
|
-
- `server.py`: serve mode
|
|
File without changes
|
|
File without changes
|
|
File without changes
|