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 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
- return content.replace(GA_PLACEHOLDER, _build_ga_snippet(ga_id))
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>
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.15.3"
2
+ __version__ = "0.15.5"
@@ -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
- const ele = polygons[2] // select the second polygon
74
- if (ele) {
75
- ele.setAttribute("stroke-width", "3.5")
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
 
@@ -2,8 +2,14 @@
2
2
  <head>
3
3
  <title>FastAPI Voyager</title>
4
4
  <meta name="theme-color" content="#ffffff" />
5
- <link rel="stylesheet" href="fastapi-voyager-static/graphviz.svg.css" />
6
- <link rel="stylesheet" href="fastapi-voyager-static/quasar.min.css" />
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 anchor="bottom middle" self="top middle" :offset="[0,8]">
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: 12px"
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 type="module" src="fastapi-voyager-static/vue-main.js"></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>
@@ -120,22 +120,23 @@ const getters = {
120
120
 
121
121
  const actions = {
122
122
  /**
123
- * Read tag and route from URL query parameters
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 route selection to URL
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
- renderBasedOnInitialPolicy()
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
@@ -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
+ [![pypi](https://img.shields.io/pypi/v/fastapi-voyager.svg)](https://pypi.python.org/pypi/fastapi-voyager)
31
+ ![Python Versions](https://img.shields.io/pypi/pyversions/fastapi-voyager)
32
+ [![PyPI Downloads](https://static.pepy.tech/badge/fastapi-voyager/month)](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=MUc_ia3_QIbYQ8VenOxv3CEYVTiPDs7e_H40YoalxZs,8970
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=mV_h7Kelwuchi4ZvsHaY1gjoGeeu7EK0TqrMYIGLwgI,49
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=cmEcM35rQsR6eQA2Sc8zJ_MOA0UfEQ9WvGA1zNInUUc,6285
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=lA59Op4u1bb-B105Iadn2KN_n11-AtXFdJglUezZIDg,23225
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=zjmtx1HGN_umfyOQVwQgET-2V5hvXbS50YgXCr294Ok,14370
34
- fastapi_voyager/web/vue-main.js,sha256=1s11NOjILgjncJ4WbN1DGzTeLUb0a2-LYIA3lUIX8Bc,10564
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.3.dist-info/METADATA,sha256=_NFcPd07mWHADnTH01kf6c2y0Lw9AQgqQNnHMK0Y5xE,8513
47
- fastapi_voyager-0.15.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
48
- fastapi_voyager-0.15.3.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
49
- fastapi_voyager-0.15.3.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
50
- fastapi_voyager-0.15.3.dist-info/RECORD,,
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
- [![pypi](https://img.shields.io/pypi/v/fastapi-voyager.svg)](https://pypi.python.org/pypi/fastapi-voyager)
31
- ![Python Versions](https://img.shields.io/pypi/pyversions/fastapi-voyager)
32
- [![PyPI Downloads](https://static.pepy.tech/badge/fastapi-voyager/month)](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