@saltcorn/builder 0.9.2 → 0.9.3-beta.0
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/babel.config.js +6 -0
- package/dist/builder_bundle.js +10 -10
- package/package.json +10 -2
- package/src/components/Toolbox.js +4 -10
- package/src/components/elements/RelationBadges.js +14 -11
- package/src/components/elements/RelationOnDemandPicker.js +212 -0
- package/src/components/elements/View.js +82 -49
- package/src/components/elements/ViewLink.js +47 -24
- package/src/components/elements/utils.js +35 -84
- package/tests/relations_finder.test.js +275 -0
- package/tests/test_data.js +163 -0
- package/src/components/elements/RelationPicker.js +0 -273
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @subcategory components / elements
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import React, {
|
|
7
|
+
import React, { useMemo } from "react";
|
|
8
8
|
import { useNode } from "@craftjs/core";
|
|
9
9
|
import optionsCtx from "../context";
|
|
10
10
|
import {
|
|
@@ -16,10 +16,11 @@ import {
|
|
|
16
16
|
setAPropGen,
|
|
17
17
|
FormulaTooltip,
|
|
18
18
|
HelpTopicLink,
|
|
19
|
+
prepCacheAndFinder,
|
|
19
20
|
} from "./utils";
|
|
20
21
|
|
|
21
|
-
import { RelationPicker } from "./RelationPicker";
|
|
22
22
|
import { RelationBadges } from "./RelationBadges";
|
|
23
|
+
import { RelationOnDemandPicker } from "./RelationOnDemandPicker";
|
|
23
24
|
|
|
24
25
|
export /**
|
|
25
26
|
* @param {object} props
|
|
@@ -43,7 +44,6 @@ export /**
|
|
|
43
44
|
const ViewLink = ({
|
|
44
45
|
name,
|
|
45
46
|
block,
|
|
46
|
-
view_name,
|
|
47
47
|
minRole,
|
|
48
48
|
link_style,
|
|
49
49
|
link_size,
|
|
@@ -61,8 +61,7 @@ const ViewLink = ({
|
|
|
61
61
|
} = useNode((node) => ({ selected: node.events.selected }));
|
|
62
62
|
const names = name.split(":");
|
|
63
63
|
|
|
64
|
-
const displabel =
|
|
65
|
-
label || view_name || (names.length > 1 ? names[1] : names[0]);
|
|
64
|
+
const displabel = label || (names.length > 1 ? names[1] : names[0]);
|
|
66
65
|
return (
|
|
67
66
|
<span
|
|
68
67
|
className={`${textStyle} ${inModal ? "btn btn-secondary btn-sm" : ""} ${
|
|
@@ -86,7 +85,7 @@ const ViewLink = ({
|
|
|
86
85
|
};
|
|
87
86
|
|
|
88
87
|
export /**
|
|
89
|
-
* @returns
|
|
88
|
+
* @returns
|
|
90
89
|
* @category saltcorn-builder
|
|
91
90
|
* @subcategory components
|
|
92
91
|
* @namespace
|
|
@@ -109,7 +108,6 @@ const ViewLinkSettings = () => {
|
|
|
109
108
|
link_bordercol: node.data.props.link_bordercol,
|
|
110
109
|
link_textcol: node.data.props.link_textcol,
|
|
111
110
|
extra_state_fml: node.data.props.extra_state_fml,
|
|
112
|
-
view_name: node.data.props.view_name,
|
|
113
111
|
}));
|
|
114
112
|
const {
|
|
115
113
|
actions: { setProp },
|
|
@@ -122,10 +120,13 @@ const ViewLinkSettings = () => {
|
|
|
122
120
|
inModal,
|
|
123
121
|
textStyle,
|
|
124
122
|
extra_state_fml,
|
|
125
|
-
view_name,
|
|
126
123
|
link_target_blank,
|
|
127
124
|
} = node;
|
|
128
|
-
const options = useContext(optionsCtx);
|
|
125
|
+
const options = React.useContext(optionsCtx);
|
|
126
|
+
const { caches, finder } = useMemo(
|
|
127
|
+
() => prepCacheAndFinder(options),
|
|
128
|
+
[undefined]
|
|
129
|
+
);
|
|
129
130
|
let errorString = false;
|
|
130
131
|
try {
|
|
131
132
|
Function("return " + extra_state_fml);
|
|
@@ -135,18 +136,42 @@ const ViewLinkSettings = () => {
|
|
|
135
136
|
const setAProp = setAPropGen(setProp);
|
|
136
137
|
//legacy values
|
|
137
138
|
const use_view_name =
|
|
138
|
-
|
|
139
|
-
(name
|
|
140
|
-
|
|
139
|
+
name &&
|
|
140
|
+
((names) => (names.length > 1 ? names[1] : names[0]))(name.split(":"));
|
|
141
|
+
const hasLegacyRelation = name && name.includes(":");
|
|
142
|
+
const safeViewName = use_view_name?.includes(".")
|
|
143
|
+
? use_view_name.split(".")[0]
|
|
144
|
+
: use_view_name;
|
|
145
|
+
const [relations, setRelations] = React.useState(
|
|
146
|
+
finder.findRelations(
|
|
147
|
+
options.tableName,
|
|
148
|
+
safeViewName,
|
|
149
|
+
options.excluded_subview_templates
|
|
150
|
+
)
|
|
151
|
+
);
|
|
152
|
+
let safeRelation = relation;
|
|
153
|
+
if (!safeRelation && !hasLegacyRelation && relations?.paths.length > 0) {
|
|
154
|
+
safeRelation = relations.paths[0];
|
|
155
|
+
setProp((prop) => {
|
|
156
|
+
prop.relation = safeRelation;
|
|
157
|
+
});
|
|
158
|
+
}
|
|
141
159
|
const set_view_name = (e) => {
|
|
142
160
|
if (e.target) {
|
|
143
161
|
const target_value = e.target.value;
|
|
144
|
-
setProp((prop) => (prop.view_name = target_value));
|
|
145
162
|
if (target_value !== use_view_name) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
163
|
+
const newRelations = finder.findRelations(
|
|
164
|
+
options.tableName,
|
|
165
|
+
target_value,
|
|
166
|
+
options.excluded_subview_templates
|
|
167
|
+
);
|
|
168
|
+
if (newRelations.paths.length > 0) {
|
|
169
|
+
setProp((prop) => {
|
|
170
|
+
prop.name = target_value;
|
|
171
|
+
prop.relation = newRelations.paths[0];
|
|
172
|
+
});
|
|
173
|
+
setRelations(newRelations);
|
|
174
|
+
}
|
|
150
175
|
}
|
|
151
176
|
}
|
|
152
177
|
};
|
|
@@ -165,7 +190,7 @@ const ViewLinkSettings = () => {
|
|
|
165
190
|
onChange={set_view_name}
|
|
166
191
|
onBlur={set_view_name}
|
|
167
192
|
>
|
|
168
|
-
{options.
|
|
193
|
+
{options.views.map((f, ix) => (
|
|
169
194
|
<option key={ix} value={f.name}>
|
|
170
195
|
{f.label}
|
|
171
196
|
</option>
|
|
@@ -175,9 +200,8 @@ const ViewLinkSettings = () => {
|
|
|
175
200
|
</tr>
|
|
176
201
|
<tr>
|
|
177
202
|
<td colSpan="2">
|
|
178
|
-
<
|
|
179
|
-
|
|
180
|
-
viewname={use_view_name}
|
|
203
|
+
<RelationOnDemandPicker
|
|
204
|
+
relations={relations.layers}
|
|
181
205
|
update={(relPath) => {
|
|
182
206
|
if (relPath.startsWith(".")) {
|
|
183
207
|
setProp((prop) => {
|
|
@@ -194,9 +218,9 @@ const ViewLinkSettings = () => {
|
|
|
194
218
|
/>
|
|
195
219
|
<RelationBadges
|
|
196
220
|
view={name}
|
|
197
|
-
relation={
|
|
221
|
+
relation={safeRelation}
|
|
198
222
|
parentTbl={options.tableName}
|
|
199
|
-
|
|
223
|
+
tableNameCache={caches.tableNameCache}
|
|
200
224
|
/>
|
|
201
225
|
</td>
|
|
202
226
|
</tr>
|
|
@@ -308,7 +332,6 @@ ViewLink.craft = {
|
|
|
308
332
|
{ name: "inModal", segment_name: "in_modal", column_name: "in_modal" },
|
|
309
333
|
"minRole",
|
|
310
334
|
"link_style",
|
|
311
|
-
"view_name",
|
|
312
335
|
"link_icon",
|
|
313
336
|
"link_size",
|
|
314
337
|
"link_target_blank",
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @subcategory components / elements
|
|
5
5
|
*/
|
|
6
6
|
/* globals $, _sc_globalCsrf*/
|
|
7
|
-
import React, { Fragment,
|
|
7
|
+
import React, { Fragment, useState } from "react";
|
|
8
8
|
import optionsCtx from "../context";
|
|
9
9
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
10
10
|
import {
|
|
@@ -80,7 +80,7 @@ export const BlockOrInlineSetting = ({ block, inline, textStyle, setProp }) =>
|
|
|
80
80
|
);
|
|
81
81
|
|
|
82
82
|
export const HelpTopicLink = ({ topic, ...context }) => {
|
|
83
|
-
const { mode } = useContext(optionsCtx);
|
|
83
|
+
const { mode } = React.useContext(optionsCtx);
|
|
84
84
|
let qs = "";
|
|
85
85
|
Object.keys(context).forEach((k) => {
|
|
86
86
|
qs += `${encodeURIComponent(k)}=${encodeURIComponent(context[k])}&`;
|
|
@@ -95,7 +95,7 @@ export const HelpTopicLink = ({ topic, ...context }) => {
|
|
|
95
95
|
};
|
|
96
96
|
|
|
97
97
|
export const FormulaTooltip = () => {
|
|
98
|
-
const { fields } = useContext(optionsCtx);
|
|
98
|
+
const { fields } = React.useContext(optionsCtx);
|
|
99
99
|
return (
|
|
100
100
|
<Tooltip>
|
|
101
101
|
<div>
|
|
@@ -137,7 +137,7 @@ export /**
|
|
|
137
137
|
* @subcategory components / elements / utils
|
|
138
138
|
*/
|
|
139
139
|
const OrFormula = ({ setProp, isFormula, node, nodekey, children }) => {
|
|
140
|
-
const { mode } = useContext(optionsCtx);
|
|
140
|
+
const { mode } = React.useContext(optionsCtx);
|
|
141
141
|
|
|
142
142
|
/**
|
|
143
143
|
* @returns {void}
|
|
@@ -225,7 +225,7 @@ export /**
|
|
|
225
225
|
* @subcategory components / elements / utils
|
|
226
226
|
*/
|
|
227
227
|
const MinRoleSetting = ({ minRole, setProp }) => {
|
|
228
|
-
const options = useContext(optionsCtx);
|
|
228
|
+
const options = React.useContext(optionsCtx);
|
|
229
229
|
return (
|
|
230
230
|
<div>
|
|
231
231
|
<label>Minimum Role</label>
|
|
@@ -259,7 +259,7 @@ export /**
|
|
|
259
259
|
* @subcategory components / elements / utils
|
|
260
260
|
*/
|
|
261
261
|
const MinRoleSettingRow = ({ minRole, setProp }) => {
|
|
262
|
-
const options = useContext(optionsCtx);
|
|
262
|
+
const options = React.useContext(optionsCtx);
|
|
263
263
|
return (
|
|
264
264
|
<tr>
|
|
265
265
|
<td>
|
|
@@ -743,7 +743,7 @@ const ConfigField = ({
|
|
|
743
743
|
* @param {object} v
|
|
744
744
|
* @returns {void}
|
|
745
745
|
*/
|
|
746
|
-
const options = useContext(optionsCtx);
|
|
746
|
+
const options = React.useContext(optionsCtx);
|
|
747
747
|
|
|
748
748
|
const myOnChange = (v) => {
|
|
749
749
|
setProp((prop) => {
|
|
@@ -1412,85 +1412,20 @@ const Tooltip = ({ children }) => {
|
|
|
1412
1412
|
);
|
|
1413
1413
|
};
|
|
1414
1414
|
|
|
1415
|
-
const
|
|
1416
|
-
const
|
|
1417
|
-
|
|
1418
|
-
};
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
if (relation.indexOf("$") > 0) {
|
|
1427
|
-
const [inboundTbl, inboundKey] = relation.split("$");
|
|
1428
|
-
result.push({ type: "Inbound", table: inboundTbl, key: inboundKey });
|
|
1429
|
-
currentTbl = inboundTbl;
|
|
1430
|
-
} else {
|
|
1431
|
-
const targetTbl = getFkTarget(relation, fk_options[currentTbl]);
|
|
1432
|
-
if (!targetTbl) {
|
|
1433
|
-
console.log(`The foreign key '${relation}' is invalid`);
|
|
1434
|
-
return [];
|
|
1435
|
-
}
|
|
1436
|
-
result.push({ type: "Foreign", table: targetTbl, key: relation });
|
|
1437
|
-
currentTbl = targetTbl;
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
return result;
|
|
1442
|
-
};
|
|
1443
|
-
|
|
1444
|
-
export const parseLegacyRelation = (type, rest, parentTbl) => {
|
|
1445
|
-
switch (type) {
|
|
1446
|
-
case "ChildList": {
|
|
1447
|
-
const path = rest ? rest.split(".") : [];
|
|
1448
|
-
if (path.length === 3) {
|
|
1449
|
-
const [viewName, table, key] = path;
|
|
1450
|
-
return [
|
|
1451
|
-
{
|
|
1452
|
-
type: "Inbound",
|
|
1453
|
-
table,
|
|
1454
|
-
key,
|
|
1455
|
-
},
|
|
1456
|
-
];
|
|
1457
|
-
} else if (path.length === 5) {
|
|
1458
|
-
const [viewName, thrTbl, thrTblFkey, fromTbl, fromTblFkey] = path;
|
|
1459
|
-
return [
|
|
1460
|
-
{
|
|
1461
|
-
type: "Inbound",
|
|
1462
|
-
table: thrTbl,
|
|
1463
|
-
key: thrTblFkey,
|
|
1464
|
-
},
|
|
1465
|
-
{
|
|
1466
|
-
type: "Inbound",
|
|
1467
|
-
table: fromTbl,
|
|
1468
|
-
key: fromTblFkey,
|
|
1469
|
-
},
|
|
1470
|
-
];
|
|
1471
|
-
}
|
|
1472
|
-
break;
|
|
1473
|
-
}
|
|
1474
|
-
case "Independent": {
|
|
1475
|
-
return [{ type: "Independent", table: "None (no relation)" }];
|
|
1476
|
-
}
|
|
1477
|
-
case "Own": {
|
|
1478
|
-
return [{ type: "Own", table: `${parentTbl} (same table)` }];
|
|
1479
|
-
}
|
|
1480
|
-
case "OneToOneShow": {
|
|
1481
|
-
const tokens = rest ? rest.split(".") : [];
|
|
1482
|
-
if (tokens.length !== 3) break;
|
|
1483
|
-
const [viewname, relatedTbl, fkey] = tokens;
|
|
1484
|
-
return [{ type: "Inbound", table: relatedTbl, key: fkey }];
|
|
1485
|
-
}
|
|
1486
|
-
case "ParentShow": {
|
|
1487
|
-
const tokens = rest ? rest.split(".") : [];
|
|
1488
|
-
if (tokens.length !== 3) break;
|
|
1489
|
-
const [viewname, parentTbl, fkey] = tokens;
|
|
1490
|
-
return [{ type: "Foreign", table: parentTbl, key: fkey }];
|
|
1415
|
+
export const buildTableCaches = (allTables) => {
|
|
1416
|
+
const tableIdCache = {};
|
|
1417
|
+
const tableNameCache = {};
|
|
1418
|
+
const fieldCache = {};
|
|
1419
|
+
for (const table of allTables) {
|
|
1420
|
+
tableIdCache[table.id] = table;
|
|
1421
|
+
tableNameCache[table.name] = table;
|
|
1422
|
+
for (const field of table.foreign_keys) {
|
|
1423
|
+
if (!fieldCache[field.reftable_name])
|
|
1424
|
+
fieldCache[field.reftable_name] = [];
|
|
1425
|
+
fieldCache[field.reftable_name].push(field);
|
|
1491
1426
|
}
|
|
1492
1427
|
}
|
|
1493
|
-
return
|
|
1428
|
+
return { tableIdCache, tableNameCache, fieldCache };
|
|
1494
1429
|
};
|
|
1495
1430
|
|
|
1496
1431
|
export const removeWhitespaces = (str) => {
|
|
@@ -1534,3 +1469,19 @@ export const buildBootstrapOptions = (values) => {
|
|
|
1534
1469
|
</option>
|
|
1535
1470
|
));
|
|
1536
1471
|
};
|
|
1472
|
+
|
|
1473
|
+
export const prepCacheAndFinder = ({
|
|
1474
|
+
tables,
|
|
1475
|
+
views,
|
|
1476
|
+
max_relations_layer_depth,
|
|
1477
|
+
}) => {
|
|
1478
|
+
if (tables && views) {
|
|
1479
|
+
const caches = buildTableCaches(tables);
|
|
1480
|
+
const finder = new relationHelpers.RelationsFinder(
|
|
1481
|
+
caches,
|
|
1482
|
+
views,
|
|
1483
|
+
max_relations_layer_depth || 6
|
|
1484
|
+
);
|
|
1485
|
+
return { caches, finder };
|
|
1486
|
+
} else return { caches: null, finder: null };
|
|
1487
|
+
};
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import renderer from "react-test-renderer";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
fixturesData,
|
|
6
|
+
withAnotherUserField,
|
|
7
|
+
withSecondTopicField,
|
|
8
|
+
withMultipleInbounds,
|
|
9
|
+
withKeyFromLayerTwo,
|
|
10
|
+
withKeyFromLayerThree,
|
|
11
|
+
withSimplePostTopicrelation,
|
|
12
|
+
} from "./test_data";
|
|
13
|
+
import { ViewSettings } from "../src/components/elements/View";
|
|
14
|
+
import { ViewLinkSettings } from "../src/components/elements/ViewLink";
|
|
15
|
+
|
|
16
|
+
jest.mock("@craftjs/core", () => ({
|
|
17
|
+
useNode: jest.fn(),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
const doTest = (
|
|
21
|
+
tables,
|
|
22
|
+
views,
|
|
23
|
+
tableName,
|
|
24
|
+
viewName,
|
|
25
|
+
expected,
|
|
26
|
+
excludedTemplates,
|
|
27
|
+
view,
|
|
28
|
+
relation
|
|
29
|
+
) => {
|
|
30
|
+
// mock craftjs
|
|
31
|
+
const useNodeMock = jest.fn();
|
|
32
|
+
useNodeMock.mockReturnValue({
|
|
33
|
+
actions: {
|
|
34
|
+
setProp: (fn) => {},
|
|
35
|
+
},
|
|
36
|
+
relation: relation,
|
|
37
|
+
view: view || viewName,
|
|
38
|
+
name: view || viewName,
|
|
39
|
+
});
|
|
40
|
+
require("@craftjs/core").useNode.mockImplementation(useNodeMock);
|
|
41
|
+
// mock react context
|
|
42
|
+
const useContextMock = (React.useContext = jest.fn());
|
|
43
|
+
useContextMock.mockReturnValue({
|
|
44
|
+
tables: tables,
|
|
45
|
+
views: views,
|
|
46
|
+
tableName: tableName,
|
|
47
|
+
roles: [],
|
|
48
|
+
excluded_subview_templates: excludedTemplates,
|
|
49
|
+
});
|
|
50
|
+
// spy on useState and extract the relations (first call)
|
|
51
|
+
const spy = jest.spyOn(React, "useState");
|
|
52
|
+
renderer.create(<ViewSettings></ViewSettings>);
|
|
53
|
+
expect(spy).toBeCalled();
|
|
54
|
+
const vCallArgs = spy.mock.calls[0];
|
|
55
|
+
expect(vCallArgs[0].paths).toBeDefined();
|
|
56
|
+
expect(vCallArgs[0].paths).toHaveLength(expected.length);
|
|
57
|
+
expect(vCallArgs[0].paths).toEqual(expect.arrayContaining(expected));
|
|
58
|
+
|
|
59
|
+
renderer.create(<ViewLinkSettings></ViewLinkSettings>);
|
|
60
|
+
expect(spy.mock.calls).toHaveLength(4);
|
|
61
|
+
const vLinkcallArgs = spy.mock.calls[2];
|
|
62
|
+
expect(vLinkcallArgs[0].paths).toBeDefined();
|
|
63
|
+
expect(vLinkcallArgs[0].paths).toHaveLength(expected.length);
|
|
64
|
+
expect(vLinkcallArgs[0].paths).toEqual(expect.arrayContaining(expected));
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
describe("relations tests", () => {
|
|
68
|
+
beforeAll(() => {
|
|
69
|
+
// inject relationHelpers (normally it's a script tag)
|
|
70
|
+
global.relationHelpers = {
|
|
71
|
+
...require("../../server/public/relation_helpers.js"),
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
jest.restoreAllMocks();
|
|
76
|
+
});
|
|
77
|
+
describe("single relations", () => {
|
|
78
|
+
it("parent relations", async () => {
|
|
79
|
+
const { tables, views } = fixturesData();
|
|
80
|
+
const expected = [".fan_club.artist"];
|
|
81
|
+
doTest(tables, views, "fan_club", "show_artist", expected);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("one to one relations", async () => {
|
|
85
|
+
const { tables, views } = fixturesData();
|
|
86
|
+
const expected = [".covers.albums$cover"];
|
|
87
|
+
doTest(tables, views, "covers", "show_album", expected);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("employee department relation", async () => {
|
|
91
|
+
const { tables, views } = fixturesData();
|
|
92
|
+
const expected = [".employee", ".employee.department.manager"];
|
|
93
|
+
doTest(tables, views, "employee", "show_manager", expected);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("multi relations", () => {
|
|
98
|
+
describe("inbound relations", () => {
|
|
99
|
+
const expectedOne = [
|
|
100
|
+
".",
|
|
101
|
+
".users.user_interested_in_topic$user.topic.blog_in_topic$topic",
|
|
102
|
+
".users.user_interested_in_topic$user.topic.inbound_inbound$topic.bp_inbound.post.blog_in_topic$post",
|
|
103
|
+
".users.messages$user.room.participants$room.user.user_interested_in_topic$user.topic.blog_in_topic$topic",
|
|
104
|
+
];
|
|
105
|
+
it("single keys to source and rel table", async () => {
|
|
106
|
+
const { tables, views } = fixturesData();
|
|
107
|
+
doTest(tables, views, "users", "blog_in_topic_feed", expectedOne);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const expectedTwo = [
|
|
111
|
+
...expectedOne,
|
|
112
|
+
".users.user_interested_in_topic$another_user.topic.blog_in_topic$topic",
|
|
113
|
+
".users.user_interested_in_topic$another_user.topic.inbound_inbound$topic.bp_inbound.post.blog_in_topic$post",
|
|
114
|
+
".users.messages$user.room.participants$room.user.user_interested_in_topic$another_user.topic.blog_in_topic$topic",
|
|
115
|
+
];
|
|
116
|
+
it("multiple keys to source and single key to rel table", async () => {
|
|
117
|
+
const { tables, views } = withAnotherUserField();
|
|
118
|
+
doTest(tables, views, "users", "blog_in_topic_feed", expectedTwo);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const expectedThree = [
|
|
122
|
+
...expectedTwo,
|
|
123
|
+
".users.user_interested_in_topic$user.topic.blog_in_topic$second_topic",
|
|
124
|
+
".users.user_interested_in_topic$another_user.topic.blog_in_topic$second_topic",
|
|
125
|
+
".users.messages$user.room.participants$room.user.user_interested_in_topic$user.topic.blog_in_topic$second_topic",
|
|
126
|
+
".users.messages$user.room.participants$room.user.user_interested_in_topic$another_user.topic.blog_in_topic$second_topic",
|
|
127
|
+
];
|
|
128
|
+
it("multiple keys to source and rel table", async () => {
|
|
129
|
+
const { tables, views } = withSecondTopicField();
|
|
130
|
+
doTest(tables, views, "users", "blog_in_topic_feed", expectedThree);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const expectedFour = [
|
|
134
|
+
...expectedThree,
|
|
135
|
+
".users.user_interested_in_topic$user.another_user.second_inbound$user.topic.blog_in_topic$topic",
|
|
136
|
+
".users.user_interested_in_topic$user.another_user.second_inbound$user.topic.blog_in_topic$second_topic",
|
|
137
|
+
".users.second_inbound$user.topic.blog_in_topic$topic",
|
|
138
|
+
".users.second_inbound$user.topic.blog_in_topic$second_topic",
|
|
139
|
+
".users.second_inbound$user.topic.inbound_inbound$topic.bp_inbound.post.blog_in_topic$post",
|
|
140
|
+
".users.messages$user.room.participants$room.user.second_inbound$user.topic.blog_in_topic$topic",
|
|
141
|
+
".users.messages$user.room.participants$room.user.second_inbound$user.topic.blog_in_topic$second_topic",
|
|
142
|
+
];
|
|
143
|
+
it("multiple inbound tables", async () => {
|
|
144
|
+
const { tables, views } = withMultipleInbounds();
|
|
145
|
+
doTest(tables, views, "users", "blog_in_topic_feed", expectedFour);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const expectedFive = [
|
|
149
|
+
...expectedFour,
|
|
150
|
+
".users.user_interested_in_topic$user.topic.inbound_inbound$topic.post_from_layer_two.blog_in_topic$post",
|
|
151
|
+
".users.user_interested_in_topic$another_user.topic.inbound_inbound$topic.post_from_layer_two.blog_in_topic$post",
|
|
152
|
+
".users.second_inbound$user.topic.inbound_inbound$topic.post_from_layer_two.blog_in_topic$post",
|
|
153
|
+
".users.user_interested_in_topic$user.topic.blog_in_topic$topic.post.inbound_inbound$post_from_layer_two.topic.blog_in_topic$second_topic",
|
|
154
|
+
".users.user_interested_in_topic$user.another_user.second_inbound$user.topic.inbound_inbound$topic.post_from_layer_two.blog_in_topic$post",
|
|
155
|
+
".users.user_interested_in_topic$another_user.topic.blog_in_topic$topic.post.inbound_inbound$post_from_layer_two.topic.blog_in_topic$second_topic",
|
|
156
|
+
".users.second_inbound$user.topic.blog_in_topic$topic.post.inbound_inbound$post_from_layer_two.topic.blog_in_topic$second_topic",
|
|
157
|
+
];
|
|
158
|
+
it("key to source from layer two", async () => {
|
|
159
|
+
const { tables, views } = withKeyFromLayerTwo();
|
|
160
|
+
doTest(tables, views, "users", "blog_in_topic_feed", expectedFive);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const expectedSix = [
|
|
164
|
+
...expectedFive,
|
|
165
|
+
".users.user_interested_in_topic$user.topic.inbound_level_three$topic.inbound_level_two.bp_inbound.post.blog_in_topic$post",
|
|
166
|
+
".users.user_interested_in_topic$user.topic.inbound_level_three$topic.inbound_level_two.post_from_layer_two.blog_in_topic$post",
|
|
167
|
+
".users.user_interested_in_topic$another_user.topic.inbound_level_three$topic.inbound_level_two.bp_inbound.post.blog_in_topic$post",
|
|
168
|
+
".users.user_interested_in_topic$another_user.topic.inbound_level_three$topic.inbound_level_two.post_from_layer_two.blog_in_topic$post",
|
|
169
|
+
".users.second_inbound$user.topic.inbound_level_three$topic.inbound_level_two.bp_inbound.post.blog_in_topic$post",
|
|
170
|
+
".users.second_inbound$user.topic.inbound_level_three$topic.inbound_level_two.post_from_layer_two.blog_in_topic$post",
|
|
171
|
+
];
|
|
172
|
+
it("three levels inbound", async () => {
|
|
173
|
+
const { tables, views } = withKeyFromLayerThree();
|
|
174
|
+
doTest(tables, views, "users", "blog_in_topic_feed", expectedSix);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("simple post topic relation", async () => {
|
|
178
|
+
const expected = [
|
|
179
|
+
".",
|
|
180
|
+
".users.favsimpletopic.simple_posts$topic",
|
|
181
|
+
".users.favsimpletopic.simple_post_inbound$topic.post",
|
|
182
|
+
".users.messages$user.room.participants$room.user.favsimpletopic.simple_posts$topic",
|
|
183
|
+
".users.messages$user.room.participants$room.user.favsimpletopic.simple_post_inbound$topic.post",
|
|
184
|
+
];
|
|
185
|
+
const { tables, views } = withSimplePostTopicrelation();
|
|
186
|
+
doTest(tables, views, "users", "simple_posts_list", expected);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe("many to many relations", () => {
|
|
191
|
+
it("artist_plays_on_album", async () => {
|
|
192
|
+
const { tables, views } = fixturesData();
|
|
193
|
+
const expected = [".", ".artists.artist_plays_on_album$artist.album"];
|
|
194
|
+
doTest(tables, views, "artists", "albums_feed", expected);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("tracks on album", async () => {
|
|
198
|
+
const { tables, views } = fixturesData();
|
|
199
|
+
const expected = [
|
|
200
|
+
".",
|
|
201
|
+
".artists.artist_plays_on_album$artist.album.tracks_on_album$album",
|
|
202
|
+
];
|
|
203
|
+
doTest(tables, views, "artists", "tracks_on_album_feed", expected);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("show pressing_job with embedded fan club feed", async () => {
|
|
207
|
+
const { tables, views } = fixturesData();
|
|
208
|
+
const expected = [
|
|
209
|
+
".",
|
|
210
|
+
".pressing_job.album.artist_plays_on_album$album.artist.fan_club$artist",
|
|
211
|
+
];
|
|
212
|
+
doTest(tables, views, "pressing_job", "fan_club_feed", expected);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe("excluded viewtemplates", () => {
|
|
217
|
+
it("excluded viewtemplates", async () => {
|
|
218
|
+
const { tables, views } = fixturesData();
|
|
219
|
+
const expected = [];
|
|
220
|
+
const excluded = ["Room"];
|
|
221
|
+
doTest(tables, views, "participants", "rooms_view", expected, excluded);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
describe("open legacy relations", () => {
|
|
226
|
+
it("ChildList", async () => {
|
|
227
|
+
const { tables, views } = fixturesData();
|
|
228
|
+
const expected = [".", ".books.discusses_books$book"];
|
|
229
|
+
doTest(
|
|
230
|
+
tables,
|
|
231
|
+
views,
|
|
232
|
+
"books",
|
|
233
|
+
"disc_books_list",
|
|
234
|
+
expected,
|
|
235
|
+
[],
|
|
236
|
+
"ChildList:disc_books_list.discusses_books.book",
|
|
237
|
+
undefined
|
|
238
|
+
);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("Independent", async () => {
|
|
242
|
+
const { tables, views } = fixturesData();
|
|
243
|
+
const expected = [
|
|
244
|
+
".",
|
|
245
|
+
".blog_posts.blog_in_topic$post.topic.inbound_inbound$topic.bp_inbound.post",
|
|
246
|
+
];
|
|
247
|
+
doTest(
|
|
248
|
+
tables,
|
|
249
|
+
views,
|
|
250
|
+
"blog_posts",
|
|
251
|
+
"blog_posts_feed",
|
|
252
|
+
expected,
|
|
253
|
+
[],
|
|
254
|
+
"Independent:blog_posts_feed",
|
|
255
|
+
undefined
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it("Own", async () => {
|
|
260
|
+
const { tables, views } = fixturesData();
|
|
261
|
+
const expected = [".books"];
|
|
262
|
+
doTest(
|
|
263
|
+
tables,
|
|
264
|
+
views,
|
|
265
|
+
"books",
|
|
266
|
+
"authorshow",
|
|
267
|
+
expected,
|
|
268
|
+
[],
|
|
269
|
+
"Own:authorshow",
|
|
270
|
+
undefined
|
|
271
|
+
);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
});
|