fastapi-voyager 0.5.1__tar.gz → 0.5.2__tar.gz

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.
Files changed (46) hide show
  1. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/PKG-INFO +1 -1
  2. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/version.py +1 -1
  3. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/voyager.py +11 -0
  4. fastapi_voyager-0.5.2/src/fastapi_voyager/web/component/render-graph.js +87 -0
  5. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/index.html +1 -1
  6. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/vue-main.js +7 -16
  7. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/demo.py +4 -2
  8. fastapi_voyager-0.5.1/src/fastapi_voyager/web/component/render-graph.js +0 -59
  9. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/.gitignore +0 -0
  10. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/.python-version +0 -0
  11. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/LICENSE +0 -0
  12. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/README.md +0 -0
  13. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/pyproject.toml +0 -0
  14. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/__init__.py +0 -0
  15. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/cli.py +0 -0
  16. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/filter.py +0 -0
  17. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/module.py +0 -0
  18. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/render.py +0 -0
  19. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/server.py +0 -0
  20. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/type.py +0 -0
  21. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/type_helper.py +0 -0
  22. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/component/route-code-display.js +0 -0
  23. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/component/schema-code-display.js +0 -0
  24. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/component/schema-field-filter.js +0 -0
  25. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/graph-ui.js +0 -0
  26. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/graphviz.svg.css +0 -0
  27. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/graphviz.svg.js +0 -0
  28. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/android-chrome-192x192.png +0 -0
  29. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/android-chrome-512x512.png +0 -0
  30. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/apple-touch-icon.png +0 -0
  31. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/favicon-16x16.png +0 -0
  32. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/favicon-32x32.png +0 -0
  33. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/favicon.ico +0 -0
  34. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/site.webmanifest +0 -0
  35. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/quasar.min.css +0 -0
  36. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/quasar.min.js +0 -0
  37. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/__init__.py +0 -0
  38. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/demo_anno.py +0 -0
  39. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/service/__init__.py +0 -0
  40. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/service/schema.py +0 -0
  41. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/test_analysis.py +0 -0
  42. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/test_import.py +0 -0
  43. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/test_module.py +0 -0
  44. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/test_type_helper.py +0 -0
  45. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/uv.lock +0 -0
  46. {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/voyager.jpg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-voyager
3
- Version: 0.5.1
3
+ Version: 0.5.2
4
4
  Summary: Visualize FastAPI application's routing tree and dependencies
5
5
  Project-URL: Homepage, https://github.com/allmonday/fastapi-voyager
6
6
  Project-URL: Source, https://github.com/allmonday/fastapi-voyager
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.5.1"
2
+ __version__ = "0.5.2"
@@ -182,6 +182,8 @@ class Voyager:
182
182
  update_forward_refs(schema)
183
183
  self.add_to_node_set(schema)
184
184
 
185
+ base_fields = set()
186
+
185
187
  # handle schema inside ensure_subset(schema)
186
188
  if subset_reference := getattr(schema, const.ENSURE_SUBSET_REFERENCE, None):
187
189
  if is_inheritance_of_pydantic_base(subset_reference):
@@ -198,6 +200,12 @@ class Voyager:
198
200
  # handle bases
199
201
  for base_class in schema.__bases__:
200
202
  if is_inheritance_of_pydantic_base(base_class):
203
+ # collect base class field names to avoid duplicating inherited fields
204
+ try:
205
+ base_fields.update(getattr(base_class, 'model_fields', {}).keys())
206
+ except Exception:
207
+ # be defensive in case of unconventional BaseModel subclasses
208
+ pass
201
209
  self.add_to_node_set(base_class)
202
210
  self.add_to_link_set(
203
211
  source=self.generate_node_head(full_class_name(schema)),
@@ -209,6 +217,9 @@ class Voyager:
209
217
 
210
218
  # handle fields
211
219
  for k, v in schema.model_fields.items():
220
+ # skip fields inherited from base classes
221
+ if k in base_fields:
222
+ continue
212
223
  annos = get_core_types(v.annotation)
213
224
  for anno in annos:
214
225
  if anno and is_inheritance_of_pydantic_base(anno):
@@ -0,0 +1,87 @@
1
+ import { GraphUI } from "../graph-ui.js";
2
+ const { defineComponent, ref, onMounted, nextTick } = window.Vue;
3
+
4
+ export default defineComponent({
5
+ name: "RenderGraph",
6
+ props: {
7
+ coreData: { type: [Object, Array], required: false, default: null },
8
+ },
9
+ emits: ["close"],
10
+ setup(props, { emit }) {
11
+ const containerId = `graph-render-${Math.random().toString(36).slice(2, 9)}`;
12
+ const hasRendered = ref(false);
13
+ const loading = ref(false);
14
+ let graphInstance = null;
15
+
16
+ async function ensureGraph() {
17
+ await nextTick();
18
+ if (!graphInstance) {
19
+ graphInstance = new GraphUI(`#${containerId}`);
20
+ }
21
+ }
22
+
23
+ async function renderFromDot(dotText) {
24
+ if (!dotText) return;
25
+ await ensureGraph();
26
+ await graphInstance.render(dotText);
27
+ hasRendered.value = true;
28
+ }
29
+
30
+ async function renderFromCoreData() {
31
+ if (!props.coreData) return;
32
+ loading.value = true;
33
+ try {
34
+ const res = await fetch("/dot-render-core-data", {
35
+ method: "POST",
36
+ headers: { "Content-Type": "application/json" },
37
+ body: JSON.stringify(props.coreData),
38
+ });
39
+ const dotText = await res.text();
40
+ await renderFromDot(dotText);
41
+ if (window.Quasar?.Notify) {
42
+ window.Quasar.Notify.create({ type: "positive", message: "Rendered" });
43
+ }
44
+ } catch (e) {
45
+ console.error("Render from core data failed", e);
46
+ if (window.Quasar?.Notify) {
47
+ window.Quasar.Notify.create({ type: "negative", message: "Render failed" });
48
+ }
49
+ } finally {
50
+ loading.value = false;
51
+ }
52
+ }
53
+
54
+ async function reload() {
55
+ await renderFromCoreData();
56
+ }
57
+
58
+ onMounted(async () => {
59
+ await reload();
60
+ });
61
+
62
+ function close() {
63
+ emit("close");
64
+ }
65
+
66
+ return { containerId, close, hasRendered, reload, loading };
67
+ },
68
+ template: `
69
+ <div style="height:100%; position:relative; background:#fff;">
70
+ <q-btn
71
+ flat dense round icon="close"
72
+ aria-label="Close"
73
+ @click="close"
74
+ style="position:absolute; top:6px; right:6px; z-index:11; background:rgba(255,255,255,0.85);"
75
+ />
76
+ <q-btn
77
+ flat dense round icon="refresh"
78
+ aria-label="Reload"
79
+ :loading="loading"
80
+ @click="reload"
81
+ style="position:absolute; top:6px; right:46px; z-index:11; background:rgba(255,255,255,0.85);"
82
+ />
83
+ <div :id="containerId" style="width:100%; height:100%; overflow:auto; background:#fafafa"></div>
84
+ </div>
85
+ `,
86
+ });
87
+
@@ -250,7 +250,7 @@
250
250
 
251
251
  <!-- Render Graph Dialog (from imported core data) -->
252
252
  <q-dialog v-model="showRenderGraph" :maximized="true" :persistent="false">
253
- <render-graph :dot="renderDotString" @close="showRenderGraph = false" />
253
+ <render-graph :core-data="renderCoreData" @close="showRenderGraph = false" />
254
254
  </q-dialog>
255
255
 
256
256
  <div id="graph" style="width: 100%; flex: 1 1 auto; overflow: auto"></div>
@@ -37,8 +37,8 @@ const app = createApp({
37
37
  const dumpJson = ref("");
38
38
  const showImportDialog = ref(false);
39
39
  const importJsonText = ref("");
40
- const showRenderGraph = ref(false);
41
- const renderDotString = ref("");
40
+ const showRenderGraph = ref(false);
41
+ const renderCoreData = ref(null);
42
42
  const schemaName = ref(""); // used by detail dialog
43
43
  const schemaFieldFilterSchema = ref(null); // external schemaName for schema-field-filter
44
44
  const schemaCodeName = ref("");
@@ -222,19 +222,10 @@ const app = createApp({
222
222
  }
223
223
  return;
224
224
  }
225
- try {
226
- const res = await fetch("/dot-render-core-data", {
227
- method: "POST",
228
- headers: { "Content-Type": "application/json" },
229
- body: JSON.stringify(payloadObj),
230
- });
231
- const dotText = await res.text();
232
- renderDotString.value = dotText;
233
- showRenderGraph.value = true;
234
- showImportDialog.value = false;
235
- } catch (e) {
236
- console.error("Import render failed", e);
237
- }
225
+ // Move the request into RenderGraph component: pass the parsed object and let the component call /dot-render-core-data
226
+ renderCoreData.value = payloadObj;
227
+ showRenderGraph.value = true;
228
+ showImportDialog.value = false;
238
229
  }
239
230
 
240
231
  function showDialog() {
@@ -290,7 +281,7 @@ const app = createApp({
290
281
  onImportConfirm,
291
282
  // render graph dialog
292
283
  showRenderGraph,
293
- renderDotString,
284
+ renderCoreData,
294
285
  };
295
286
  },
296
287
  });
@@ -48,10 +48,12 @@ class PageSprint(serv.Sprint):
48
48
  class PageOverall(BaseModel):
49
49
  sprints: list[PageSprint]
50
50
 
51
+ class PageOverallWrap(PageOverall):
52
+ content: str
51
53
 
52
- @app.get("/page_overall", tags=['for-page'], response_model=PageOverall)
54
+ @app.get("/page_overall", tags=['for-page'], response_model=PageOverallWrap)
53
55
  async def get_page_info():
54
- page_overall = PageOverall(sprints=[]) # focus on schema only
56
+ page_overall = PageOverallWrap(content="Page Overall Content", sprints=[]) # focus on schema only
55
57
  return await Resolver().resolve(page_overall)
56
58
 
57
59
 
@@ -1,59 +0,0 @@
1
- import { GraphUI } from "../graph-ui.js";
2
- const { defineComponent, ref, onMounted, watch, nextTick } = window.Vue;
3
-
4
- // Simple dialog-embeddable component that renders a DOT graph.
5
- // Props:
6
- // - dot: String (required) the DOT source to render
7
- // Emits:
8
- // - close: when the close button is clicked
9
- export default defineComponent({
10
- name: "RenderGraph",
11
- props: {
12
- dot: { type: String, required: true },
13
- },
14
- emits: ["close"],
15
- setup(props, { emit }) {
16
- const containerId = `graph-render-${Math.random().toString(36).slice(2, 9)}`;
17
- const hasRendered = ref(false);
18
- let graphInstance = null;
19
-
20
- async function renderDot() {
21
- if (!props.dot) return;
22
- await nextTick();
23
- if (!graphInstance) {
24
- graphInstance = new GraphUI(`#${containerId}`);
25
- }
26
- await graphInstance.render(props.dot);
27
- hasRendered.value = true;
28
- }
29
-
30
- onMounted(async () => {
31
- await renderDot();
32
- });
33
-
34
- watch(
35
- () => props.dot,
36
- async () => {
37
- await renderDot();
38
- }
39
- );
40
-
41
- function close() {
42
- emit("close");
43
- }
44
-
45
- return { containerId, close, hasRendered };
46
- },
47
- template: `
48
- <div style="height:100%; position:relative; background:#fff;">
49
- <q-btn
50
- flat dense round icon="close"
51
- aria-label="Close"
52
- @click="close"
53
- style="position:absolute; top:6px; right:6px; z-index:11; background:rgba(255,255,255,0.85);"
54
- />
55
- <div :id="containerId" style="width:100%; height:100%; overflow:auto; background:#fafafa"></div>
56
- </div>
57
- `,
58
- });
59
-
File without changes
File without changes