fastapi-voyager 0.11.11__py3-none-any.whl → 0.12.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
fastapi_voyager/server.py CHANGED
@@ -6,7 +6,7 @@ from pydantic import BaseModel
6
6
  from fastapi.responses import HTMLResponse, PlainTextResponse, JSONResponse
7
7
  from fastapi.staticfiles import StaticFiles
8
8
  from fastapi_voyager.voyager import Voyager
9
- from fastapi_voyager.type import Tag, FieldInfo, CoreData, SchemaNode
9
+ from fastapi_voyager.type import Tag, CoreData, SchemaNode
10
10
  from fastapi_voyager.render import Renderer
11
11
  from fastapi_voyager.type_helper import get_source, get_vscode_link
12
12
  from fastapi_voyager.version import __version__
@@ -15,6 +15,22 @@ from fastapi_voyager.version import __version__
15
15
  WEB_DIR = Path(__file__).parent / "web"
16
16
  WEB_DIR.mkdir(exist_ok=True)
17
17
 
18
+ GA_PLACEHOLDER = "<!-- GA_SNIPPET -->"
19
+
20
+ def _build_ga_snippet(ga_id: Optional[str]) -> str:
21
+ if not ga_id:
22
+ return ""
23
+
24
+ return f""" <script async src="https://www.googletagmanager.com/gtag/js?id={ga_id}"></script>
25
+ <script>
26
+ window.dataLayer = window.dataLayer || [];
27
+ function gtag(){{dataLayer.push(arguments);}}
28
+ gtag('js', new Date());
29
+
30
+ gtag('config', '{ga_id}');
31
+ </script>
32
+ """
33
+
18
34
  INITIAL_PAGE_POLICY = Literal['first', 'full', 'empty']
19
35
 
20
36
  class OptionParam(BaseModel):
@@ -46,6 +62,7 @@ def create_voyager(
46
62
  swagger_url: Optional[str] = None,
47
63
  online_repo_url: Optional[str] = None,
48
64
  initial_page_policy: INITIAL_PAGE_POLICY = 'first',
65
+ ga_id: Optional[str] = None,
49
66
  ) -> FastAPI:
50
67
  router = APIRouter(tags=['fastapi-voyager'])
51
68
 
@@ -57,6 +74,9 @@ def create_voyager(
57
74
 
58
75
  # include tags and their routes
59
76
  tags = voyager.tags
77
+ for t in tags:
78
+ t.routes.sort(key=lambda r: r.name)
79
+ tags.sort(key=lambda t: t.name)
60
80
 
61
81
  schemas = voyager.nodes[:]
62
82
  schemas.sort(key=lambda s: s.name)
@@ -113,7 +133,8 @@ def create_voyager(
113
133
  def index():
114
134
  index_file = WEB_DIR / "index.html"
115
135
  if index_file.exists():
116
- return index_file.read_text(encoding="utf-8")
136
+ content = index_file.read_text(encoding="utf-8")
137
+ return content.replace(GA_PLACEHOLDER, _build_ga_snippet(ga_id))
117
138
  # fallback simple page if index.html missing
118
139
  return """
119
140
  <!doctype html>
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.11.11"
2
+ __version__ = "0.12.2"
@@ -157,6 +157,7 @@ export default defineComponent({
157
157
  <q-linear-progress indeterminate color="primary" size="2px"/>
158
158
  </div>
159
159
  <div class="q-ml-lg q-mt-md">
160
+ <p style="font-size: 16px;"> {{ schemaName }} </p>
160
161
  <a :href="link" target="_blank" rel="noopener" style="font-size:12px; color:#3b82f6;">
161
162
  Open in VSCode
162
163
  </a>
@@ -58,9 +58,10 @@ export class GraphUI {
58
58
  }
59
59
 
60
60
  highlightSchemaBanner(node) {
61
- const ele = node.querySelector("polygon[fill='#009485']")
61
+ const polygons = node.querySelectorAll("polygon");
62
+ const ele = polygons[2]; // select the second polygon
62
63
  if (ele) {
63
- ele.setAttribute('fill', 'tomato');
64
+ ele.setAttribute('stroke-width', '3.5');
64
65
  }
65
66
  }
66
67
 
@@ -160,12 +161,6 @@ export class GraphUI {
160
161
  }
161
162
  }
162
163
  });
163
-
164
- $(document).on("keydown.graphui", function (evt) {
165
- if (evt.keyCode === 27 && self.gv) {
166
- self.gv.highlight();
167
- }
168
- });
169
164
  },
170
165
  });
171
166
  }
@@ -59,6 +59,9 @@
59
59
  .adjust-fit {
60
60
  height: calc(100vh - 54px);
61
61
  }
62
+
63
+ .github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}
64
+
62
65
  /* App boot loading overlay & gating */
63
66
  #app-loading-overlay {
64
67
  position: fixed;
@@ -167,9 +170,8 @@
167
170
  </div>
168
171
  </q-tooltip>
169
172
  </q-btn>
170
- <a href="https://github.com/allmonday/fastapi-voyager" target="_blank" class="github-corner" aria-label="View source on GitHub"><svg width="52" height="52" viewBox="0 0 250 250" style="fill:#009485; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"/><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"/><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"/></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style></div>
171
- <!-- <q-toolbar-title class="text-primary row">
172
- </q-toolbar-title> -->
173
+ <a href="https://github.com/allmonday/fastapi-voyager" target="_blank" class="github-corner" aria-label="View source on GitHub"><svg width="52" height="52" viewBox="0 0 250 250" style="fill:#009485; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"/><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"/><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"/></svg></a>
174
+ </div>
173
175
  </q-toolbar>
174
176
  </q-header>
175
177
 
@@ -475,5 +477,7 @@
475
477
  });
476
478
  </script>
477
479
  <script type="module" src="fastapi-voyager-static/vue-main.js"></script>
480
+
481
+ <!-- GA_SNIPPET -->
478
482
  </body>
479
483
  </html>
@@ -59,6 +59,66 @@ const app = createApp({
59
59
  showDetail.value = false;
60
60
  }
61
61
 
62
+ function readQuerySelection() {
63
+ if (typeof window === "undefined") {
64
+ return { tag: null, route: null };
65
+ }
66
+ const params = new URLSearchParams(window.location.search);
67
+ return {
68
+ tag: params.get("tag") || null,
69
+ route: params.get("route") || null,
70
+ };
71
+ }
72
+
73
+ function findTagByRoute(routeId) {
74
+ return (
75
+ state.rawTags.find((tag) =>
76
+ (tag.routes || []).some((route) => route.id === routeId)
77
+ )?.name || null
78
+ );
79
+ }
80
+
81
+ function syncSelectionToUrl() {
82
+ if (typeof window === "undefined") {
83
+ return;
84
+ }
85
+ const params = new URLSearchParams(window.location.search);
86
+ if (state.tag) {
87
+ params.set("tag", state.tag);
88
+ } else {
89
+ params.delete("tag");
90
+ }
91
+ if (state.routeId) {
92
+ params.set("route", state.routeId);
93
+ } else {
94
+ params.delete("route");
95
+ }
96
+ const hash = window.location.hash || "";
97
+ const search = params.toString();
98
+ const base = window.location.pathname;
99
+ const newUrl = search ? `${base}?${search}${hash}` : `${base}${hash}`;
100
+ window.history.replaceState({}, "", newUrl);
101
+ }
102
+
103
+ function applySelectionFromQuery(selection) {
104
+ let applied = false;
105
+ if (selection.tag && state.rawTags.some((tag) => tag.name === selection.tag)) {
106
+ state.tag = selection.tag;
107
+ state._tag = selection.tag;
108
+ applied = true;
109
+ }
110
+ if (selection.route && state.routeItems?.[selection.route]) {
111
+ state.routeId = selection.route;
112
+ applied = true;
113
+ const inferredTag = findTagByRoute(selection.route);
114
+ if (inferredTag) {
115
+ state.tag = inferredTag;
116
+ state._tag = inferredTag;
117
+ }
118
+ }
119
+ return applied;
120
+ }
121
+
62
122
  async function loadInitial() {
63
123
  state.initializing = true;
64
124
  try {
@@ -82,6 +142,14 @@ const app = createApp({
82
142
  state.version = data.version || "";
83
143
  state.swaggerUrl = data.swagger_url || null
84
144
 
145
+ const querySelection = readQuerySelection();
146
+ const restoredFromQuery = applySelectionFromQuery(querySelection);
147
+ if (restoredFromQuery) {
148
+ syncSelectionToUrl();
149
+ onGenerate();
150
+ return;
151
+ }
152
+
85
153
  switch (data.initial_page_policy) {
86
154
  case "full":
87
155
  onGenerate()
@@ -246,6 +314,7 @@ const app = createApp({
246
314
  state.focus = false;
247
315
  schemaCodeName.value = "";
248
316
  onGenerate();
317
+ syncSelectionToUrl();
249
318
  }
250
319
 
251
320
  function toggleTag(tagName, expanded = null) {
@@ -262,6 +331,7 @@ const app = createApp({
262
331
 
263
332
  state.detailDrawer = false;
264
333
  showRouteDetail.value = false;
334
+ syncSelectionToUrl();
265
335
  }
266
336
 
267
337
  function selectRoute(routeId) {
@@ -275,6 +345,7 @@ const app = createApp({
275
345
  state.focus = false;
276
346
  schemaCodeName.value = "";
277
347
  onGenerate();
348
+ syncSelectionToUrl();
278
349
  }
279
350
 
280
351
  function toggleShowModule(val) {
@@ -0,0 +1,182 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastapi-voyager
3
+ Version: 0.12.2
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.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: fastapi>=0.110
23
+ Requires-Dist: pydantic-resolve>=1.13.2
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
+ > This repo is still in early stage, it supports pydantic v2 only
36
+
37
+ Visualize your FastAPI endpoints, and explore them interactively.
38
+
39
+ [visit online demo](https://www.newsyeah.fun/voyager/) of project: [composition oriented development pattern](https://github.com/allmonday/composition-oriented-development-pattern)
40
+
41
+ <img width="1600" height="986" alt="image" src="https://github.com/user-attachments/assets/8829cda0-f42d-4c84-be2f-b019bb5fe7e1" />
42
+
43
+ ## Plan & Raodmap
44
+ - [ideas](./docs/idea.md)
45
+ - [changelog & roadmap](./docs/changelog.md)
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ pip install fastapi-voyager
51
+ # or
52
+ uv add fastapi-voyager
53
+ ```
54
+
55
+ ```shell
56
+ voyager -m path.to.your.app.module --server
57
+ ```
58
+
59
+ > [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:
60
+
61
+ ```shell
62
+ voyager -m path.to.your.app.module --server --app api
63
+ ```
64
+
65
+ ## Mount into project
66
+
67
+ ```python
68
+ from fastapi import FastAPI
69
+ from fastapi_voyager import create_voyager
70
+ from tests.demo import app
71
+
72
+ app.mount('/voyager', create_voyager(
73
+ app,
74
+ module_color={"tests.service": "red"},
75
+ module_prefix="tests.service"),
76
+ swagger_url="/docs")
77
+ ```
78
+
79
+ ## Features
80
+
81
+ For scenarios of using FastAPI as internal API integration endpoints, `fastapi-voyager` helps to visualize the dependencies.
82
+
83
+ It is also an architecture inspection tool that can identify issues in data relationships during design phase before turly implemtatioin.
84
+
85
+ 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
86
+
87
+ ### highlight nodes and links
88
+ 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.
89
+
90
+
91
+ <img width="1100" height="700" alt="image" src="https://github.com/user-attachments/assets/3e0369ea-5fa4-469a-82c1-ed57d407e53d" />
92
+
93
+ ### focus on nodes
94
+
95
+ Double click a node, and then toggle focus to hide irrelevant nodes.
96
+
97
+ <img width="1061" height="937" alt="image" src="https://github.com/user-attachments/assets/79709b02-7571-43fc-abc9-17a287a97515" />
98
+
99
+ ### view source code
100
+
101
+ double click a node or route to show source code or open file in vscode.
102
+
103
+ <img width="1297" height="940" alt="image" src="https://github.com/user-attachments/assets/c8bb2e7d-b727-42a6-8c9e-64dce297d2d8" />
104
+
105
+ <img width="1132" height="824" alt="image" src="https://github.com/user-attachments/assets/b706e879-e4fc-48dd-ace1-99bf97e3ed6a" />
106
+
107
+
108
+ ## Command Line Usage
109
+
110
+ ### open in browser
111
+
112
+ ```bash
113
+ # open in browser
114
+ voyager -m tests.demo --server
115
+
116
+ voyager -m tests.demo --server --port=8002
117
+ ```
118
+
119
+ ### generate the dot file
120
+ ```bash
121
+ # generate .dot file
122
+ voyager -m tests.demo
123
+
124
+ voyager -m tests.demo --app my_app
125
+
126
+ voyager -m tests.demo --schema Task
127
+
128
+ voyager -m tests.demo --show_fields all
129
+
130
+ voyager -m tests.demo --module_color=tests.demo:red --module_color=tests.service:tomato
131
+
132
+ voyager -m tests.demo -o my_visualization.dot
133
+
134
+ voyager --version
135
+
136
+ voyager --help
137
+ ```
138
+
139
+ ## About pydantic-resolve
140
+
141
+ pydantic-resolve's `@ensure_subset` decorator helps safely pick fields from the 'source class' while **indicating the reference** from the current class to the base class.
142
+
143
+ pydantic-resolve is a lightweight tool designed to build complex, nested data in a simple, declarative way. In version 2.0.0alpha, it will introduce an important feature: ER Diagram, and fastapi-voyager will support this feature, allowing for a clearer understanding of the business relationships between the data.
144
+
145
+ Developers can use fastapi-voyager without needing to know anything about pydantic-resolve, but I still highly recommend everyone to give it a try.
146
+
147
+ ## Dependencies
148
+
149
+ - FastAPI
150
+ - [pydantic-resolve](https://github.com/allmonday/pydantic-resolve)
151
+ - Quasar
152
+
153
+
154
+ ## Credits
155
+
156
+ - https://apis.guru/graphql-voyager/, thanks for inspiration.
157
+ - https://github.com/tintinweb/vscode-interactive-graphviz, thanks for web visualization.
158
+
159
+
160
+ ## How to develop & contribute?
161
+
162
+ fork, clone.
163
+
164
+ install uv.
165
+
166
+ ```shell
167
+ uv venv
168
+ source .venv/bin/activate
169
+ uv pip install ".[dev]"
170
+ uvicorn tests.programatic:app --reload
171
+ ```
172
+
173
+ open `localhost:8000/voyager`
174
+
175
+
176
+ frontend:
177
+ - `src/web/vue-main.js`: main js
178
+
179
+ backend:
180
+ - `voyager.py`: main entry
181
+ - `render.py`: generate dot file
182
+ - `server.py`: serve mode
@@ -3,21 +3,21 @@ fastapi_voyager/cli.py,sha256=xK8DT-m2qP38FK2dGhLP-sHEuS29SBw6ACrnX9w85P0,10521
3
3
  fastapi_voyager/filter.py,sha256=9Y-NepveIiCLOI-5eXk6DNK9H3dr5_h4xUbWYHkbo7M,11552
4
4
  fastapi_voyager/module.py,sha256=Z2QHNmiLk6ZAJlm2nSmO875Q33TweSg8UxZSzIpU9zY,3499
5
5
  fastapi_voyager/render.py,sha256=8hVsEDQi2-aP3QKN6KI3RnNz0uG-FuDr_k4D7QNsdQQ,9823
6
- fastapi_voyager/server.py,sha256=G48St-leUcEwshIhTYVotxuFWXDAvzjthOCK9AKh9dI,6497
6
+ fastapi_voyager/server.py,sha256=fVKruGccBJ039z3EbYRjqUYHbbw5HOlo5jzK_n76ids,7092
7
7
  fastapi_voyager/type.py,sha256=VmcTB1G-LOT70EWCzi4LU_FUkSGWUIBJX15T_J5HnOo,1764
8
8
  fastapi_voyager/type_helper.py,sha256=TqtYP2_54aar_iQjD0XhjJPXYhfi6icnPPrxkj0a4sk,9523
9
- fastapi_voyager/version.py,sha256=PokgCRzjnyxlFdVF1p-f93rdL31U6_AVnSKLvGGed9Y,50
9
+ fastapi_voyager/version.py,sha256=jnAp1zbYtBp4Jx35wH9VM8Hi_uyCLhudDOycCg10IfY,49
10
10
  fastapi_voyager/voyager.py,sha256=nioo56oFDeZ8nwwPWDtaQbkpe4pVssFoBVHCWFhs0K4,13549
11
- fastapi_voyager/web/graph-ui.js,sha256=DTedkpZNbtufexONVkJ8mOwF_-VnvxoReYHtox6IKR4,5842
11
+ fastapi_voyager/web/graph-ui.js,sha256=9ONPxQHvk4HxYq6KtKc_2VbJmUgd-gh7i3Biv1rkqC4,5734
12
12
  fastapi_voyager/web/graphviz.svg.css,sha256=zDCjjpT0Idufu5YOiZI76PL70-avP3vTyzGPh9M85Do,1563
13
13
  fastapi_voyager/web/graphviz.svg.js,sha256=lvAdbjHc-lMSk4GQp-iqYA2PCFX4RKnW7dFaoe0LUHs,16005
14
- fastapi_voyager/web/index.html,sha256=FJtx1_SiNq7tTMRXMKIx7zMInhEMCusw6VFTYBPtpak,19444
14
+ fastapi_voyager/web/index.html,sha256=8cmlwQzE5tonHj_QozdKcr3z-7JFsvE7cjf70dye0y8,19377
15
15
  fastapi_voyager/web/quasar.min.css,sha256=F5jQe7X2XT54VlvAaa2V3GsBFdVD-vxDZeaPLf6U9CU,203145
16
16
  fastapi_voyager/web/quasar.min.js,sha256=h0ftyPMW_CRiyzeVfQqiup0vrVt4_QWojpqmpnpn07E,502974
17
- fastapi_voyager/web/vue-main.js,sha256=D1H6UhwAlQta73shjZLmQAKbss7X2Y471fjC1H58p9g,11156
17
+ fastapi_voyager/web/vue-main.js,sha256=m6U24ythzjQuAYXUm9BsTdFrApFNqW26B0Bf7TsybqQ,13275
18
18
  fastapi_voyager/web/component/render-graph.js,sha256=e8Xgh2Kl-nYU0P1gstEmAepCgFnk2J6UdxW8TlMafGs,2322
19
19
  fastapi_voyager/web/component/route-code-display.js,sha256=8NJPPjNRUC21gjpY8XYEQs4RBbhX1pCiqEhJp39ku6k,3678
20
- fastapi_voyager/web/component/schema-code-display.js,sha256=FjoD3CLl967VMfbZNQMdbiEnS0z-doLJDJIeDCmKGew,6983
20
+ fastapi_voyager/web/component/schema-code-display.js,sha256=qKUMV2RFQzR8deof2iC4vyp65UaWadtVsDAXjY-i3vE,7042
21
21
  fastapi_voyager/web/component/schema-field-filter.js,sha256=c--XiXJrhIS7sYo1x8ZwMoqak0k9xLkNYTWoli-zd38,6253
22
22
  fastapi_voyager/web/icon/android-chrome-192x192.png,sha256=35sBy6jmUFJCcquStaafHH1qClZIbd-X3PIKSeLkrNo,37285
23
23
  fastapi_voyager/web/icon/android-chrome-512x512.png,sha256=eb2eDjCwIruc05029_0L9hcrkVkv8KceLn1DJMYU0zY,210789
@@ -26,8 +26,8 @@ fastapi_voyager/web/icon/favicon-16x16.png,sha256=JC07jEzfIYxBIoQn_FHXvyHuxESdhW
26
26
  fastapi_voyager/web/icon/favicon-32x32.png,sha256=C7v1h58cfWOsiLp9yOIZtlx-dLasBcq3NqpHVGRmpt4,1859
27
27
  fastapi_voyager/web/icon/favicon.ico,sha256=tZolYIXkkBcFiYl1A8ksaXN2VjGamzcSdes838dLvNc,15406
28
28
  fastapi_voyager/web/icon/site.webmanifest,sha256=ep4Hzh9zhmiZF2At3Fp1dQrYQuYF_3ZPZxc1KcGBvwQ,263
29
- fastapi_voyager-0.11.11.dist-info/METADATA,sha256=uufha8zMMchzxEm6ZWW5r4VUF26sKl2DQ9G14HefXgw,11451
30
- fastapi_voyager-0.11.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
31
- fastapi_voyager-0.11.11.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
32
- fastapi_voyager-0.11.11.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
33
- fastapi_voyager-0.11.11.dist-info/RECORD,,
29
+ fastapi_voyager-0.12.2.dist-info/METADATA,sha256=hXiqtPfKvni9gv4HxyjGdQTQFaDIAGJ69qE4WzsSzus,6059
30
+ fastapi_voyager-0.12.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
31
+ fastapi_voyager-0.12.2.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
32
+ fastapi_voyager-0.12.2.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
33
+ fastapi_voyager-0.12.2.dist-info/RECORD,,
@@ -1,350 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: fastapi-voyager
3
- Version: 0.11.11
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.9
16
- Classifier: Programming Language :: Python :: 3.10
17
- Classifier: Programming Language :: Python :: 3.11
18
- Classifier: Programming Language :: Python :: 3.12
19
- Classifier: Programming Language :: Python :: 3.13
20
- Classifier: Programming Language :: Python :: 3.14
21
- Requires-Python: >=3.10
22
- Requires-Dist: fastapi>=0.110
23
- Requires-Dist: pydantic-resolve>=1.13.2
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
- > This repo is still in early stage, it supports pydantic v2 only
36
-
37
- Visualize your FastAPI endpoints, and explore them interactively.
38
-
39
- [visit online demo](https://www.newsyeah.fun/voyager/) of project: [composition oriented development pattern](https://github.com/allmonday/composition-oriented-development-pattern)
40
-
41
- <img width="1600" height="986" alt="image" src="https://github.com/user-attachments/assets/8829cda0-f42d-4c84-be2f-b019bb5fe7e1" />
42
-
43
- ## Installation
44
-
45
- ```bash
46
- pip install fastapi-voyager
47
- # or
48
- uv add fastapi-voyager
49
- ```
50
-
51
- ```shell
52
- voyager -m path.to.your.app.module --server
53
- ```
54
-
55
- > [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:
56
-
57
- ```shell
58
- voyager -m path.to.your.app.module --server --app api
59
- ```
60
-
61
- ## Mount into project
62
-
63
- ```python
64
- from fastapi import FastAPI
65
- from fastapi_voyager import create_voyager
66
- from tests.demo import app
67
-
68
- app.mount('/voyager', create_voyager(
69
- app,
70
- module_color={"tests.service": "red"},
71
- module_prefix="tests.service"),
72
- swagger_url="/docs")
73
- ```
74
-
75
- more about [sub application](https://fastapi.tiangolo.com/advanced/sub-applications/?h=sub)
76
-
77
-
78
- ## Feature
79
-
80
- For scenarios of using FastAPI as internal API integration endpoints, `fastapi-voyager` helps to visualize the dependencies.
81
-
82
- It is also an architecture inspection tool that can identify issues in data relationships through visualization during the design phase.
83
-
84
- 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
85
-
86
-
87
-
88
- ```shell
89
- git clone https://github.com/allmonday/fastapi-voyager.git
90
- cd fastapi-voyager
91
-
92
- voyager -m tests.demo
93
- --server --port=8001
94
- --module_color=tests.service:blue
95
- --module_color=tests.demo:tomato
96
- ```
97
-
98
- ### highlight
99
- 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.
100
-
101
- <img width="1100" height="700" alt="image" src="https://github.com/user-attachments/assets/3e0369ea-5fa4-469a-82c1-ed57d407e53d" />
102
-
103
- ### focus on nodes
104
- toggle focus to hide nodes not related with current picked one.
105
-
106
- before:
107
- <img width="1066" height="941" alt="image" src="https://github.com/user-attachments/assets/39f30817-899a-4289-93f4-a1646d3441c1" />
108
- after:
109
- <img width="1061" height="937" alt="image" src="https://github.com/user-attachments/assets/79709b02-7571-43fc-abc9-17a287a97515" />
110
-
111
- ### view source code
112
- double click a node to show source code or open file in vscode.
113
- <img width="1297" height="940" alt="image" src="https://github.com/user-attachments/assets/c8bb2e7d-b727-42a6-8c9e-64dce297d2d8" />
114
-
115
- double click a route to show source code or open file in vscode
116
- <img width="1132" height="824" alt="image" src="https://github.com/user-attachments/assets/b706e879-e4fc-48dd-ace1-99bf97e3ed6a" />
117
-
118
-
119
-
120
- ## Command Line Usage
121
-
122
- ### open in browser
123
-
124
- ```bash
125
- # open in browser
126
- voyager -m tests.demo --server
127
-
128
- voyager -m tests.demo --server --port=8002
129
- ```
130
-
131
- ### generate the dot file
132
- ```bash
133
- # generate .dot file
134
- voyager -m tests.demo
135
-
136
- voyager -m tests.demo --app my_app
137
-
138
- voyager -m tests.demo --schema Task
139
-
140
- voyager -m tests.demo --show_fields all
141
-
142
- voyager -m tests.demo --module_color=tests.demo:red --module_color=tests.service:tomato
143
-
144
- voyager -m tests.demo -o my_visualization.dot
145
-
146
- voyager --version
147
-
148
- voyager --help
149
- ```
150
-
151
- The tool will generate a DOT file that you can render using Graphviz:
152
-
153
- ```bash
154
- # Install graphviz
155
- brew install graphviz # macOS
156
- apt-get install graphviz # Ubuntu/Debian
157
-
158
- # Render the graph
159
- dot -Tpng router_viz.dot -o router_viz.png
160
-
161
- # Or view online at: https://dreampuf.github.io/GraphvizOnline/
162
- ```
163
-
164
- or you can open router_viz.dot with vscode extension `graphviz interactive preview`
165
-
166
-
167
- ## Plan before v1.0
168
-
169
-
170
- ### backlog
171
- - [ ] user can generate nodes/edges manually and connect to generated ones
172
- - [ ] eg: add owner
173
- - [ ] add extra info for schema
174
- - [ ] optimize static resource (allow manually config url)
175
- - [ ] improve search dialog
176
- - [ ] add route/tag list
177
- - [ ] type alias should not be kept as node instead of compiling to original type
178
- - [ ] how to correctly handle the generic type ?
179
- - [ ] support Google analysis config
180
-
181
- ### in analysis
182
- - [ ] click field to highlight links
183
- - [ ] animation effect for edges
184
- - [ ] display standard ER diagram spec. `hard but important`
185
- - [ ] display potential invalid links
186
- - [ ] highlight relationship belongs to ER diagram
187
-
188
- ### plan:
189
- #### <0.9:
190
- - [x] group schemas by module hierarchy
191
- - [x] module-based coloring via Analytics(module_color={...})
192
- - [x] view in web browser
193
- - [x] config params
194
- - [x] make a explorer dashboard, provide list of routes, schemas, to make it easy to switch and search
195
- - [x] support programmatic usage
196
- - [x] better schema /router node appearance
197
- - [x] hide fields duplicated with parent's (show `parent fields` instead)
198
- - [x] refactor the frontend to vue, and tweak the build process
199
- - [x] find dependency based on picked schema and it's field.
200
- - [x] optimize static resource (cdn -> local)
201
- - [x] add configuration for highlight (optional)
202
- - [x] alt+click to show field details
203
- - [x] display source code of routes (including response_model)
204
- - [x] handle excluded field
205
- - [x] add tooltips
206
- - [x] route
207
- - [x] group routes by module hierarchy
208
- - [x] add response_model in route
209
- - [x] fixed left bar show tag/ route
210
- - [x] export voyager core data into json (for better debugging)
211
- - [x] add api to rebuild core data from json, and render it
212
- - [x] fix Generic case `test_generic.py`
213
- - [x] show tips for routes not return pydantic type.
214
- - [x] fix duplicated link from class and parent class, it also break clicking highlight
215
- - [x] refactor: abstract render module
216
-
217
- #### 0.9
218
- - [x] refactor: server.py
219
- - [x] rename create_app_with_fastapi -> create_voyager
220
- - [x] add doc for parameters
221
- - [x] improve initialization time cost
222
- - [x] query route / schema info through realtime api
223
- - [x] adjust fe
224
- - 0.9.3
225
- - [x] adjust layout
226
- - [x] show field detail in right panel
227
- - [x] show route info in bottom
228
- - 0.9.4
229
- - [x] close schema sidebar when switch tag/route
230
- - [x] schema detail panel show fields by default
231
- - [x] adjust schema panel's height
232
- - [x] show from base information in subset case
233
- - 0.9.5
234
- - [x] route list should have a max height
235
-
236
- #### 0.10
237
- - 0.10.1
238
- - [x] refactor voyager.py tag -> route structure
239
- - [x] fix missing route (tag has only one route which return primitive value)
240
- - [x] make right panel resizable by dragging
241
- - [x] allow closing tag expansion item
242
- - [x] hide brief mode if not configured
243
- - [x] add focus button to only show related nodes under current route/tag graph in dialog
244
- - 0.10.2
245
- - [x] fix graph height
246
- - [x] show version in title
247
- - 0.10.3
248
- - [x] fix focus in brief-mode
249
- - [x] ui: adjust focus position
250
- - [x] refactor naming
251
- - [x] fix layout issue when rendering huge graph
252
- - 0.10.4
253
- - [x] fix: when focus is on, should ensure changes from other params not broken.
254
- - 0.10.5
255
- - [x] double click to show details, and highlight as tomato
256
-
257
-
258
- #### 0.11
259
- - 0.11.1
260
- - [x] support opening route in swagger
261
- - [x] config docs path
262
- - [x] provide option to hide routes in brief mode (auto hide in full graph mode)
263
- - 0.11.2
264
- - [x] enable/disable module cluster (to save space)
265
- - 0.11.3
266
- - [x] support online repo url
267
- - 0.11.4
268
- - [x] add loading for field detail panel
269
- - 0.11.5
270
- - [x] optimize open in swagger link
271
- - [x] change jquery cdn
272
- - 0.11.6
273
- - [x] flag of loading full graph in first render or not
274
- - [x] optimize loading static resource
275
- - 0.11.7
276
- - [x] fix swagger link
277
- - 0.11.8
278
- - [x] fix swagger link in another way
279
- - 0.11.9
280
- - [x] replace issubclass with safe_issubclass to prevent exception.
281
- - 0.11.10
282
- - [x] fix bug during updating forward refs
283
- - 0.11.11
284
- - [x] replace print with logging and add `--log-level` in cli, by default info
285
- - [x] fill node title color with module color
286
- - [x] optimize cluster render logic
287
-
288
- ### 0.12
289
- - 0.12.1
290
- - [ ] sort tag / route names in left panel
291
- - [ ] sort field name in nodes
292
- - [ ] set max limit for fields in nodes
293
- - [ ] upgrade network algorithm (optional)
294
- - [ ] refactor render.py
295
-
296
- #### 0.13
297
- - 0.12.0
298
- - [ ] integration with pydantic-resolve
299
- - [ ] show hint for resolve, post fields
300
- - [ ] display loader as edges
301
- - [ ] add tests
302
-
303
- #### 0.13
304
- placeholder
305
-
306
-
307
- ## About pydantic-resolve
308
-
309
- pydantic-resolve's `@ensure_subset` decorator helps safely pick fields from the 'source class' while indicating the reference from the current class to the base class.
310
-
311
- pydantic-resolve is a lightweight tool designed to build complex, nested data in a simple, declarative way. In version 2, it will introduce an important feature: ER model definition, and fastapi-voyager will support and visualize these diagrams.
312
-
313
- Developers can use fastapi-voyager without needing to know about pydantic-resolve.
314
-
315
-
316
- ## Credits
317
-
318
- - https://apis.guru/graphql-voyager/, thanks for inspiration.
319
- - https://github.com/tintinweb/vscode-interactive-graphviz, thanks for web visualization.
320
-
321
-
322
- ## Dependencies
323
-
324
- - FastAPI
325
- - [pydantic-resolve](https://github.com/allmonday/pydantic-resolve)
326
- - Quasar
327
-
328
-
329
- ## How to develop & contribute?
330
-
331
- fork, clone.
332
-
333
- install uv.
334
-
335
- ```shell
336
- uv venv
337
- source .venv/bin/activate
338
- uv pip install ".[dev]"
339
- uvicorn tests.programatic:app --reload
340
- ```
341
-
342
- open `localhost:8000/voyager`
343
-
344
-
345
- frontend: `src/web/vue-main.js`
346
- backend: `voyager.py`, `render.py`, `server.py`
347
-
348
- ## Branch and Release flow
349
-
350
- TODO