@saltcorn/builder 0.9.4-beta.1 → 0.9.4-beta.11
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.
- package/dist/builder_bundle.js +15 -11
- package/package.json +2 -1
- package/src/components/Builder.js +59 -13
- package/src/components/Library.js +34 -19
- package/src/components/Toolbox.js +112 -163
- package/src/components/elements/BoxModelEditor.js +8 -8
- package/src/components/elements/Columns.js +4 -4
- package/src/components/elements/Container.js +26 -2
- package/src/components/elements/DropMenu.js +31 -2
- package/src/components/elements/Link.js +1 -0
- package/src/components/elements/RelationBadges.js +53 -44
- package/src/components/elements/Tabs.js +118 -40
- package/src/components/elements/View.js +86 -40
- package/src/components/elements/ViewLink.js +80 -34
- package/src/components/elements/utils.js +139 -76
- package/src/components/storage.js +6 -0
- package/tests/relations_finder.test.js +57 -92
- package/tests/test_data.js +0 -163
|
@@ -17,13 +17,18 @@ import {
|
|
|
17
17
|
buildOptions,
|
|
18
18
|
HelpTopicLink,
|
|
19
19
|
initialRelation,
|
|
20
|
-
|
|
21
|
-
updateRelationsCache,
|
|
20
|
+
buildLayers,
|
|
22
21
|
} from "./utils";
|
|
23
22
|
|
|
24
23
|
import { RelationBadges } from "./RelationBadges";
|
|
25
24
|
import { RelationOnDemandPicker } from "./RelationOnDemandPicker";
|
|
26
25
|
|
|
26
|
+
import {
|
|
27
|
+
RelationsFinder,
|
|
28
|
+
Relation,
|
|
29
|
+
buildTableCaches,
|
|
30
|
+
} from "@saltcorn/common-code";
|
|
31
|
+
|
|
27
32
|
export /**
|
|
28
33
|
* @param {object} props
|
|
29
34
|
* @param {*} props.name
|
|
@@ -48,7 +53,6 @@ const View = ({ name, view, configuration, state }) => {
|
|
|
48
53
|
if (rest.startsWith(".")) viewname = prefix;
|
|
49
54
|
else viewname = rest;
|
|
50
55
|
}
|
|
51
|
-
|
|
52
56
|
const theview = options.views.find((v) => v.name === viewname);
|
|
53
57
|
const label = theview ? theview.label : view;
|
|
54
58
|
const { previews, setPreviews } = React.useContext(previewCtx);
|
|
@@ -109,10 +113,23 @@ const ViewSettings = () => {
|
|
|
109
113
|
extra_state_fml,
|
|
110
114
|
} = node;
|
|
111
115
|
const options = React.useContext(optionsCtx);
|
|
112
|
-
const {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
const {
|
|
117
|
+
tables,
|
|
118
|
+
views,
|
|
119
|
+
max_relations_layer_depth,
|
|
120
|
+
tableName,
|
|
121
|
+
excluded_subview_templates,
|
|
122
|
+
} = options;
|
|
123
|
+
// not needed in page editor
|
|
124
|
+
let finder = null;
|
|
125
|
+
let tableCaches = null;
|
|
126
|
+
if (tables && views) {
|
|
127
|
+
finder = useMemo(
|
|
128
|
+
() => new RelationsFinder(tables, views, max_relations_layer_depth),
|
|
129
|
+
[undefined]
|
|
130
|
+
);
|
|
131
|
+
tableCaches = useMemo(() => buildTableCaches(tables), [undefined]);
|
|
132
|
+
}
|
|
116
133
|
const fixed_state_fields =
|
|
117
134
|
options.fixed_state_fields && options.fixed_state_fields[view];
|
|
118
135
|
const { setPreviews } = React.useContext(previewCtx);
|
|
@@ -135,27 +152,48 @@ const ViewSettings = () => {
|
|
|
135
152
|
else viewname = rest;
|
|
136
153
|
}
|
|
137
154
|
if (viewname.includes(".")) viewname = viewname.split(".")[0];
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
155
|
+
|
|
156
|
+
if (
|
|
157
|
+
finder &&
|
|
158
|
+
!(relationsCache[tableName] && relationsCache[tableName][viewname])
|
|
159
|
+
) {
|
|
160
|
+
const relations = finder.findRelations(
|
|
161
|
+
tableName,
|
|
162
|
+
viewname,
|
|
163
|
+
excluded_subview_templates
|
|
164
|
+
);
|
|
165
|
+
const layers = buildLayers(
|
|
166
|
+
relations,
|
|
167
|
+
tableName,
|
|
168
|
+
tableCaches.tableNameCache
|
|
145
169
|
);
|
|
146
|
-
|
|
147
|
-
|
|
170
|
+
relationsCache[tableName] = relationsCache[tableName] || {};
|
|
171
|
+
relationsCache[tableName][viewname] = { relations, layers };
|
|
172
|
+
setRelationsCache({ ...relationsCache });
|
|
173
|
+
}
|
|
174
|
+
const [relationsData, setRelationsData] = finder
|
|
175
|
+
? React.useState(relationsCache[tableName][viewname])
|
|
148
176
|
: [undefined, undefined];
|
|
149
|
-
let safeRelation =
|
|
177
|
+
let safeRelation = null;
|
|
178
|
+
const subView = views.find((view) => view.name === viewname);
|
|
179
|
+
if (relation) {
|
|
180
|
+
const subTbl = tables.find((tbl) => tbl.id === subView.table_id);
|
|
181
|
+
safeRelation = new Relation(
|
|
182
|
+
relation,
|
|
183
|
+
subTbl ? subTbl.name : "",
|
|
184
|
+
subView.display_type
|
|
185
|
+
);
|
|
186
|
+
}
|
|
150
187
|
if (
|
|
151
188
|
options.mode !== "filter" &&
|
|
189
|
+
subView.table_id &&
|
|
152
190
|
!safeRelation &&
|
|
153
191
|
!hasLegacyRelation &&
|
|
154
|
-
relations
|
|
192
|
+
relationsData?.relations.length > 0
|
|
155
193
|
) {
|
|
156
|
-
safeRelation = initialRelation(relations
|
|
194
|
+
safeRelation = initialRelation(relationsData.relations);
|
|
157
195
|
setProp((prop) => {
|
|
158
|
-
prop.relation = safeRelation;
|
|
196
|
+
prop.relation = safeRelation.relationString;
|
|
159
197
|
});
|
|
160
198
|
}
|
|
161
199
|
const helpContext = { view_name: viewname };
|
|
@@ -169,24 +207,32 @@ const ViewSettings = () => {
|
|
|
169
207
|
prop.view = target_value;
|
|
170
208
|
});
|
|
171
209
|
} else {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
finder,
|
|
177
|
-
target_value
|
|
210
|
+
const newRelations = finder.findRelations(
|
|
211
|
+
tableName,
|
|
212
|
+
target_value,
|
|
213
|
+
excluded_subview_templates
|
|
178
214
|
);
|
|
179
|
-
const
|
|
180
|
-
|
|
215
|
+
const layers = buildLayers(
|
|
216
|
+
newRelations,
|
|
217
|
+
tableName,
|
|
218
|
+
tableCaches.tableNameCache
|
|
219
|
+
);
|
|
220
|
+
relationsCache[tableName] = relationsCache[tableName] || {};
|
|
221
|
+
relationsCache[tableName][target_value] = {
|
|
222
|
+
relations: newRelations,
|
|
223
|
+
layers,
|
|
224
|
+
};
|
|
225
|
+
if (newRelations.length > 0) {
|
|
181
226
|
setProp((prop) => {
|
|
182
227
|
prop.view = target_value;
|
|
183
|
-
prop.relation = initialRelation(
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
228
|
+
prop.relation = initialRelation(newRelations).relationString;
|
|
229
|
+
});
|
|
230
|
+
setRelationsData({ relations: newRelations, layers });
|
|
231
|
+
} else
|
|
232
|
+
window.notifyAlert({
|
|
233
|
+
type: "warning",
|
|
234
|
+
text: `${target_value} has no relations`,
|
|
187
235
|
});
|
|
188
|
-
setRelations(newRelations);
|
|
189
|
-
}
|
|
190
236
|
}
|
|
191
237
|
}
|
|
192
238
|
}
|
|
@@ -194,7 +240,7 @@ const ViewSettings = () => {
|
|
|
194
240
|
|
|
195
241
|
return (
|
|
196
242
|
<div>
|
|
197
|
-
{
|
|
243
|
+
{relationsData ? (
|
|
198
244
|
<Fragment>
|
|
199
245
|
<div>
|
|
200
246
|
<label>View to {options.mode === "show" ? "embed" : "show"}</label>
|
|
@@ -214,7 +260,7 @@ const ViewSettings = () => {
|
|
|
214
260
|
{options.mode !== "filter" && (
|
|
215
261
|
<div>
|
|
216
262
|
<RelationOnDemandPicker
|
|
217
|
-
relations={
|
|
263
|
+
relations={relationsData.layers}
|
|
218
264
|
update={(relPath) => {
|
|
219
265
|
if (relPath.startsWith(".")) {
|
|
220
266
|
setProp((prop) => {
|
|
@@ -232,8 +278,8 @@ const ViewSettings = () => {
|
|
|
232
278
|
<RelationBadges
|
|
233
279
|
view={view}
|
|
234
280
|
relation={safeRelation}
|
|
235
|
-
parentTbl={
|
|
236
|
-
|
|
281
|
+
parentTbl={tableName}
|
|
282
|
+
caches={tableCaches}
|
|
237
283
|
/>
|
|
238
284
|
</div>
|
|
239
285
|
)}
|
|
@@ -293,7 +339,7 @@ const ViewSettings = () => {
|
|
|
293
339
|
)}
|
|
294
340
|
</Fragment>
|
|
295
341
|
)}
|
|
296
|
-
{
|
|
342
|
+
{
|
|
297
343
|
<Fragment>
|
|
298
344
|
<label>
|
|
299
345
|
Extra state Formula
|
|
@@ -311,7 +357,7 @@ const ViewSettings = () => {
|
|
|
311
357
|
</small>
|
|
312
358
|
) : null}
|
|
313
359
|
</Fragment>
|
|
314
|
-
|
|
360
|
+
}
|
|
315
361
|
{view ? (
|
|
316
362
|
<a
|
|
317
363
|
className="d-block mt-2"
|
|
@@ -18,14 +18,19 @@ import {
|
|
|
18
18
|
setAPropGen,
|
|
19
19
|
FormulaTooltip,
|
|
20
20
|
HelpTopicLink,
|
|
21
|
-
prepCacheAndFinder,
|
|
22
21
|
initialRelation,
|
|
23
|
-
|
|
22
|
+
buildLayers,
|
|
24
23
|
} from "./utils";
|
|
25
24
|
|
|
26
25
|
import { RelationBadges } from "./RelationBadges";
|
|
27
26
|
import { RelationOnDemandPicker } from "./RelationOnDemandPicker";
|
|
28
27
|
|
|
28
|
+
import {
|
|
29
|
+
RelationsFinder,
|
|
30
|
+
Relation,
|
|
31
|
+
buildTableCaches,
|
|
32
|
+
} from "@saltcorn/common-code";
|
|
33
|
+
|
|
29
34
|
export /**
|
|
30
35
|
* @param {object} props
|
|
31
36
|
* @param {string} props.name
|
|
@@ -127,10 +132,18 @@ const ViewLinkSettings = () => {
|
|
|
127
132
|
link_target_blank,
|
|
128
133
|
} = node;
|
|
129
134
|
const options = React.useContext(optionsCtx);
|
|
130
|
-
const {
|
|
131
|
-
|
|
135
|
+
const {
|
|
136
|
+
tables,
|
|
137
|
+
views,
|
|
138
|
+
tableName,
|
|
139
|
+
excluded_subview_templates,
|
|
140
|
+
max_relations_layer_depth,
|
|
141
|
+
} = options;
|
|
142
|
+
const finder = useMemo(
|
|
143
|
+
() => new RelationsFinder(tables, views, max_relations_layer_depth),
|
|
132
144
|
[undefined]
|
|
133
145
|
);
|
|
146
|
+
const tableCaches = useMemo(() => buildTableCaches(tables), [undefined]);
|
|
134
147
|
const { relationsCache, setRelationsCache } = React.useContext(relationsCtx);
|
|
135
148
|
let errorString = false;
|
|
136
149
|
try {
|
|
@@ -147,50 +160,82 @@ const ViewLinkSettings = () => {
|
|
|
147
160
|
const safeViewName = use_view_name?.includes(".")
|
|
148
161
|
? use_view_name.split(".")[0]
|
|
149
162
|
: use_view_name;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
163
|
+
const subView = views.find((view) => view.name === safeViewName);
|
|
164
|
+
const hasTableId = subView?.table_id !== undefined;
|
|
165
|
+
if (!(relationsCache[tableName] && relationsCache[tableName][safeViewName])) {
|
|
166
|
+
const relations = finder.findRelations(
|
|
167
|
+
tableName,
|
|
168
|
+
safeViewName,
|
|
169
|
+
excluded_subview_templates
|
|
170
|
+
);
|
|
171
|
+
const layers = buildLayers(
|
|
172
|
+
relations,
|
|
173
|
+
tableName,
|
|
174
|
+
tableCaches.tableNameCache
|
|
175
|
+
);
|
|
176
|
+
relationsCache[tableName] = relationsCache[tableName] || {};
|
|
177
|
+
relationsCache[tableName][safeViewName] = { relations, layers };
|
|
178
|
+
setRelationsCache({ ...relationsCache });
|
|
179
|
+
}
|
|
180
|
+
const [relationsData, setRelationsData] = React.useState(
|
|
158
181
|
relationsCache[options.tableName][safeViewName]
|
|
159
182
|
);
|
|
160
|
-
let safeRelation =
|
|
161
|
-
if (
|
|
162
|
-
|
|
183
|
+
let safeRelation = null;
|
|
184
|
+
if (relation) {
|
|
185
|
+
const subView = views.find((view) => view.name === safeViewName);
|
|
186
|
+
const subTbl = tables.find((tbl) => tbl.id === subView.table_id);
|
|
187
|
+
safeRelation = new Relation(
|
|
188
|
+
relation,
|
|
189
|
+
subTbl ? subTbl.name : "",
|
|
190
|
+
subView.display_type
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
if (
|
|
194
|
+
!safeRelation &&
|
|
195
|
+
!hasLegacyRelation &&
|
|
196
|
+
relationsData?.relations.length > 0
|
|
197
|
+
) {
|
|
198
|
+
safeRelation = initialRelation(relationsData.relations);
|
|
163
199
|
setProp((prop) => {
|
|
164
|
-
prop.relation = safeRelation;
|
|
200
|
+
prop.relation = safeRelation.relationString;
|
|
165
201
|
});
|
|
166
202
|
}
|
|
167
203
|
const set_view_name = (e) => {
|
|
168
204
|
if (e.target) {
|
|
169
205
|
const target_value = e.target.value;
|
|
170
206
|
if (target_value !== use_view_name) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
207
|
+
const newRelations = finder.findRelations(
|
|
208
|
+
tableName,
|
|
209
|
+
target_value,
|
|
210
|
+
excluded_subview_templates
|
|
211
|
+
);
|
|
212
|
+
const layers = buildLayers(
|
|
213
|
+
newRelations,
|
|
214
|
+
tableName,
|
|
215
|
+
tableCaches.tableNameCache
|
|
177
216
|
);
|
|
178
|
-
|
|
179
|
-
|
|
217
|
+
|
|
218
|
+
relationsCache[tableName] = relationsCache[tableName] || {};
|
|
219
|
+
relationsCache[tableName][target_value] = {
|
|
220
|
+
relations: newRelations,
|
|
221
|
+
layers,
|
|
222
|
+
};
|
|
223
|
+
if (newRelations.length > 0) {
|
|
180
224
|
setProp((prop) => {
|
|
181
225
|
prop.name = target_value;
|
|
182
|
-
prop.relation = initialRelation(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
226
|
+
prop.relation = initialRelation(newRelations).relationString;
|
|
227
|
+
});
|
|
228
|
+
setRelationsData({ relations: newRelations, layers });
|
|
229
|
+
} else
|
|
230
|
+
window.notifyAlert({
|
|
231
|
+
type: "warning",
|
|
232
|
+
text: `${target_value} has no relations`,
|
|
186
233
|
});
|
|
187
|
-
setRelations(newRelations);
|
|
188
|
-
}
|
|
189
234
|
}
|
|
190
235
|
}
|
|
191
236
|
};
|
|
192
237
|
const helpContext = { view_name: use_view_name };
|
|
193
|
-
if (
|
|
238
|
+
if (tableName) helpContext.srcTable = tableName;
|
|
194
239
|
return (
|
|
195
240
|
<div>
|
|
196
241
|
<table className="w-100">
|
|
@@ -215,7 +260,7 @@ const ViewLinkSettings = () => {
|
|
|
215
260
|
<tr>
|
|
216
261
|
<td colSpan="2">
|
|
217
262
|
<RelationOnDemandPicker
|
|
218
|
-
relations={
|
|
263
|
+
relations={relationsData.layers}
|
|
219
264
|
update={(relPath) => {
|
|
220
265
|
if (relPath.startsWith(".")) {
|
|
221
266
|
setProp((prop) => {
|
|
@@ -233,8 +278,8 @@ const ViewLinkSettings = () => {
|
|
|
233
278
|
<RelationBadges
|
|
234
279
|
view={name}
|
|
235
280
|
relation={safeRelation}
|
|
236
|
-
parentTbl={
|
|
237
|
-
|
|
281
|
+
parentTbl={tableName}
|
|
282
|
+
caches={tableCaches}
|
|
238
283
|
/>
|
|
239
284
|
</td>
|
|
240
285
|
</tr>
|
|
@@ -278,6 +323,7 @@ const ViewLinkSettings = () => {
|
|
|
278
323
|
values={node}
|
|
279
324
|
linkFirst={true}
|
|
280
325
|
linkIsBlank={true}
|
|
326
|
+
allowRunOnLoad={false}
|
|
281
327
|
/>
|
|
282
328
|
</tbody>
|
|
283
329
|
</table>
|
|
@@ -18,6 +18,7 @@ import FontIconPicker from "@fonticonpicker/react-fonticonpicker";
|
|
|
18
18
|
import faIcons from "./faicons";
|
|
19
19
|
import { Columns, ntimes } from "./Columns";
|
|
20
20
|
import Tippy from "@tippyjs/react";
|
|
21
|
+
import { RelationType } from "@saltcorn/common-code";
|
|
21
22
|
|
|
22
23
|
export const DynamicFontAwesomeIcon = ({ icon, className }) => {
|
|
23
24
|
if (!icon) return null;
|
|
@@ -445,6 +446,10 @@ const fetchPreview = ({ url, body, options, setPreviews, node_id, isView }) => {
|
|
|
445
446
|
}
|
|
446
447
|
const newHtml = $(".preview-scratchpad").html();
|
|
447
448
|
setPreviews((prevState) => ({ ...prevState, [node_id]: newHtml }));
|
|
449
|
+
})
|
|
450
|
+
.catch((e) => {
|
|
451
|
+
console.log("Unable to fetch the preview:");
|
|
452
|
+
console.log(e);
|
|
448
453
|
});
|
|
449
454
|
};
|
|
450
455
|
|
|
@@ -1245,19 +1250,27 @@ const ButtonOrLinkSettingsRows = ({
|
|
|
1245
1250
|
<option value={addBtnClass("btn-secondary")}>Secondary button</option>
|
|
1246
1251
|
<option value={addBtnClass("btn-success")}>Success button</option>
|
|
1247
1252
|
<option value={addBtnClass("btn-danger")}>Danger button</option>
|
|
1253
|
+
<option value={addBtnClass("btn-warning")}>Warning button</option>
|
|
1254
|
+
<option value={addBtnClass("btn-info")}>Info button</option>
|
|
1248
1255
|
<option value={addBtnClass("btn-outline-primary")}>
|
|
1249
1256
|
Primary outline button
|
|
1250
1257
|
</option>
|
|
1251
1258
|
<option value={addBtnClass("btn-outline-secondary")}>
|
|
1252
1259
|
Secondary outline button
|
|
1253
1260
|
</option>
|
|
1261
|
+
<option value={addBtnClass("btn-outline-warning")}>
|
|
1262
|
+
Warning outline button
|
|
1263
|
+
</option>{" "}
|
|
1264
|
+
<option value={addBtnClass("btn-outline-info")}>
|
|
1265
|
+
Info outline button
|
|
1266
|
+
</option>
|
|
1254
1267
|
<option value={addBtnClass("btn-custom-color")}>
|
|
1255
1268
|
Button custom color
|
|
1256
1269
|
</option>
|
|
1257
1270
|
{!linkFirst ? (
|
|
1258
1271
|
<option value={addBtnClass("btn-link")}>Link</option>
|
|
1259
1272
|
) : null}
|
|
1260
|
-
{!linkFirst ? (
|
|
1273
|
+
{!linkFirst && allowRunOnLoad ? (
|
|
1261
1274
|
<option value="on_page_load">Run on Page Load</option>
|
|
1262
1275
|
) : null}
|
|
1263
1276
|
</select>
|
|
@@ -1439,22 +1452,6 @@ const Tooltip = ({ children }) => {
|
|
|
1439
1452
|
);
|
|
1440
1453
|
};
|
|
1441
1454
|
|
|
1442
|
-
export const buildTableCaches = (allTables) => {
|
|
1443
|
-
const tableIdCache = {};
|
|
1444
|
-
const tableNameCache = {};
|
|
1445
|
-
const fieldCache = {};
|
|
1446
|
-
for (const table of allTables) {
|
|
1447
|
-
tableIdCache[table.id] = table;
|
|
1448
|
-
tableNameCache[table.name] = table;
|
|
1449
|
-
for (const field of table.foreign_keys) {
|
|
1450
|
-
if (!fieldCache[field.reftable_name])
|
|
1451
|
-
fieldCache[field.reftable_name] = [];
|
|
1452
|
-
fieldCache[field.reftable_name].push(field);
|
|
1453
|
-
}
|
|
1454
|
-
}
|
|
1455
|
-
return { tableIdCache, tableNameCache, fieldCache };
|
|
1456
|
-
};
|
|
1457
|
-
|
|
1458
1455
|
export const removeWhitespaces = (str) => {
|
|
1459
1456
|
return str.replace(/\s/g, "X");
|
|
1460
1457
|
};
|
|
@@ -1497,79 +1494,145 @@ export const buildBootstrapOptions = (values) => {
|
|
|
1497
1494
|
));
|
|
1498
1495
|
};
|
|
1499
1496
|
|
|
1500
|
-
export const
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
const caches = buildTableCaches(tables);
|
|
1507
|
-
const finder = new relationHelpers.RelationsFinder(
|
|
1508
|
-
caches,
|
|
1509
|
-
views,
|
|
1510
|
-
max_relations_layer_depth || 6
|
|
1511
|
-
);
|
|
1512
|
-
return { caches, finder };
|
|
1513
|
-
} else return { caches: null, finder: null };
|
|
1497
|
+
export const arrayChunks = (xs, n) => {
|
|
1498
|
+
const arrayOfArrays = [];
|
|
1499
|
+
for (var i = 0; i < bigarray.length; i += n) {
|
|
1500
|
+
arrayOfArrays.push(bigarray.slice(i, i + n));
|
|
1501
|
+
}
|
|
1502
|
+
return arrayOfArrays;
|
|
1514
1503
|
};
|
|
1515
1504
|
|
|
1516
1505
|
/**
|
|
1517
|
-
* @param {string[]}
|
|
1506
|
+
* @param {string[]} relations
|
|
1518
1507
|
* @param {string} sourceTbl name of the topview table
|
|
1519
1508
|
* @returns either a same table relation, a parent relation, a child relation, or the first relation
|
|
1520
1509
|
*/
|
|
1521
|
-
export const initialRelation = (
|
|
1510
|
+
export const initialRelation = (relations) => {
|
|
1522
1511
|
let sameTblRel = null;
|
|
1523
1512
|
let parentRel = null;
|
|
1524
1513
|
let childRel = null;
|
|
1525
|
-
for (const
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
const lastToken = tokens[tokens.length - 1];
|
|
1538
|
-
if (
|
|
1539
|
-
lastToken.indexOf("$") > 0 &&
|
|
1540
|
-
(!childRel || childRel.split(".").length > tokens.length)
|
|
1541
|
-
)
|
|
1542
|
-
childRel = path;
|
|
1543
|
-
}
|
|
1514
|
+
for (const relation of relations) {
|
|
1515
|
+
switch (relation.type) {
|
|
1516
|
+
case RelationType.OWN:
|
|
1517
|
+
sameTblRel = relation;
|
|
1518
|
+
break;
|
|
1519
|
+
case RelationType.PARENT_SHOW:
|
|
1520
|
+
parentRel = relation;
|
|
1521
|
+
break;
|
|
1522
|
+
case RelationType.CHILD_LIST:
|
|
1523
|
+
case RelationType.ONE_TO_ONE_SHOW:
|
|
1524
|
+
childRel = relation;
|
|
1525
|
+
break;
|
|
1544
1526
|
}
|
|
1545
1527
|
}
|
|
1546
|
-
return sameTblRel || parentRel || childRel ||
|
|
1528
|
+
return sameTblRel || parentRel || childRel || relations[0];
|
|
1547
1529
|
};
|
|
1548
1530
|
|
|
1549
1531
|
/**
|
|
1550
|
-
*
|
|
1551
|
-
*
|
|
1552
|
-
* @param
|
|
1553
|
-
* @
|
|
1554
|
-
* @param {any} options builder options
|
|
1555
|
-
* @param {RelationsFinder} finder
|
|
1556
|
-
* @param {string} viewname subview name
|
|
1532
|
+
* builder intern path method
|
|
1533
|
+
* @param path
|
|
1534
|
+
* @param tableNameCache
|
|
1535
|
+
* @returns
|
|
1557
1536
|
*/
|
|
1558
|
-
export const
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
)
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1537
|
+
export const buildRelationArray = (path, tableNameCache) => {
|
|
1538
|
+
if (path === ".")
|
|
1539
|
+
return [{ type: "Independent", table: "None (no relation)" }];
|
|
1540
|
+
const tokens = path.split(".");
|
|
1541
|
+
if (tokens.length === 2)
|
|
1542
|
+
return [{ type: "Own", table: `${tokens[1]} (same table)` }];
|
|
1543
|
+
else if (tokens.length >= 3) {
|
|
1544
|
+
const result = [];
|
|
1545
|
+
let currentTbl = tokens[1];
|
|
1546
|
+
for (const relation of tokens.slice(2)) {
|
|
1547
|
+
if (relation.indexOf("$") > 0) {
|
|
1548
|
+
const [inboundTbl, inboundKey] = relation.split("$");
|
|
1549
|
+
result.push({ type: "Inbound", table: inboundTbl, key: inboundKey });
|
|
1550
|
+
currentTbl = inboundTbl;
|
|
1551
|
+
} else {
|
|
1552
|
+
const srcTbl = tableNameCache[currentTbl];
|
|
1553
|
+
const fk = srcTbl.foreign_keys.find((fk) => fk.name === relation);
|
|
1554
|
+
if (fk) {
|
|
1555
|
+
const targetTbl = tableNameCache[fk.reftable_name];
|
|
1556
|
+
result.push({
|
|
1557
|
+
type: "Foreign",
|
|
1558
|
+
table: targetTbl.name,
|
|
1559
|
+
key: relation,
|
|
1560
|
+
});
|
|
1561
|
+
currentTbl = targetTbl.name;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
return result;
|
|
1566
|
+
}
|
|
1567
|
+
};
|
|
1568
|
+
|
|
1569
|
+
export const buildLayers = (relations, tableName, tableNameCache) => {
|
|
1570
|
+
const result = { table: tableName, inboundKeys: [], fkeys: [] };
|
|
1571
|
+
for (const relation of relations) {
|
|
1572
|
+
const relType = relation.type;
|
|
1573
|
+
let currentLevel = result;
|
|
1574
|
+
if (relType === RelationType.INDEPENDENT) {
|
|
1575
|
+
currentLevel.fkeys.push({
|
|
1576
|
+
name: "none (no relation)",
|
|
1577
|
+
table: "",
|
|
1578
|
+
inboundKeys: [],
|
|
1579
|
+
fkeys: [],
|
|
1580
|
+
relPath: relation.relationString,
|
|
1581
|
+
});
|
|
1582
|
+
} else if (relType === RelationType.OWN) {
|
|
1583
|
+
currentLevel.fkeys.push({
|
|
1584
|
+
name: "same table",
|
|
1585
|
+
table: relation.targetTblName,
|
|
1586
|
+
inboundKeys: [],
|
|
1587
|
+
fkeys: [],
|
|
1588
|
+
relPath: relation.relationString,
|
|
1589
|
+
});
|
|
1590
|
+
} else {
|
|
1591
|
+
let currentTbl = relation.sourceTblName;
|
|
1592
|
+
for (const pathElement of relation.path) {
|
|
1593
|
+
if (pathElement.inboundKey) {
|
|
1594
|
+
currentTbl = pathElement.table;
|
|
1595
|
+
const existing = currentLevel.inboundKeys.find(
|
|
1596
|
+
(key) =>
|
|
1597
|
+
key.name === pathElement.inboundKey && key.table === currentTbl
|
|
1598
|
+
);
|
|
1599
|
+
if (existing) {
|
|
1600
|
+
currentLevel = existing;
|
|
1601
|
+
} else {
|
|
1602
|
+
const nextLevel = {
|
|
1603
|
+
name: pathElement.inboundKey,
|
|
1604
|
+
table: currentTbl,
|
|
1605
|
+
inboundKeys: [],
|
|
1606
|
+
fkeys: [],
|
|
1607
|
+
};
|
|
1608
|
+
currentLevel.inboundKeys.push(nextLevel);
|
|
1609
|
+
currentLevel = nextLevel;
|
|
1610
|
+
}
|
|
1611
|
+
} else if (pathElement.fkey) {
|
|
1612
|
+
const tblObj = tableNameCache[currentTbl];
|
|
1613
|
+
const fkey = tblObj.foreign_keys.find(
|
|
1614
|
+
(key) => key.name === pathElement.fkey
|
|
1615
|
+
);
|
|
1616
|
+
currentTbl = fkey.reftable_name;
|
|
1617
|
+
const existing = currentLevel.fkeys.find(
|
|
1618
|
+
(key) => key.name === pathElement.fkey
|
|
1619
|
+
);
|
|
1620
|
+
if (existing) {
|
|
1621
|
+
currentLevel = existing;
|
|
1622
|
+
} else {
|
|
1623
|
+
const nextLevel = {
|
|
1624
|
+
name: pathElement.fkey,
|
|
1625
|
+
table: currentTbl,
|
|
1626
|
+
inboundKeys: [],
|
|
1627
|
+
fkeys: [],
|
|
1628
|
+
};
|
|
1629
|
+
currentLevel.fkeys.push(nextLevel);
|
|
1630
|
+
currentLevel = nextLevel;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
currentLevel.relPath = relation.relationString;
|
|
1574
1636
|
}
|
|
1637
|
+
return result;
|
|
1575
1638
|
};
|