fastapi-voyager 0.12.6__py3-none-any.whl → 0.12.8__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 +32 -1
- fastapi_voyager/version.py +1 -1
- fastapi_voyager/voyager.py +16 -0
- fastapi_voyager/web/index.html +58 -36
- fastapi_voyager/web/store.js +8 -4
- fastapi_voyager/web/vue-main.js +185 -71
- {fastapi_voyager-0.12.6.dist-info → fastapi_voyager-0.12.8.dist-info}/METADATA +1 -1
- {fastapi_voyager-0.12.6.dist-info → fastapi_voyager-0.12.8.dist-info}/RECORD +11 -12
- fastapi_voyager/web/component/schema-field-filter.js +0 -190
- {fastapi_voyager-0.12.6.dist-info → fastapi_voyager-0.12.8.dist-info}/WHEEL +0 -0
- {fastapi_voyager-0.12.6.dist-info → fastapi_voyager-0.12.8.dist-info}/entry_points.txt +0 -0
- {fastapi_voyager-0.12.6.dist-info → fastapi_voyager-0.12.8.dist-info}/licenses/LICENSE +0 -0
fastapi_voyager/server.py
CHANGED
|
@@ -34,6 +34,8 @@ def _build_ga_snippet(ga_id: str | None) -> str:
|
|
|
34
34
|
|
|
35
35
|
INITIAL_PAGE_POLICY = Literal['first', 'full', 'empty']
|
|
36
36
|
|
|
37
|
+
# ---------- setup ----------
|
|
38
|
+
|
|
37
39
|
class OptionParam(BaseModel):
|
|
38
40
|
tags: list[Tag]
|
|
39
41
|
schemas: list[SchemaNode]
|
|
@@ -49,11 +51,21 @@ class Payload(BaseModel):
|
|
|
49
51
|
schema_field: str | None = None
|
|
50
52
|
route_name: str | None = None
|
|
51
53
|
show_fields: str = 'object'
|
|
52
|
-
show_meta: bool = False
|
|
53
54
|
brief: bool = False
|
|
54
55
|
hide_primitive_route: bool = False
|
|
55
56
|
show_module: bool = True
|
|
56
57
|
|
|
58
|
+
# ---------- search ----------
|
|
59
|
+
class SearchResultOptionParam(BaseModel):
|
|
60
|
+
tags: list[Tag]
|
|
61
|
+
|
|
62
|
+
class SchemaSearchPayload(BaseModel): # leave tag, route out
|
|
63
|
+
schema_name: str | None = None
|
|
64
|
+
schema_field: str | None = None
|
|
65
|
+
show_fields: str = 'object'
|
|
66
|
+
brief: bool = False
|
|
67
|
+
hide_primitive_route: bool = False
|
|
68
|
+
show_module: bool = True
|
|
57
69
|
|
|
58
70
|
def create_voyager(
|
|
59
71
|
target_app: FastAPI,
|
|
@@ -91,6 +103,25 @@ def create_voyager(
|
|
|
91
103
|
swagger_url=swagger_url,
|
|
92
104
|
initial_page_policy=initial_page_policy)
|
|
93
105
|
|
|
106
|
+
@router.post("/dot-search", response_model=SearchResultOptionParam)
|
|
107
|
+
def get_search_dot(payload: SchemaSearchPayload):
|
|
108
|
+
voyager = Voyager(
|
|
109
|
+
schema=payload.schema_name,
|
|
110
|
+
schema_field=payload.schema_field,
|
|
111
|
+
show_fields=payload.show_fields,
|
|
112
|
+
module_color=module_color,
|
|
113
|
+
hide_primitive_route=payload.hide_primitive_route,
|
|
114
|
+
show_module=payload.show_module,
|
|
115
|
+
)
|
|
116
|
+
voyager.analysis(target_app)
|
|
117
|
+
tags = voyager.calculate_filtered_tag_and_route()
|
|
118
|
+
|
|
119
|
+
for t in tags:
|
|
120
|
+
t.routes.sort(key=lambda r: r.name)
|
|
121
|
+
tags.sort(key=lambda t: t.name)
|
|
122
|
+
|
|
123
|
+
return SearchResultOptionParam(tags=tags)
|
|
124
|
+
|
|
94
125
|
@router.post("/dot", response_class=PlainTextResponse)
|
|
95
126
|
def get_filtered_dot(payload: Payload) -> str:
|
|
96
127
|
voyager = Voyager(
|
fastapi_voyager/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__all__ = ["__version__"]
|
|
2
|
-
__version__ = "0.12.
|
|
2
|
+
__version__ = "0.12.8"
|
fastapi_voyager/voyager.py
CHANGED
|
@@ -297,6 +297,22 @@ class Voyager:
|
|
|
297
297
|
else:
|
|
298
298
|
return tags, routes, links
|
|
299
299
|
|
|
300
|
+
def calculate_filtered_tag_and_route(self):
|
|
301
|
+
_tags, _routes, _, _ = filter_graph(
|
|
302
|
+
schema=self.schema,
|
|
303
|
+
schema_field=self.schema_field,
|
|
304
|
+
tags=self.tags,
|
|
305
|
+
routes=self.routes,
|
|
306
|
+
nodes=self.nodes,
|
|
307
|
+
links=self.links,
|
|
308
|
+
node_set=self.node_set,
|
|
309
|
+
)
|
|
310
|
+
# filter tag.routes based by _routes
|
|
311
|
+
route_ids = {r.id for r in _routes}
|
|
312
|
+
for t in _tags:
|
|
313
|
+
t.routes = [r for r in t.routes if r.id in route_ids]
|
|
314
|
+
return _tags
|
|
315
|
+
|
|
300
316
|
def render_dot(self):
|
|
301
317
|
_tags, _routes, _nodes, _links = filter_graph(
|
|
302
318
|
schema=self.schema,
|
fastapi_voyager/web/index.html
CHANGED
|
@@ -55,6 +55,12 @@
|
|
|
55
55
|
flex: 1 1 auto;
|
|
56
56
|
overflow: auto;
|
|
57
57
|
}
|
|
58
|
+
.tag-bg {
|
|
59
|
+
/* background-color: #287e75; */
|
|
60
|
+
/* border-bottom: 2px solid #009485;
|
|
61
|
+
border-left: 2px solid #009485;
|
|
62
|
+
border-top: 0; */
|
|
63
|
+
}
|
|
58
64
|
|
|
59
65
|
.adjust-fit {
|
|
60
66
|
height: calc(100vh - 54px);
|
|
@@ -107,9 +113,20 @@
|
|
|
107
113
|
<span> FastAPI Voyager </span>
|
|
108
114
|
<span v-if="store.state.version" style="font-size: 12px; margin-left: 8px; font-weight: normal;">{{ store.state.version }}</span>
|
|
109
115
|
</div>
|
|
116
|
+
<div class="col-auto">
|
|
117
|
+
<q-btn
|
|
118
|
+
style="margin-left: 80px"
|
|
119
|
+
outline
|
|
120
|
+
@click="onReset"
|
|
121
|
+
title="clear tag, route selection"
|
|
122
|
+
icon="open_in_full"
|
|
123
|
+
color="primary"
|
|
124
|
+
flat
|
|
125
|
+
class="q-mx-md"
|
|
126
|
+
/>
|
|
127
|
+
</div>
|
|
110
128
|
<div class="col-auto" style="font-size: 16px">
|
|
111
129
|
<q-option-group
|
|
112
|
-
style="margin-left: 80px"
|
|
113
130
|
v-model="store.state.filter.showFields"
|
|
114
131
|
:options="store.state.fieldOptions"
|
|
115
132
|
@update:model-value="(val) => toggleShowField(val)"
|
|
@@ -120,25 +137,40 @@
|
|
|
120
137
|
/>
|
|
121
138
|
</div>
|
|
122
139
|
<div class="col-auto q-ml-auto">
|
|
123
|
-
<q-btn
|
|
124
|
-
outline
|
|
125
|
-
@click="onReset"
|
|
126
|
-
title="may be very slow"
|
|
127
|
-
icon="border_all"
|
|
128
|
-
flat
|
|
129
|
-
class="q-mr-sm"
|
|
130
|
-
label="Full Graph"
|
|
131
|
-
/>
|
|
132
140
|
</div>
|
|
133
|
-
<div class="col-auto">
|
|
134
|
-
<q-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
<div class="col-auto row items-center q-gutter-sm">
|
|
142
|
+
<q-select
|
|
143
|
+
dense
|
|
144
|
+
outlined
|
|
145
|
+
use-input
|
|
146
|
+
fill-input
|
|
147
|
+
hide-selected
|
|
148
|
+
input-debounce="200"
|
|
149
|
+
v-model="store.state.search.schemaName"
|
|
150
|
+
:options="store.state.search.schemaOptions"
|
|
151
|
+
option-label="label"
|
|
152
|
+
option-value="value"
|
|
153
|
+
emit-value
|
|
154
|
+
map-options
|
|
155
|
+
style="min-width: 320px;"
|
|
156
|
+
clearable
|
|
157
|
+
label="Select schema"
|
|
158
|
+
@update:model-value="onSearchSchemaChange"
|
|
159
|
+
@filter="filterSearchSchemas"
|
|
160
|
+
@clear="resetSearch"
|
|
161
|
+
></q-select>
|
|
162
|
+
|
|
163
|
+
<q-select
|
|
164
|
+
dense
|
|
165
|
+
outlined
|
|
166
|
+
v-model="store.state.search.fieldName"
|
|
167
|
+
:disable="!store.state.search.schemaName || store.state.search.fieldOptions.length===0"
|
|
168
|
+
:options="store.state.search.fieldOptions"
|
|
169
|
+
@update:model-value="onSearch"
|
|
170
|
+
style="min-width: 180px"
|
|
171
|
+
clearable
|
|
172
|
+
label="Select field (optional)"
|
|
173
|
+
></q-select>
|
|
142
174
|
</div>
|
|
143
175
|
<div class="col-auto">
|
|
144
176
|
<q-btn
|
|
@@ -147,7 +179,7 @@
|
|
|
147
179
|
flat
|
|
148
180
|
icon="help_outline"
|
|
149
181
|
aria-label="Help"
|
|
150
|
-
style="margin-right:
|
|
182
|
+
style="margin-right: 50px; margin-left: 20px"
|
|
151
183
|
>
|
|
152
184
|
<q-tooltip
|
|
153
185
|
anchor="bottom middle"
|
|
@@ -164,7 +196,7 @@
|
|
|
164
196
|
double click node to view details.
|
|
165
197
|
</li>
|
|
166
198
|
<li>
|
|
167
|
-
shift + click to
|
|
199
|
+
shift + click to search the schema and highlight related nodes.
|
|
168
200
|
</li>
|
|
169
201
|
</ul>
|
|
170
202
|
</div>
|
|
@@ -183,7 +215,6 @@
|
|
|
183
215
|
bordered
|
|
184
216
|
overlay
|
|
185
217
|
>
|
|
186
|
-
<!-- 可拖拽的调整栏 -->
|
|
187
218
|
<div
|
|
188
219
|
@mousedown="startDragDrawer"
|
|
189
220
|
style="
|
|
@@ -239,9 +270,10 @@
|
|
|
239
270
|
v-for="tag in store.state.leftPanel.tags"
|
|
240
271
|
:key="tag.name"
|
|
241
272
|
expand-separator
|
|
242
|
-
:model-value="store.state.leftPanel._tag === tag.name"
|
|
273
|
+
:model-value="store.state.leftPanel._tag === tag.name || store.state.search.mode"
|
|
243
274
|
@update:model-value="(val) => toggleTag(tag.name, val)"
|
|
244
|
-
|
|
275
|
+
@click="() => toggleTag(tag.name, true)"
|
|
276
|
+
:header-class="store.state.leftPanel.tag === tag.name ? 'text-primary text-bold tag-bg' : 'tag-bg'"
|
|
245
277
|
content-class="q-pa-none"
|
|
246
278
|
>
|
|
247
279
|
<template #header>
|
|
@@ -299,7 +331,7 @@
|
|
|
299
331
|
<div style="position: relative; width: 100%; height: 100%;">
|
|
300
332
|
<div id="graph" class="adjust-fit"></div>
|
|
301
333
|
<div style="position: absolute; left: 8px; top: 8px; z-index: 10; background: rgba(255,255,255,0.85); border-radius: 4px; padding: 2px 8px;">
|
|
302
|
-
<div class="q-mt-sm">
|
|
334
|
+
<div class="q-mt-sm" v-if="store.state.modeControl.briefModeEnabled && store.state.search.mode === false">
|
|
303
335
|
<q-toggle
|
|
304
336
|
v-if="store.state.modeControl.briefModeEnabled"
|
|
305
337
|
dense
|
|
@@ -309,7 +341,7 @@
|
|
|
309
341
|
title="skip middle classes, config module_prefix to enable it"
|
|
310
342
|
/>
|
|
311
343
|
</div>
|
|
312
|
-
<div class="q-mt-sm">
|
|
344
|
+
<div class="q-mt-sm" v-if="store.state.modeControl.briefModeEnabled && store.state.search.mode === false">
|
|
313
345
|
<q-toggle
|
|
314
346
|
v-model="store.state.filter.hidePrimitiveRoute"
|
|
315
347
|
@update:model-value="(val) => toggleHidePrimitiveRoute(val)"
|
|
@@ -327,16 +359,6 @@
|
|
|
327
359
|
title="show module cluster"
|
|
328
360
|
/>
|
|
329
361
|
</div>
|
|
330
|
-
<div class="q-mt-sm">
|
|
331
|
-
<q-toggle
|
|
332
|
-
v-model="store.state.modeControl.focus"
|
|
333
|
-
v-show="store.state.schemaDetail.schemaCodeName"
|
|
334
|
-
@update:model-value="val => onFocusChange(val)"
|
|
335
|
-
label="Focus"
|
|
336
|
-
dense
|
|
337
|
-
title="pick a schema and toggle focus on to display related nodes only"
|
|
338
|
-
/>
|
|
339
|
-
</div>
|
|
340
362
|
</div>
|
|
341
363
|
</template>
|
|
342
364
|
</q-splitter>
|
fastapi_voyager/web/store.js
CHANGED
|
@@ -6,6 +6,9 @@ const state = reactive({
|
|
|
6
6
|
},
|
|
7
7
|
|
|
8
8
|
version: '',
|
|
9
|
+
config: {
|
|
10
|
+
initial_page_policy: 'first'
|
|
11
|
+
},
|
|
9
12
|
|
|
10
13
|
swagger: {
|
|
11
14
|
url: ''
|
|
@@ -38,13 +41,14 @@ const state = reactive({
|
|
|
38
41
|
routeItems: []
|
|
39
42
|
},
|
|
40
43
|
|
|
41
|
-
leftPanelFiltered: {
|
|
42
|
-
|
|
43
|
-
},
|
|
44
44
|
|
|
45
45
|
// schema options, schema, fields
|
|
46
46
|
search: {
|
|
47
|
-
|
|
47
|
+
mode: false,
|
|
48
|
+
schemaName: null,
|
|
49
|
+
fieldName: null,
|
|
50
|
+
schemaOptions: [],
|
|
51
|
+
fieldOptions: [],
|
|
48
52
|
},
|
|
49
53
|
|
|
50
54
|
|
fastapi_voyager/web/vue-main.js
CHANGED
|
@@ -1,16 +1,71 @@
|
|
|
1
|
-
import SchemaFieldFilter from "./component/schema-field-filter.js";
|
|
2
1
|
import SchemaCodeDisplay from "./component/schema-code-display.js";
|
|
3
2
|
import RouteCodeDisplay from "./component/route-code-display.js";
|
|
4
|
-
import Demo from
|
|
3
|
+
import Demo from "./component/demo.js";
|
|
5
4
|
import RenderGraph from "./component/render-graph.js";
|
|
6
5
|
import { GraphUI } from "./graph-ui.js";
|
|
7
|
-
import { store } from
|
|
6
|
+
import { store } from "./store.js";
|
|
8
7
|
|
|
9
|
-
const { createApp, onMounted, ref } = window.Vue;
|
|
8
|
+
const { createApp, onMounted, ref, watch } = window.Vue;
|
|
10
9
|
|
|
11
10
|
const app = createApp({
|
|
12
11
|
setup() {
|
|
13
12
|
let graphUI = null;
|
|
13
|
+
const allSchemaOptions = ref([]);
|
|
14
|
+
|
|
15
|
+
const NBSP = String.fromCharCode(160);
|
|
16
|
+
const formatSchemaLabel = (name, id) =>
|
|
17
|
+
`${name}${NBSP}${NBSP}${NBSP}-${NBSP}${NBSP}${NBSP}${id}`;
|
|
18
|
+
|
|
19
|
+
function rebuildSchemaOptions() {
|
|
20
|
+
const dict = store.state.graph.schemaMap || {};
|
|
21
|
+
const opts = Object.values(dict).map((s) => ({
|
|
22
|
+
label: formatSchemaLabel(s.name, s.id),
|
|
23
|
+
value: s.id,
|
|
24
|
+
}));
|
|
25
|
+
allSchemaOptions.value = opts;
|
|
26
|
+
store.state.search.schemaOptions = opts.slice();
|
|
27
|
+
populateFieldOptions(store.state.search.schemaName);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function populateFieldOptions(schemaId) {
|
|
31
|
+
if (!schemaId) {
|
|
32
|
+
store.state.search.fieldOptions = [];
|
|
33
|
+
store.state.search.fieldName = null;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const schema = store.state.graph.schemaMap?.[schemaId];
|
|
37
|
+
if (!schema) {
|
|
38
|
+
store.state.search.fieldOptions = [];
|
|
39
|
+
store.state.search.fieldName = null;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const fields = Array.isArray(schema.fields)
|
|
43
|
+
? schema.fields.map((f) => f.name)
|
|
44
|
+
: [];
|
|
45
|
+
store.state.search.fieldOptions = fields;
|
|
46
|
+
if (!fields.includes(store.state.search.fieldName)) {
|
|
47
|
+
store.state.search.fieldName = null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function filterSearchSchemas(val, update) {
|
|
52
|
+
const needle = (val || "").toLowerCase();
|
|
53
|
+
update(() => {
|
|
54
|
+
if (!needle) {
|
|
55
|
+
store.state.search.schemaOptions = allSchemaOptions.value.slice();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
store.state.search.schemaOptions = allSchemaOptions.value.filter((option) =>
|
|
59
|
+
option.label.toLowerCase().includes(needle)
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function onSearchSchemaChange(val) {
|
|
65
|
+
store.state.search.schemaName = val;
|
|
66
|
+
store.state.search.mode = false;
|
|
67
|
+
onSearch()
|
|
68
|
+
}
|
|
14
69
|
|
|
15
70
|
function readQuerySelection() {
|
|
16
71
|
if (typeof window === "undefined") {
|
|
@@ -55,7 +110,10 @@ const app = createApp({
|
|
|
55
110
|
|
|
56
111
|
function applySelectionFromQuery(selection) {
|
|
57
112
|
let applied = false;
|
|
58
|
-
if (
|
|
113
|
+
if (
|
|
114
|
+
selection.tag &&
|
|
115
|
+
store.state.leftPanel.tags.some((tag) => tag.name === selection.tag)
|
|
116
|
+
) {
|
|
59
117
|
store.state.leftPanel.tag = selection.tag;
|
|
60
118
|
store.state.leftPanel._tag = selection.tag;
|
|
61
119
|
applied = true;
|
|
@@ -72,6 +130,51 @@ const app = createApp({
|
|
|
72
130
|
return applied;
|
|
73
131
|
}
|
|
74
132
|
|
|
133
|
+
async function resetSearch() {
|
|
134
|
+
store.state.search.mode = false;
|
|
135
|
+
store.state.leftPanel.tag = null;
|
|
136
|
+
store.state.leftPanel._tag = null;
|
|
137
|
+
store.state.leftPanel.routeId = null;
|
|
138
|
+
syncSelectionToUrl()
|
|
139
|
+
await loadSearchedTags();
|
|
140
|
+
renderBasedOnInitialPolicy()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function onSearch() {
|
|
144
|
+
console.log('start search')
|
|
145
|
+
store.state.search.mode = true;
|
|
146
|
+
store.state.leftPanel.tag = null;
|
|
147
|
+
store.state.leftPanel._tag = null;
|
|
148
|
+
store.state.leftPanel.routeId = null;
|
|
149
|
+
syncSelectionToUrl()
|
|
150
|
+
await loadSearchedTags();
|
|
151
|
+
await onGenerate();
|
|
152
|
+
}
|
|
153
|
+
async function loadSearchedTags() {
|
|
154
|
+
try {
|
|
155
|
+
const payload = {
|
|
156
|
+
schema_name: store.state.search.schemaName,
|
|
157
|
+
schema_field: store.state.search.fieldName || null,
|
|
158
|
+
show_fields: store.state.filter.showFields,
|
|
159
|
+
brief: store.state.filter.brief,
|
|
160
|
+
hide_primitive_route: store.state.filter.hidePrimitiveRoute,
|
|
161
|
+
show_module: store.state.filter.showModule,
|
|
162
|
+
};
|
|
163
|
+
const res = await fetch("dot-search", {
|
|
164
|
+
method: "POST",
|
|
165
|
+
headers: { "Content-Type": "application/json" },
|
|
166
|
+
body: JSON.stringify(payload),
|
|
167
|
+
});
|
|
168
|
+
if (res.ok) {
|
|
169
|
+
const data = await res.json();
|
|
170
|
+
const tags = Array.isArray(data.tags) ? data.tags : [];
|
|
171
|
+
store.state.leftPanel.tags = tags;
|
|
172
|
+
}
|
|
173
|
+
} catch (err) {
|
|
174
|
+
console.error("dot-search failed", err);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
75
178
|
async function loadInitial() {
|
|
76
179
|
store.state.initializing = true;
|
|
77
180
|
try {
|
|
@@ -81,10 +184,8 @@ const app = createApp({
|
|
|
81
184
|
|
|
82
185
|
const schemasArr = Array.isArray(data.schemas) ? data.schemas : [];
|
|
83
186
|
// Build dict keyed by id for faster lookups and simpler prop passing
|
|
84
|
-
const schemaMap = Object.fromEntries(
|
|
85
|
-
|
|
86
|
-
);
|
|
87
|
-
store.state.graph.schemaMap = schemaMap
|
|
187
|
+
const schemaMap = Object.fromEntries(schemasArr.map((s) => [s.id, s]));
|
|
188
|
+
store.state.graph.schemaMap = schemaMap;
|
|
88
189
|
store.state.graph.schemaKeys = new Set(Object.keys(schemaMap));
|
|
89
190
|
store.state.graph.routeItems = data.tags
|
|
90
191
|
.map((t) => t.routes)
|
|
@@ -93,9 +194,12 @@ const app = createApp({
|
|
|
93
194
|
acc[r.id] = r;
|
|
94
195
|
return acc;
|
|
95
196
|
}, {});
|
|
96
|
-
store.state.modeControl.briefModeEnabled =
|
|
197
|
+
store.state.modeControl.briefModeEnabled =
|
|
198
|
+
data.enable_brief_mode || false;
|
|
97
199
|
store.state.version = data.version || "";
|
|
98
|
-
store.state.swagger.url = data.swagger_url || null
|
|
200
|
+
store.state.swagger.url = data.swagger_url || null;
|
|
201
|
+
|
|
202
|
+
rebuildSchemaOptions();
|
|
99
203
|
|
|
100
204
|
const querySelection = readQuerySelection();
|
|
101
205
|
const restoredFromQuery = applySelectionFromQuery(querySelection);
|
|
@@ -103,19 +207,9 @@ const app = createApp({
|
|
|
103
207
|
syncSelectionToUrl();
|
|
104
208
|
onGenerate();
|
|
105
209
|
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
case "full":
|
|
110
|
-
onGenerate()
|
|
111
|
-
return
|
|
112
|
-
case "empty":
|
|
113
|
-
return
|
|
114
|
-
case "first":
|
|
115
|
-
store.state.leftPanel.tag = store.state.leftPanel.tags.length > 0 ? store.state.leftPanel.tags[0].name : null;
|
|
116
|
-
store.state.leftPanel._tag = store.state.leftPanel.tag;
|
|
117
|
-
onGenerate();
|
|
118
|
-
return
|
|
210
|
+
} else {
|
|
211
|
+
store.state.config.initial_page_policy = data.initial_page_policy
|
|
212
|
+
renderBasedOnInitialPolicy()
|
|
119
213
|
}
|
|
120
214
|
|
|
121
215
|
// default route options placeholder
|
|
@@ -126,28 +220,41 @@ const app = createApp({
|
|
|
126
220
|
}
|
|
127
221
|
}
|
|
128
222
|
|
|
129
|
-
async function
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
223
|
+
async function renderBasedOnInitialPolicy() {
|
|
224
|
+
switch (store.state.config.initial_page_policy) {
|
|
225
|
+
case "full":
|
|
226
|
+
onGenerate();
|
|
227
|
+
return;
|
|
228
|
+
case "empty":
|
|
229
|
+
return;
|
|
230
|
+
case "first":
|
|
231
|
+
store.state.leftPanel.tag =
|
|
232
|
+
store.state.leftPanel.tags.length > 0
|
|
233
|
+
? store.state.leftPanel.tags[0].name
|
|
234
|
+
: null;
|
|
235
|
+
store.state.leftPanel._tag = store.state.leftPanel.tag;
|
|
236
|
+
syncSelectionToUrl()
|
|
237
|
+
onGenerate();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
139
240
|
}
|
|
140
241
|
|
|
141
242
|
async function onGenerate(resetZoom = true) {
|
|
142
|
-
const
|
|
243
|
+
const activeSchema = store.state.search.mode
|
|
244
|
+
? store.state.search.schemaName
|
|
245
|
+
: null;
|
|
246
|
+
const activeField = store.state.search.mode
|
|
247
|
+
? store.state.search.fieldName
|
|
248
|
+
: null;
|
|
143
249
|
store.state.generating = true;
|
|
144
250
|
try {
|
|
145
251
|
const payload = {
|
|
146
252
|
tags: store.state.leftPanel.tag ? [store.state.leftPanel.tag] : null,
|
|
147
|
-
schema_name:
|
|
253
|
+
schema_name: activeSchema || null,
|
|
254
|
+
schema_field: activeField || null,
|
|
148
255
|
route_name: store.state.leftPanel.routeId || null,
|
|
149
256
|
show_fields: store.state.filter.showFields,
|
|
150
|
-
brief: store.state.
|
|
257
|
+
brief: store.state.filter.brief,
|
|
151
258
|
hide_primitive_route: store.state.filter.hidePrimitiveRoute,
|
|
152
259
|
show_module: store.state.filter.showModule,
|
|
153
260
|
};
|
|
@@ -163,9 +270,9 @@ const app = createApp({
|
|
|
163
270
|
graphUI = new GraphUI("#graph", {
|
|
164
271
|
onSchemaShiftClick: (id) => {
|
|
165
272
|
if (store.state.graph.schemaKeys.has(id)) {
|
|
166
|
-
|
|
167
|
-
store.state.
|
|
168
|
-
|
|
273
|
+
store.state.search.mode = true;
|
|
274
|
+
store.state.search.schemaName = id;
|
|
275
|
+
onSearch();
|
|
169
276
|
}
|
|
170
277
|
},
|
|
171
278
|
onSchemaClick: (id) => {
|
|
@@ -192,29 +299,18 @@ const app = createApp({
|
|
|
192
299
|
}
|
|
193
300
|
}
|
|
194
301
|
|
|
195
|
-
function showSearchDialog() {
|
|
196
|
-
store.state.searchDialog.show = true;
|
|
197
|
-
store.state.searchDialog.schema = null;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
302
|
function resetDetailPanels() {
|
|
201
303
|
store.state.rightDrawer.drawer = false;
|
|
202
|
-
store.state.routeDetail.show = false
|
|
304
|
+
store.state.routeDetail.show = false;
|
|
203
305
|
store.state.schemaDetail.schemaCodeName = "";
|
|
204
306
|
}
|
|
205
307
|
|
|
206
308
|
async function onReset() {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
// state.showFields = "object";
|
|
214
|
-
store.state.modeControl.focus = false;
|
|
215
|
-
store.state.schemaDetail.schemaCodeName = "";
|
|
216
|
-
onGenerate();
|
|
217
|
-
syncSelectionToUrl();
|
|
309
|
+
store.state.leftPanel.tag = null;
|
|
310
|
+
store.state.leftPanel._tag = null;
|
|
311
|
+
store.state.leftPanel.routeId = null;
|
|
312
|
+
syncSelectionToUrl()
|
|
313
|
+
onGenerate()
|
|
218
314
|
}
|
|
219
315
|
|
|
220
316
|
function toggleTag(tagName, expanded = null) {
|
|
@@ -223,7 +319,6 @@ const app = createApp({
|
|
|
223
319
|
store.state.leftPanel.tag = tagName;
|
|
224
320
|
store.state.leftPanel.routeId = "";
|
|
225
321
|
|
|
226
|
-
store.state.modeControl.focus = false;
|
|
227
322
|
store.state.schemaDetail.schemaCodeName = "";
|
|
228
323
|
onGenerate();
|
|
229
324
|
} else {
|
|
@@ -231,7 +326,7 @@ const app = createApp({
|
|
|
231
326
|
}
|
|
232
327
|
|
|
233
328
|
store.state.rightDrawer.drawer = false;
|
|
234
|
-
store.state.routeDetail.show = false
|
|
329
|
+
store.state.routeDetail.show = false;
|
|
235
330
|
syncSelectionToUrl();
|
|
236
331
|
}
|
|
237
332
|
|
|
@@ -242,8 +337,7 @@ const app = createApp({
|
|
|
242
337
|
store.state.leftPanel.routeId = routeId;
|
|
243
338
|
}
|
|
244
339
|
store.state.rightDrawer.drawer = false;
|
|
245
|
-
store.state.routeDetail.show = false
|
|
246
|
-
store.state.modeControl.focus = false;
|
|
340
|
+
store.state.routeDetail.show = false;
|
|
247
341
|
store.state.schemaDetail.schemaCodeName = "";
|
|
248
342
|
onGenerate();
|
|
249
343
|
syncSelectionToUrl();
|
|
@@ -251,7 +345,7 @@ const app = createApp({
|
|
|
251
345
|
|
|
252
346
|
function toggleShowModule(val) {
|
|
253
347
|
store.state.filter.showModule = val;
|
|
254
|
-
onGenerate()
|
|
348
|
+
onGenerate();
|
|
255
349
|
}
|
|
256
350
|
|
|
257
351
|
function toggleShowField(field) {
|
|
@@ -260,7 +354,7 @@ const app = createApp({
|
|
|
260
354
|
}
|
|
261
355
|
|
|
262
356
|
function toggleBrief(val) {
|
|
263
|
-
store.state.
|
|
357
|
+
store.state.filter.brief = val;
|
|
264
358
|
onGenerate();
|
|
265
359
|
}
|
|
266
360
|
|
|
@@ -293,24 +387,45 @@ const app = createApp({
|
|
|
293
387
|
e.preventDefault();
|
|
294
388
|
}
|
|
295
389
|
|
|
390
|
+
watch(
|
|
391
|
+
() => store.state.graph.schemaMap,
|
|
392
|
+
() => {
|
|
393
|
+
rebuildSchemaOptions();
|
|
394
|
+
},
|
|
395
|
+
{ deep: false }
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
watch(
|
|
399
|
+
() => store.state.search.schemaName,
|
|
400
|
+
(schemaId) => {
|
|
401
|
+
store.state.search.schemaOptions = allSchemaOptions.value.slice();
|
|
402
|
+
populateFieldOptions(schemaId);
|
|
403
|
+
if (!schemaId) {
|
|
404
|
+
store.state.search.mode = false;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
);
|
|
408
|
+
|
|
296
409
|
onMounted(async () => {
|
|
297
|
-
document.body.classList.remove("app-loading")
|
|
410
|
+
document.body.classList.remove("app-loading");
|
|
298
411
|
await loadInitial();
|
|
299
412
|
// Reveal app content only after initial JS/data is ready
|
|
300
413
|
});
|
|
301
414
|
|
|
302
415
|
return {
|
|
303
416
|
store,
|
|
417
|
+
onSearch,
|
|
418
|
+
resetSearch,
|
|
419
|
+
filterSearchSchemas,
|
|
420
|
+
onSearchSchemaChange,
|
|
304
421
|
toggleTag,
|
|
305
422
|
toggleBrief,
|
|
306
423
|
toggleHidePrimitiveRoute,
|
|
307
424
|
selectRoute,
|
|
308
425
|
onGenerate,
|
|
309
426
|
onReset,
|
|
310
|
-
showSearchDialog,
|
|
311
427
|
toggleShowField,
|
|
312
428
|
startDragDrawer,
|
|
313
|
-
onFocusChange,
|
|
314
429
|
toggleShowModule,
|
|
315
430
|
};
|
|
316
431
|
},
|
|
@@ -323,10 +438,9 @@ if (window.Quasar && typeof window.Quasar.setCssVar === "function") {
|
|
|
323
438
|
window.Quasar.setCssVar("primary", "#009485");
|
|
324
439
|
}
|
|
325
440
|
|
|
326
|
-
app.component("schema-
|
|
327
|
-
app.component("
|
|
328
|
-
app.component("
|
|
329
|
-
app.component("
|
|
330
|
-
app.component('demo-component', Demo)
|
|
441
|
+
app.component("schema-code-display", SchemaCodeDisplay); // double click to see node details
|
|
442
|
+
app.component("route-code-display", RouteCodeDisplay); // double click to see route details
|
|
443
|
+
app.component("render-graph", RenderGraph); // for debug, render pasted dot content
|
|
444
|
+
app.component("demo-component", Demo);
|
|
331
445
|
|
|
332
446
|
app.mount("#q-app");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-voyager
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.8
|
|
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
|
|
@@ -3,24 +3,23 @@ fastapi_voyager/cli.py,sha256=td3yIIigEomhSdDO-Xkh-CgpEwCafwlwnpvxnT9QsBo,10488
|
|
|
3
3
|
fastapi_voyager/filter.py,sha256=AN_HIu8-DtKisIq5mFt7CnqRHtxKewedNGyyaI82hSY,11529
|
|
4
4
|
fastapi_voyager/module.py,sha256=h9YR3BpS-CAcJW9WCdVkF4opqwY32w9T67g9GfdLytk,3425
|
|
5
5
|
fastapi_voyager/render.py,sha256=7jSChISqTV2agaO55thhwyrOhqVOMux4x7k8rSQROnU,9960
|
|
6
|
-
fastapi_voyager/server.py,sha256=
|
|
6
|
+
fastapi_voyager/server.py,sha256=wwCFl1eeq_jn8nfIwcy7s8Sh6MB55SN8u9AsJRnkFWQ,7979
|
|
7
7
|
fastapi_voyager/type.py,sha256=7EL1zaIwKVRGpLig7fqaOrZGN5k0Rm31C9COfck3CSs,1750
|
|
8
8
|
fastapi_voyager/type_helper.py,sha256=JXD_OE_xTARkGjWDsnO_xfvyZ0vcwViYyqCp6oEHBTM,9719
|
|
9
|
-
fastapi_voyager/version.py,sha256=
|
|
10
|
-
fastapi_voyager/voyager.py,sha256=
|
|
9
|
+
fastapi_voyager/version.py,sha256=8OqYP1mxS3xWjsLRv38vU0MvxCcmF8rJa6hCwrGsIhw,49
|
|
10
|
+
fastapi_voyager/voyager.py,sha256=qJZvkLh5W_18X5B4pJ8XYbytx2Wvcz-0OnYoHWw8XlA,14046
|
|
11
11
|
fastapi_voyager/web/graph-ui.js,sha256=wxKjmVEpaMJZXMTW7tJ4BiaACCI1oVN6cx7hSMI0K5U,6428
|
|
12
12
|
fastapi_voyager/web/graphviz.svg.css,sha256=zDCjjpT0Idufu5YOiZI76PL70-avP3vTyzGPh9M85Do,1563
|
|
13
13
|
fastapi_voyager/web/graphviz.svg.js,sha256=wZwz_lBztoXmujEN21P0w-HMpdmbqPwTQQ6Ebxd9rGo,18569
|
|
14
|
-
fastapi_voyager/web/index.html,sha256=
|
|
14
|
+
fastapi_voyager/web/index.html,sha256=gF73eTnh5M2PcjWFOsDzqvCQBdl4KpaZW28hjS0iJnM,18454
|
|
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/store.js,sha256=
|
|
18
|
-
fastapi_voyager/web/vue-main.js,sha256=
|
|
17
|
+
fastapi_voyager/web/store.js,sha256=ul6I00XqT9FByAD20JgO2mzdbYCZhTI-CP5t0g_k6yo,1776
|
|
18
|
+
fastapi_voyager/web/vue-main.js,sha256=_yxkrWqxNIUMc3LMBi0TxT5SnDlV-_0n9tPaJdlwiFg,14064
|
|
19
19
|
fastapi_voyager/web/component/demo.js,sha256=bQb16Un4XZ3Mf8qL6gvyrXe_mmA3V3mSIRMQAWg2MNk,352
|
|
20
20
|
fastapi_voyager/web/component/render-graph.js,sha256=e8Xgh2Kl-nYU0P1gstEmAepCgFnk2J6UdxW8TlMafGs,2322
|
|
21
21
|
fastapi_voyager/web/component/route-code-display.js,sha256=8NJPPjNRUC21gjpY8XYEQs4RBbhX1pCiqEhJp39ku6k,3678
|
|
22
22
|
fastapi_voyager/web/component/schema-code-display.js,sha256=qKUMV2RFQzR8deof2iC4vyp65UaWadtVsDAXjY-i3vE,7042
|
|
23
|
-
fastapi_voyager/web/component/schema-field-filter.js,sha256=i6Zp02wfSBWQb4AJ7H_Q9vgCVzLaVnH1I2JIot0sV-0,6172
|
|
24
23
|
fastapi_voyager/web/icon/android-chrome-192x192.png,sha256=35sBy6jmUFJCcquStaafHH1qClZIbd-X3PIKSeLkrNo,37285
|
|
25
24
|
fastapi_voyager/web/icon/android-chrome-512x512.png,sha256=eb2eDjCwIruc05029_0L9hcrkVkv8KceLn1DJMYU0zY,210789
|
|
26
25
|
fastapi_voyager/web/icon/apple-touch-icon.png,sha256=gnWK46tPnvSw1-oYZjgI02wpoO4OrIzsVzGHC5oKWO0,33187
|
|
@@ -28,8 +27,8 @@ fastapi_voyager/web/icon/favicon-16x16.png,sha256=JC07jEzfIYxBIoQn_FHXvyHuxESdhW
|
|
|
28
27
|
fastapi_voyager/web/icon/favicon-32x32.png,sha256=C7v1h58cfWOsiLp9yOIZtlx-dLasBcq3NqpHVGRmpt4,1859
|
|
29
28
|
fastapi_voyager/web/icon/favicon.ico,sha256=tZolYIXkkBcFiYl1A8ksaXN2VjGamzcSdes838dLvNc,15406
|
|
30
29
|
fastapi_voyager/web/icon/site.webmanifest,sha256=ep4Hzh9zhmiZF2At3Fp1dQrYQuYF_3ZPZxc1KcGBvwQ,263
|
|
31
|
-
fastapi_voyager-0.12.
|
|
32
|
-
fastapi_voyager-0.12.
|
|
33
|
-
fastapi_voyager-0.12.
|
|
34
|
-
fastapi_voyager-0.12.
|
|
35
|
-
fastapi_voyager-0.12.
|
|
30
|
+
fastapi_voyager-0.12.8.dist-info/METADATA,sha256=dLqApqdWozRSnnj1riYzm6AcyhBUeOV3gfjONHSHV2E,6523
|
|
31
|
+
fastapi_voyager-0.12.8.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
32
|
+
fastapi_voyager-0.12.8.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
|
|
33
|
+
fastapi_voyager-0.12.8.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
|
|
34
|
+
fastapi_voyager-0.12.8.dist-info/RECORD,,
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import { GraphUI } from "../graph-ui.js";
|
|
2
|
-
const { defineComponent, reactive, ref, onMounted, nextTick, watch } =
|
|
3
|
-
window.Vue;
|
|
4
|
-
|
|
5
|
-
// SchemaFieldFilter component
|
|
6
|
-
// Features:
|
|
7
|
-
// - Fetch initial schemas list (GET /dot) and build schema options
|
|
8
|
-
// - Second selector lists fields of the chosen schema
|
|
9
|
-
// - Query button disabled until a schema is selected
|
|
10
|
-
// - On query: POST /dot with schema_name + optional schema_field; render returned DOT in #graph-schema-field
|
|
11
|
-
// - Uses GraphUI once and re-renders
|
|
12
|
-
// - Emits 'queried' event after successful render (payload: { schemaName, fieldName })
|
|
13
|
-
export default defineComponent({
|
|
14
|
-
name: "SchemaFieldFilter",
|
|
15
|
-
props: {
|
|
16
|
-
schemaName: { type: String, default: null }, // external injection triggers auto-query
|
|
17
|
-
schemas: { type: Object, default: () => ({}) },
|
|
18
|
-
},
|
|
19
|
-
emits: ["queried", "close"],
|
|
20
|
-
setup(props, { emit }) {
|
|
21
|
-
const state = reactive({
|
|
22
|
-
loadingSchemas: false,
|
|
23
|
-
querying: false,
|
|
24
|
-
schemas: [], // [{ name, fullname, fields: [{name,...}] }]
|
|
25
|
-
schemaOptions: [], // [{ label, value }]
|
|
26
|
-
fieldOptions: [], // [ field.name ]
|
|
27
|
-
schemaFullname: null,
|
|
28
|
-
fieldName: null,
|
|
29
|
-
error: null,
|
|
30
|
-
showFields: "object",
|
|
31
|
-
showFieldOptions: [
|
|
32
|
-
{ label: "No fields", value: "single" },
|
|
33
|
-
{ label: "Object fields", value: "object" },
|
|
34
|
-
{ label: "All fields", value: "all" },
|
|
35
|
-
],
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
let graphInstance = null;
|
|
39
|
-
let lastAppliedExternal = null;
|
|
40
|
-
|
|
41
|
-
async function loadSchemas() {
|
|
42
|
-
// Use externally provided props.schemas dict directly; no network call.
|
|
43
|
-
state.error = null;
|
|
44
|
-
const dict =
|
|
45
|
-
props.schemas && typeof props.schemas === "object" ? props.schemas : {};
|
|
46
|
-
// Flatten to array for local operations
|
|
47
|
-
state.schemas = Object.values(dict);
|
|
48
|
-
state.schemaOptions = state.schemas.map((s) => ({
|
|
49
|
-
label: `${s.name} - ${s.id}`,
|
|
50
|
-
value: s.id,
|
|
51
|
-
}));
|
|
52
|
-
// Maintain compatibility: loadingSchemas flag toggled quickly (no async work)
|
|
53
|
-
state.loadingSchemas = false;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function onFilterSchemas(val, update) {
|
|
57
|
-
const needle = (val || "").toLowerCase();
|
|
58
|
-
update(() => {
|
|
59
|
-
let opts = state.schemas.map((s) => ({
|
|
60
|
-
label: `${s.name} - ${s.id}`,
|
|
61
|
-
value: s.id,
|
|
62
|
-
}));
|
|
63
|
-
if (needle) {
|
|
64
|
-
opts = opts.filter((o) => o.label.toLowerCase().includes(needle));
|
|
65
|
-
}
|
|
66
|
-
state.schemaOptions = opts;
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function onSchemaChange(val) {
|
|
71
|
-
state.schemaFullname = val;
|
|
72
|
-
state.fieldName = null;
|
|
73
|
-
const schema = state.schemas.find((s) => s.id === val);
|
|
74
|
-
state.fieldOptions = schema ? schema.fields.map((f) => f.name) : [];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async function onQuery() {
|
|
78
|
-
if (!state.schemaFullname) return;
|
|
79
|
-
state.querying = true;
|
|
80
|
-
state.error = null;
|
|
81
|
-
try {
|
|
82
|
-
const payload = {
|
|
83
|
-
schema_name: state.schemaFullname,
|
|
84
|
-
schema_field: state.fieldName || null,
|
|
85
|
-
show_fields: state.showFields,
|
|
86
|
-
};
|
|
87
|
-
const res = await fetch("dot", {
|
|
88
|
-
method: "POST",
|
|
89
|
-
headers: { "Content-Type": "application/json" },
|
|
90
|
-
body: JSON.stringify(payload),
|
|
91
|
-
});
|
|
92
|
-
const dotText = await res.text();
|
|
93
|
-
if (!graphInstance) {
|
|
94
|
-
graphInstance = new GraphUI("#graph-schema-field");
|
|
95
|
-
}
|
|
96
|
-
await graphInstance.render(dotText);
|
|
97
|
-
emit("queried", {
|
|
98
|
-
schemaName: state.schemaFullname,
|
|
99
|
-
fieldName: state.fieldName,
|
|
100
|
-
});
|
|
101
|
-
} catch (e) {
|
|
102
|
-
state.error = "Query failed";
|
|
103
|
-
console.error("SchemaFieldFilter query failed", e);
|
|
104
|
-
} finally {
|
|
105
|
-
state.querying = false;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function applyExternalSchema(name) {
|
|
110
|
-
if (!name || !state.schemas.length) return;
|
|
111
|
-
if (lastAppliedExternal === name) return; // avoid duplicate
|
|
112
|
-
const schema = state.schemas.find((s) => s.id === name);
|
|
113
|
-
if (!schema) return;
|
|
114
|
-
state.schemaFullname = schema.id;
|
|
115
|
-
state.fieldOptions = schema.fields.map((f) => f.name);
|
|
116
|
-
state.fieldName = null; // reset field for external injection
|
|
117
|
-
lastAppliedExternal = name;
|
|
118
|
-
// auto query
|
|
119
|
-
onQuery();
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
onMounted(async () => {
|
|
123
|
-
await nextTick();
|
|
124
|
-
await loadSchemas();
|
|
125
|
-
if (props.schemaName) {
|
|
126
|
-
applyExternalSchema(props.schemaName);
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
function close() {
|
|
131
|
-
emit("close");
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return { state, onSchemaChange, onQuery, close, onFilterSchemas };
|
|
135
|
-
},
|
|
136
|
-
template: `
|
|
137
|
-
<div style="height:100%; position:relative; background:#fff;">
|
|
138
|
-
<div style="position:absolute; top:8px; left:8px; z-index:10; background:rgba(255,255,255,0.95); padding:8px 10px; border-radius:4px; box-shadow:0 1px 3px rgba(0,0,0,0.15);" class="q-gutter-sm row items-center">
|
|
139
|
-
<q-select
|
|
140
|
-
dense outlined use-input input-debounce="0"
|
|
141
|
-
v-model="state.schemaFullname"
|
|
142
|
-
:options="state.schemaOptions"
|
|
143
|
-
option-label="label"
|
|
144
|
-
option-value="value"
|
|
145
|
-
emit-value
|
|
146
|
-
map-options
|
|
147
|
-
:loading="state.loadingSchemas"
|
|
148
|
-
style="min-width:220px"
|
|
149
|
-
clearable
|
|
150
|
-
label="Select schema"
|
|
151
|
-
@update:model-value="onSchemaChange"
|
|
152
|
-
@filter="onFilterSchemas"
|
|
153
|
-
/>
|
|
154
|
-
<q-select
|
|
155
|
-
dense outlined
|
|
156
|
-
v-model="state.fieldName"
|
|
157
|
-
:disable="!state.schemaFullname || state.fieldOptions.length===0"
|
|
158
|
-
:options="state.fieldOptions"
|
|
159
|
-
style="min-width:180px"
|
|
160
|
-
clearable
|
|
161
|
-
label="Select field (optional)"
|
|
162
|
-
/>
|
|
163
|
-
<q-option-group
|
|
164
|
-
v-model="state.showFields"
|
|
165
|
-
:options="state.showFieldOptions"
|
|
166
|
-
type="radio"
|
|
167
|
-
inline
|
|
168
|
-
dense
|
|
169
|
-
color="primary"
|
|
170
|
-
style="min-width:260px"
|
|
171
|
-
/>
|
|
172
|
-
<q-btn
|
|
173
|
-
class="q-ml-md"
|
|
174
|
-
icon="search"
|
|
175
|
-
label="Search"
|
|
176
|
-
outline
|
|
177
|
-
:disable="!state.schemaFullname"
|
|
178
|
-
:loading="state.querying"
|
|
179
|
-
@click="onQuery" />
|
|
180
|
-
<q-btn
|
|
181
|
-
flat dense round icon="close"
|
|
182
|
-
aria-label="Close"
|
|
183
|
-
@click="close"
|
|
184
|
-
/>
|
|
185
|
-
</div>
|
|
186
|
-
<div v-if="state.error" style="position:absolute; top:52px; left:8px; z-index:10; color:#c10015; font-size:12px;">{{ state.error }}</div>
|
|
187
|
-
<div id="graph-schema-field" style="width:100%; height:100%; overflow:auto; background:#fafafa"></div>
|
|
188
|
-
</div>
|
|
189
|
-
`,
|
|
190
|
-
});
|
|
File without changes
|
|
File without changes
|
|
File without changes
|