@saltcorn/server 0.9.4-beta.8 → 0.9.4
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/app.js +16 -1
- package/auth/admin.js +19 -3
- package/auth/routes.js +16 -4
- package/auth/testhelp.js +17 -1
- package/load_plugins.js +8 -2
- package/locales/en.json +29 -1
- package/markup/admin.js +22 -18
- package/package.json +10 -9
- package/public/dayjslocales/af.js +1 -0
- package/public/dayjslocales/am.js +1 -0
- package/public/dayjslocales/ar-dz.js +1 -0
- package/public/dayjslocales/ar-iq.js +1 -0
- package/public/dayjslocales/ar-kw.js +1 -0
- package/public/dayjslocales/ar-ly.js +1 -0
- package/public/dayjslocales/ar-ma.js +1 -0
- package/public/dayjslocales/ar-sa.js +1 -0
- package/public/dayjslocales/ar-tn.js +1 -0
- package/public/dayjslocales/ar.js +1 -0
- package/public/dayjslocales/az.js +1 -0
- package/public/dayjslocales/be.js +1 -0
- package/public/dayjslocales/bg.js +1 -0
- package/public/dayjslocales/bi.js +1 -0
- package/public/dayjslocales/bm.js +1 -0
- package/public/dayjslocales/bn-bd.js +1 -0
- package/public/dayjslocales/bn.js +1 -0
- package/public/dayjslocales/bo.js +1 -0
- package/public/dayjslocales/br.js +1 -0
- package/public/dayjslocales/bs.js +1 -0
- package/public/dayjslocales/ca.js +1 -0
- package/public/dayjslocales/cs.js +1 -0
- package/public/dayjslocales/cv.js +1 -0
- package/public/dayjslocales/cy.js +1 -0
- package/public/dayjslocales/da.js +1 -0
- package/public/dayjslocales/de-at.js +1 -0
- package/public/dayjslocales/de-ch.js +1 -0
- package/public/dayjslocales/de.js +1 -0
- package/public/dayjslocales/dv.js +1 -0
- package/public/dayjslocales/el.js +1 -0
- package/public/dayjslocales/en-au.js +1 -0
- package/public/dayjslocales/en-ca.js +1 -0
- package/public/dayjslocales/en-gb.js +1 -0
- package/public/dayjslocales/en-ie.js +1 -0
- package/public/dayjslocales/en-il.js +1 -0
- package/public/dayjslocales/en-in.js +1 -0
- package/public/dayjslocales/en-nz.js +1 -0
- package/public/dayjslocales/en-sg.js +1 -0
- package/public/dayjslocales/en-tt.js +1 -0
- package/public/dayjslocales/en.js +1 -0
- package/public/dayjslocales/eo.js +1 -0
- package/public/dayjslocales/es-do.js +1 -0
- package/public/dayjslocales/es-mx.js +1 -0
- package/public/dayjslocales/es-pr.js +1 -0
- package/public/dayjslocales/es-us.js +1 -0
- package/public/dayjslocales/es.js +1 -0
- package/public/dayjslocales/et.js +1 -0
- package/public/dayjslocales/eu.js +1 -0
- package/public/dayjslocales/fa.js +1 -0
- package/public/dayjslocales/fi.js +1 -0
- package/public/dayjslocales/fo.js +1 -0
- package/public/dayjslocales/fr-ca.js +1 -0
- package/public/dayjslocales/fr-ch.js +1 -0
- package/public/dayjslocales/fr.js +1 -0
- package/public/dayjslocales/fy.js +1 -0
- package/public/dayjslocales/ga.js +1 -0
- package/public/dayjslocales/gd.js +1 -0
- package/public/dayjslocales/gl.js +1 -0
- package/public/dayjslocales/gom-latn.js +1 -0
- package/public/dayjslocales/gu.js +1 -0
- package/public/dayjslocales/he.js +1 -0
- package/public/dayjslocales/hi.js +1 -0
- package/public/dayjslocales/hr.js +1 -0
- package/public/dayjslocales/ht.js +1 -0
- package/public/dayjslocales/hu.js +1 -0
- package/public/dayjslocales/hy-am.js +1 -0
- package/public/dayjslocales/id.js +1 -0
- package/public/dayjslocales/is.js +1 -0
- package/public/dayjslocales/it-ch.js +1 -0
- package/public/dayjslocales/it.js +1 -0
- package/public/dayjslocales/ja.js +1 -0
- package/public/dayjslocales/jv.js +1 -0
- package/public/dayjslocales/ka.js +1 -0
- package/public/dayjslocales/kk.js +1 -0
- package/public/dayjslocales/km.js +1 -0
- package/public/dayjslocales/kn.js +1 -0
- package/public/dayjslocales/ko.js +1 -0
- package/public/dayjslocales/ku.js +1 -0
- package/public/dayjslocales/ky.js +1 -0
- package/public/dayjslocales/lb.js +1 -0
- package/public/dayjslocales/lo.js +1 -0
- package/public/dayjslocales/lt.js +1 -0
- package/public/dayjslocales/lv.js +1 -0
- package/public/dayjslocales/me.js +1 -0
- package/public/dayjslocales/mi.js +1 -0
- package/public/dayjslocales/mk.js +1 -0
- package/public/dayjslocales/ml.js +1 -0
- package/public/dayjslocales/mn.js +1 -0
- package/public/dayjslocales/mr.js +1 -0
- package/public/dayjslocales/ms-my.js +1 -0
- package/public/dayjslocales/ms.js +1 -0
- package/public/dayjslocales/mt.js +1 -0
- package/public/dayjslocales/my.js +1 -0
- package/public/dayjslocales/nb.js +1 -0
- package/public/dayjslocales/ne.js +1 -0
- package/public/dayjslocales/nl-be.js +1 -0
- package/public/dayjslocales/nl.js +1 -0
- package/public/dayjslocales/nn.js +1 -0
- package/public/dayjslocales/oc-lnc.js +1 -0
- package/public/dayjslocales/pa-in.js +1 -0
- package/public/dayjslocales/pl.js +1 -0
- package/public/dayjslocales/pt-br.js +1 -0
- package/public/dayjslocales/pt.js +1 -0
- package/public/dayjslocales/rn.js +1 -0
- package/public/dayjslocales/ro.js +1 -0
- package/public/dayjslocales/ru.js +1 -0
- package/public/dayjslocales/rw.js +1 -0
- package/public/dayjslocales/sd.js +1 -0
- package/public/dayjslocales/se.js +1 -0
- package/public/dayjslocales/si.js +1 -0
- package/public/dayjslocales/sk.js +1 -0
- package/public/dayjslocales/sl.js +1 -0
- package/public/dayjslocales/sq.js +1 -0
- package/public/dayjslocales/sr-cyrl.js +1 -0
- package/public/dayjslocales/sr.js +1 -0
- package/public/dayjslocales/ss.js +1 -0
- package/public/dayjslocales/sv-fi.js +1 -0
- package/public/dayjslocales/sv.js +1 -0
- package/public/dayjslocales/sw.js +1 -0
- package/public/dayjslocales/ta.js +1 -0
- package/public/dayjslocales/te.js +1 -0
- package/public/dayjslocales/tet.js +1 -0
- package/public/dayjslocales/tg.js +1 -0
- package/public/dayjslocales/th.js +1 -0
- package/public/dayjslocales/tk.js +1 -0
- package/public/dayjslocales/tl-ph.js +1 -0
- package/public/dayjslocales/tlh.js +1 -0
- package/public/dayjslocales/tr.js +1 -0
- package/public/dayjslocales/tzl.js +1 -0
- package/public/dayjslocales/tzm-latn.js +1 -0
- package/public/dayjslocales/tzm.js +1 -0
- package/public/dayjslocales/ug-cn.js +1 -0
- package/public/dayjslocales/uk.js +1 -0
- package/public/dayjslocales/ur.js +1 -0
- package/public/dayjslocales/uz-latn.js +1 -0
- package/public/dayjslocales/uz.js +1 -0
- package/public/dayjslocales/vi.js +1 -0
- package/public/dayjslocales/x-pseudo.js +1 -0
- package/public/dayjslocales/yo.js +1 -0
- package/public/dayjslocales/zh-cn.js +1 -0
- package/public/dayjslocales/zh-hk.js +1 -0
- package/public/dayjslocales/zh-tw.js +1 -0
- package/public/dayjslocales/zh.js +1 -0
- package/public/gridedit.js +2 -2
- package/public/log_viewer_utils.js +156 -0
- package/public/saltcorn-builder.css +43 -2
- package/public/saltcorn-common.js +39 -29
- package/public/saltcorn.js +29 -8
- package/public/tabulator_bootstrap5.min.css +1 -0
- package/restart_watcher.js +1 -0
- package/routes/actions.js +175 -18
- package/routes/admin.js +83 -9
- package/routes/common_lists.js +344 -152
- package/routes/fields.js +18 -3
- package/routes/homepage.js +2 -1
- package/routes/page.js +30 -13
- package/routes/page_groupedit.js +104 -83
- package/routes/pageedit.js +23 -7
- package/routes/tables.js +51 -5
- package/routes/tag_entries.js +18 -5
- package/routes/tags.js +65 -12
- package/routes/utils.js +23 -2
- package/routes/view.js +12 -1
- package/routes/viewedit.js +46 -3
- package/serve.js +177 -10
- package/tests/admin.test.js +17 -11
- package/tests/api.test.js +27 -0
- package/tests/fields.test.js +132 -5
- package/tests/help.test.js +37 -0
- package/tests/page_group.test.js +1 -0
- package/tests/plugins.test.js +0 -12
- package/tests/table.test.js +1 -5
- package/tests/view.test.js +127 -15
- package/tests/viewedit.test.js +52 -8
- package/wrapper.js +9 -2
- package/public/relation_helpers.js +0 -351
package/wrapper.js
CHANGED
|
@@ -185,18 +185,25 @@ const get_headers = (req, version_tag, description, extras = []) => {
|
|
|
185
185
|
},
|
|
186
186
|
]
|
|
187
187
|
: [];
|
|
188
|
+
const locale = req.getLocale();
|
|
188
189
|
const stdHeaders = [
|
|
189
190
|
{
|
|
190
191
|
headerTag: `<script>var _sc_loglevel = ${
|
|
191
192
|
state.logLevel
|
|
192
|
-
}, _sc_globalCsrf = "${req.csrfToken()}", _sc_version_tag = "${version_tag}"
|
|
193
|
+
}, _sc_globalCsrf = "${req.csrfToken()}", _sc_version_tag = "${version_tag}"${
|
|
194
|
+
locale ? `, _sc_locale = "${locale}"` : ""
|
|
195
|
+
};</script>`,
|
|
193
196
|
},
|
|
194
197
|
{ css: `/static_assets/${version_tag}/saltcorn.css` },
|
|
195
198
|
{ script: `/static_assets/${version_tag}/saltcorn-common.js` },
|
|
196
199
|
{ script: `/static_assets/${version_tag}/saltcorn.js` },
|
|
197
200
|
{ script: `/static_assets/${version_tag}/dayjs.min.js` },
|
|
198
|
-
{ script: `/static_assets/${version_tag}/relation_helpers.js` },
|
|
199
201
|
];
|
|
202
|
+
if (locale !== "en") {
|
|
203
|
+
stdHeaders.push({
|
|
204
|
+
script: `/static_assets/${version_tag}/dayjslocales/${locale}.js`,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
200
207
|
let from_cfg = [];
|
|
201
208
|
if (state.getConfig("page_custom_css", ""))
|
|
202
209
|
from_cfg.push({ style: state.getConfig("page_custom_css", "") });
|
|
@@ -1,351 +0,0 @@
|
|
|
1
|
-
var relationHelpers = (() => {
|
|
2
|
-
// internal helper to build an object, structured for the picker
|
|
3
|
-
const buildLayers = (path, pathArr, result) => {
|
|
4
|
-
let currentLevel = result;
|
|
5
|
-
for (const relation of pathArr) {
|
|
6
|
-
if (relation.type === "Inbound") {
|
|
7
|
-
const existing = currentLevel.inboundKeys.find(
|
|
8
|
-
(key) => key.name === relation.key && key.table === relation.table
|
|
9
|
-
);
|
|
10
|
-
if (existing) {
|
|
11
|
-
currentLevel = existing;
|
|
12
|
-
} else {
|
|
13
|
-
const nextLevel = {
|
|
14
|
-
name: relation.key,
|
|
15
|
-
table: relation.table,
|
|
16
|
-
inboundKeys: [],
|
|
17
|
-
fkeys: [],
|
|
18
|
-
};
|
|
19
|
-
currentLevel.inboundKeys.push(nextLevel);
|
|
20
|
-
currentLevel = nextLevel;
|
|
21
|
-
}
|
|
22
|
-
} else if (relation.type === "Foreign") {
|
|
23
|
-
const existing = currentLevel.fkeys.find(
|
|
24
|
-
(key) => key.name === relation.key
|
|
25
|
-
);
|
|
26
|
-
if (existing) {
|
|
27
|
-
currentLevel = existing;
|
|
28
|
-
} else {
|
|
29
|
-
const nextLevel = {
|
|
30
|
-
name: relation.key,
|
|
31
|
-
table: relation.table,
|
|
32
|
-
inboundKeys: [],
|
|
33
|
-
fkeys: [],
|
|
34
|
-
};
|
|
35
|
-
currentLevel.fkeys.push(nextLevel);
|
|
36
|
-
currentLevel = nextLevel;
|
|
37
|
-
}
|
|
38
|
-
} else if (relation.type === "Independent") {
|
|
39
|
-
currentLevel.fkeys.push({
|
|
40
|
-
name: "None (no relation)",
|
|
41
|
-
table: relation.table,
|
|
42
|
-
inboundKeys: [],
|
|
43
|
-
fkeys: [],
|
|
44
|
-
relPath: path,
|
|
45
|
-
});
|
|
46
|
-
} else if (relation.type === "Own") {
|
|
47
|
-
currentLevel.fkeys.push({
|
|
48
|
-
name: "Same table",
|
|
49
|
-
table: "",
|
|
50
|
-
inboundKeys: [],
|
|
51
|
-
fkeys: [],
|
|
52
|
-
relPath: path,
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
currentLevel.relPath = path;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* build an array of relation objects from a path string
|
|
61
|
-
* '.' stands for no relation
|
|
62
|
-
* '.table' stands for same table
|
|
63
|
-
* @param {*} path relation path string separated by '.', the first token is the source table
|
|
64
|
-
* @param {*} tableNameCache an object with table name as key and table object as value
|
|
65
|
-
* @returns
|
|
66
|
-
*/
|
|
67
|
-
const parseRelationPath = (path, tableNameCache) => {
|
|
68
|
-
if (path === ".")
|
|
69
|
-
return [{ type: "Independent", table: "None (no relation)" }];
|
|
70
|
-
const tokens = path.split(".");
|
|
71
|
-
if (tokens.length === 2)
|
|
72
|
-
return [{ type: "Own", table: `${tokens[1]} (same table)` }];
|
|
73
|
-
else if (tokens.length >= 3) {
|
|
74
|
-
const result = [];
|
|
75
|
-
let currentTbl = tokens[1];
|
|
76
|
-
for (const relation of tokens.slice(2)) {
|
|
77
|
-
if (relation.indexOf("$") > 0) {
|
|
78
|
-
const [inboundTbl, inboundKey] = relation.split("$");
|
|
79
|
-
result.push({ type: "Inbound", table: inboundTbl, key: inboundKey });
|
|
80
|
-
currentTbl = inboundTbl;
|
|
81
|
-
} else {
|
|
82
|
-
const srcTbl = tableNameCache[currentTbl];
|
|
83
|
-
const fk = srcTbl.foreign_keys.find((fk) => fk.name === relation);
|
|
84
|
-
if (fk) {
|
|
85
|
-
const targetTbl = tableNameCache[fk.reftable_name];
|
|
86
|
-
result.push({
|
|
87
|
-
type: "Foreign",
|
|
88
|
-
table: targetTbl.name,
|
|
89
|
-
key: relation,
|
|
90
|
-
});
|
|
91
|
-
currentTbl = targetTbl.name;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return result;
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* build an array of relation objects from a legacy relation
|
|
101
|
-
* @param {string} type relation type (ChildList, Independent, Own, OneToOneShow, ParentShow)
|
|
102
|
-
* @param {string} rest rest of the legaccy relation
|
|
103
|
-
* @param {string} parentTbl source table
|
|
104
|
-
* @returns
|
|
105
|
-
*/
|
|
106
|
-
const parseLegacyRelation = (type, rest, parentTbl) => {
|
|
107
|
-
switch (type) {
|
|
108
|
-
case "ChildList": {
|
|
109
|
-
const path = rest ? rest.split(".") : [];
|
|
110
|
-
if (path.length === 3) {
|
|
111
|
-
const [viewName, table, key] = path;
|
|
112
|
-
return [
|
|
113
|
-
{
|
|
114
|
-
type: "Inbound",
|
|
115
|
-
table,
|
|
116
|
-
key,
|
|
117
|
-
},
|
|
118
|
-
];
|
|
119
|
-
} else if (path.length === 5) {
|
|
120
|
-
const [viewName, thrTbl, thrTblFkey, fromTbl, fromTblFkey] = path;
|
|
121
|
-
return [
|
|
122
|
-
{
|
|
123
|
-
type: "Inbound",
|
|
124
|
-
table: thrTbl,
|
|
125
|
-
key: thrTblFkey,
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
type: "Inbound",
|
|
129
|
-
table: fromTbl,
|
|
130
|
-
key: fromTblFkey,
|
|
131
|
-
},
|
|
132
|
-
];
|
|
133
|
-
}
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
case "Independent": {
|
|
137
|
-
return [{ type: "Independent", table: "None (no relation)" }];
|
|
138
|
-
}
|
|
139
|
-
case "Own": {
|
|
140
|
-
return [{ type: "Own", table: `${parentTbl} (same table)` }];
|
|
141
|
-
}
|
|
142
|
-
case "OneToOneShow": {
|
|
143
|
-
const tokens = rest ? rest.split(".") : [];
|
|
144
|
-
if (tokens.length !== 3) break;
|
|
145
|
-
const [viewname, relatedTbl, fkey] = tokens;
|
|
146
|
-
return [{ type: "Inbound", table: relatedTbl, key: fkey }];
|
|
147
|
-
}
|
|
148
|
-
case "ParentShow": {
|
|
149
|
-
const tokens = rest ? rest.split(".") : [];
|
|
150
|
-
if (tokens.length !== 3) break;
|
|
151
|
-
const [viewname, parentTbl, fkey] = tokens;
|
|
152
|
-
return [{ type: "Foreign", table: parentTbl, key: fkey }];
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return [];
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
const ViewDisplayType = {
|
|
159
|
-
ROW_REQUIRED: "ROW_REQUIRED",
|
|
160
|
-
NO_ROW_LIMIT: "NO_ROW_LIMIT",
|
|
161
|
-
INVALID: "INVALID",
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* prepare the relations finder
|
|
166
|
-
* @param {object} tablesCache
|
|
167
|
-
* @param {object} allViews
|
|
168
|
-
* @param {number} maxDepth
|
|
169
|
-
*/
|
|
170
|
-
const RelationsFinder = function (tablesCache, allViews, maxDepth) {
|
|
171
|
-
this.maxDepth = +maxDepth;
|
|
172
|
-
if (isNaN(this.maxDepth)) {
|
|
173
|
-
console.log(`maxDepth '${maxDepth}' is not a number, set to 6`);
|
|
174
|
-
this.maxDepth = 6;
|
|
175
|
-
}
|
|
176
|
-
this.allViews = allViews;
|
|
177
|
-
const { tableIdCache, tableNameCache, fieldCache } = tablesCache;
|
|
178
|
-
this.tableIdCache = tableIdCache;
|
|
179
|
-
this.tableNameCache = tableNameCache;
|
|
180
|
-
this.fieldCache = fieldCache;
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* find relations between a source table and a subview
|
|
185
|
-
* @param {string} sourceTblName
|
|
186
|
-
* @param {string} subView
|
|
187
|
-
* @param {string[]} excluded
|
|
188
|
-
* @returns {object} {paths: string[], layers: object}
|
|
189
|
-
*/
|
|
190
|
-
RelationsFinder.prototype.findRelations = function (
|
|
191
|
-
sourceTblName,
|
|
192
|
-
subView,
|
|
193
|
-
excluded
|
|
194
|
-
) {
|
|
195
|
-
let paths = [];
|
|
196
|
-
const layers = { table: sourceTblName, inboundKeys: [], fkeys: [] };
|
|
197
|
-
try {
|
|
198
|
-
const view = this.allViews.find((v) => v.name === subView);
|
|
199
|
-
if (!view) throw new Error(`The view ${subView} does not exist`);
|
|
200
|
-
if (excluded?.find((e) => e === view.viewtemplate)) {
|
|
201
|
-
console.log(`view ${subView} is excluded`);
|
|
202
|
-
return { paths, layers };
|
|
203
|
-
}
|
|
204
|
-
switch (view.display_type) {
|
|
205
|
-
case ViewDisplayType.ROW_REQUIRED:
|
|
206
|
-
paths = this.singleRelationPaths(sourceTblName, subView, excluded);
|
|
207
|
-
break;
|
|
208
|
-
case ViewDisplayType.NO_ROW_LIMIT:
|
|
209
|
-
paths = this.multiRelationPaths(sourceTblName, subView, excluded);
|
|
210
|
-
break;
|
|
211
|
-
default:
|
|
212
|
-
throw new Error(
|
|
213
|
-
`view ${subView}: The displayType (${view.display_type}) is not valid`
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
for (const path of paths)
|
|
217
|
-
buildLayers(path, parseRelationPath(path, this.tableNameCache), layers);
|
|
218
|
-
} catch (error) {
|
|
219
|
-
console.log(error);
|
|
220
|
-
} finally {
|
|
221
|
-
return { paths, layers };
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* find relations between a source table and a subview with single row display (e.g. show)
|
|
227
|
-
* @param {string} sourceTblName
|
|
228
|
-
* @param {string} subView
|
|
229
|
-
* @param {string[]} excluded
|
|
230
|
-
* @returns
|
|
231
|
-
*/
|
|
232
|
-
RelationsFinder.prototype.singleRelationPaths = function (
|
|
233
|
-
sourceTblName,
|
|
234
|
-
subView,
|
|
235
|
-
excluded
|
|
236
|
-
) {
|
|
237
|
-
const result = [];
|
|
238
|
-
const subViewObj = this.allViews.find((v) => v.name === subView);
|
|
239
|
-
if (!subViewObj) throw new Error(`The view ${subView} does not exist`);
|
|
240
|
-
if (excluded?.find((e) => e === subViewObj.viewtemplate)) {
|
|
241
|
-
console.log(`view ${subView} is excluded`);
|
|
242
|
-
return result;
|
|
243
|
-
}
|
|
244
|
-
const sourceTbl = this.tableNameCache[sourceTblName];
|
|
245
|
-
if (!sourceTbl)
|
|
246
|
-
throw new Error(`The table ${sourceTblName} does not exist`);
|
|
247
|
-
// 1. parent relations
|
|
248
|
-
const parentRelations = sourceTbl.foreign_keys;
|
|
249
|
-
if (sourceTbl.id === subViewObj.table_id) result.push(`.${sourceTblName}`);
|
|
250
|
-
for (const relation of parentRelations) {
|
|
251
|
-
const targetTbl = this.tableNameCache[relation.reftable_name];
|
|
252
|
-
if (!targetTbl)
|
|
253
|
-
throw new Error(`The table ${relation.reftable_name} does not exist`);
|
|
254
|
-
if (targetTbl.id === subViewObj.table_id)
|
|
255
|
-
result.push(`.${sourceTblName}.${relation.name}`);
|
|
256
|
-
}
|
|
257
|
-
// 2. OneToOneShow
|
|
258
|
-
const uniqueFksToSrc = (this.fieldCache[sourceTblName] || []).filter(
|
|
259
|
-
(f) => f.is_unique
|
|
260
|
-
);
|
|
261
|
-
for (const relation of uniqueFksToSrc) {
|
|
262
|
-
const targetTbl = this.tableIdCache[relation.table_id];
|
|
263
|
-
if (!targetTbl)
|
|
264
|
-
throw new Error(`The table ${relation.table_id} does not exist`);
|
|
265
|
-
if (targetTbl.id === subViewObj.table_id)
|
|
266
|
-
result.push(`.${sourceTblName}.${targetTbl.name}$${relation.name}`);
|
|
267
|
-
}
|
|
268
|
-
// 3. inbound_self_relations
|
|
269
|
-
const srcFks = sourceTbl.foreign_keys;
|
|
270
|
-
for (const fkToSrc of uniqueFksToSrc) {
|
|
271
|
-
const refTable = this.tableIdCache[fkToSrc.table_id];
|
|
272
|
-
if (!refTable)
|
|
273
|
-
throw new Error(`The table ${fkToSrc.table_id} does not exist`);
|
|
274
|
-
const fromSrcToRef = srcFks.filter(
|
|
275
|
-
(field) => field.reftable_name === refTable.name
|
|
276
|
-
);
|
|
277
|
-
for (const toRef of fromSrcToRef) {
|
|
278
|
-
if (fkToSrc.reftable_name === sourceTblName)
|
|
279
|
-
result.push(`.${sourceTblName}.${toRef.name}.${fkToSrc.name}`);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return result;
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* find relations between a source table and a subview with multiple rows display (e.g. list)
|
|
287
|
-
* @param {string} sourceTblName
|
|
288
|
-
* @param {string} subView
|
|
289
|
-
* @param {string[]} excluded
|
|
290
|
-
* @returns
|
|
291
|
-
*/
|
|
292
|
-
RelationsFinder.prototype.multiRelationPaths = function (
|
|
293
|
-
sourceTblName,
|
|
294
|
-
subView,
|
|
295
|
-
excluded
|
|
296
|
-
) {
|
|
297
|
-
const result = ["."]; // none no relation
|
|
298
|
-
const subViewObj = this.allViews.find((v) => v.name === subView);
|
|
299
|
-
if (!subViewObj) throw new Error(`The view ${subView} does not exist`);
|
|
300
|
-
if (excluded?.find((e) => e === subViewObj.viewtemplate)) {
|
|
301
|
-
console.log(`view ${subView} is excluded`);
|
|
302
|
-
return result;
|
|
303
|
-
}
|
|
304
|
-
const sourceTbl = this.tableNameCache[sourceTblName];
|
|
305
|
-
if (!sourceTbl)
|
|
306
|
-
throw new Error(`The table ${sourceTblName} does not exist`);
|
|
307
|
-
if (sourceTbl.id === subViewObj.table_id) result.push(`.${sourceTblName}`);
|
|
308
|
-
const searcher = (current, path, level, visited) => {
|
|
309
|
-
if (level > this.maxDepth) return;
|
|
310
|
-
const visitedFkCopy = new Set(visited);
|
|
311
|
-
for (const fk of current.foreign_keys) {
|
|
312
|
-
if (visitedFkCopy.has(fk.id)) continue;
|
|
313
|
-
visitedFkCopy.add(fk.id);
|
|
314
|
-
const target = this.tableNameCache[fk.reftable_name];
|
|
315
|
-
if (!target)
|
|
316
|
-
throw new Error(`The table ${fk.reftable_name} does not exist`);
|
|
317
|
-
const newPath = `${path}.${fk.name}`;
|
|
318
|
-
if (target.id === subViewObj.table_id) result.push(newPath);
|
|
319
|
-
searcher(target, newPath, level + 1, visitedFkCopy);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
const visitedInboundCopy = new Set(visited);
|
|
323
|
-
for (const inbound of this.fieldCache[current.name] || []) {
|
|
324
|
-
if (visitedInboundCopy.has(inbound.id)) continue;
|
|
325
|
-
visitedInboundCopy.add(inbound.id);
|
|
326
|
-
const target = this.tableIdCache[inbound.table_id];
|
|
327
|
-
if (!target)
|
|
328
|
-
throw new Error(`The table ${inbound.table_id} does not exist`);
|
|
329
|
-
const newPath = `${path}.${target.name}$${inbound.name}`;
|
|
330
|
-
if (target.id === subViewObj.table_id) result.push(newPath);
|
|
331
|
-
searcher(target, newPath, level + 1, visitedInboundCopy);
|
|
332
|
-
}
|
|
333
|
-
};
|
|
334
|
-
const path = `.${sourceTblName}`;
|
|
335
|
-
const visited = new Set();
|
|
336
|
-
searcher(sourceTbl, path, 0, visited);
|
|
337
|
-
return result;
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
return {
|
|
341
|
-
RelationsFinder: RelationsFinder,
|
|
342
|
-
ViewDisplayType: ViewDisplayType,
|
|
343
|
-
parseRelationPath: parseRelationPath,
|
|
344
|
-
parseLegacyRelation: parseLegacyRelation,
|
|
345
|
-
};
|
|
346
|
-
})();
|
|
347
|
-
|
|
348
|
-
// make the module available for jest with react
|
|
349
|
-
if (typeof process !== "undefined" && process.env?.NODE_ENV === "test") {
|
|
350
|
-
module.exports = relationHelpers;
|
|
351
|
-
}
|