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.
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/PKG-INFO +1 -1
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/version.py +1 -1
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/voyager.py +11 -0
- fastapi_voyager-0.5.2/src/fastapi_voyager/web/component/render-graph.js +87 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/index.html +1 -1
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/vue-main.js +7 -16
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/demo.py +4 -2
- fastapi_voyager-0.5.1/src/fastapi_voyager/web/component/render-graph.js +0 -59
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/.gitignore +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/.python-version +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/LICENSE +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/README.md +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/pyproject.toml +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/__init__.py +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/cli.py +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/filter.py +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/module.py +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/render.py +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/server.py +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/type.py +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/type_helper.py +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/component/route-code-display.js +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/component/schema-code-display.js +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/component/schema-field-filter.js +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/graph-ui.js +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/graphviz.svg.css +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/graphviz.svg.js +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/android-chrome-192x192.png +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/android-chrome-512x512.png +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/apple-touch-icon.png +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/favicon-16x16.png +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/favicon-32x32.png +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/favicon.ico +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/site.webmanifest +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/quasar.min.css +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/quasar.min.js +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/__init__.py +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/demo_anno.py +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/service/__init__.py +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/service/schema.py +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/test_analysis.py +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/test_import.py +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/test_module.py +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/tests/test_type_helper.py +0 -0
- {fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/uv.lock +0 -0
- {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.
|
|
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.
|
|
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 :
|
|
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
|
-
|
|
41
|
-
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
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=
|
|
54
|
+
@app.get("/page_overall", tags=['for-page'], response_model=PageOverallWrap)
|
|
53
55
|
async def get_page_info():
|
|
54
|
-
page_overall =
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/apple-touch-icon.png
RENAMED
|
File without changes
|
{fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/favicon-16x16.png
RENAMED
|
File without changes
|
{fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/favicon-32x32.png
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_voyager-0.5.1 → fastapi_voyager-0.5.2}/src/fastapi_voyager/web/icon/site.webmanifest
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|