@saltcorn/server 1.0.0-beta.8 → 1.0.0-rc.1
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 +4 -7
- package/auth/admin.js +1 -0
- package/help/Aggregation where formula.tmd +11 -0
- package/help/Calculated fields.tmd +28 -0
- package/help/Date format.tmd +55 -0
- package/help/Protected fields.tmd +4 -0
- package/help/Snapshots.tmd +19 -0
- package/help/Table history.tmd +42 -0
- package/help/index.js +2 -0
- package/load_plugins.js +89 -8
- package/locales/en.json +13 -1
- package/locales/pl.json +407 -85
- package/package.json +11 -11
- package/public/saltcorn-common.js +195 -100
- package/public/saltcorn.css +17 -0
- package/public/saltcorn.js +69 -12
- package/routes/actions.js +18 -24
- package/routes/admin.js +256 -54
- package/routes/eventlog.js +2 -1
- package/routes/fields.js +21 -5
- package/routes/infoarch.js +40 -2
- package/routes/menu.js +3 -0
- package/routes/pageedit.js +7 -1
- package/routes/plugins.js +118 -23
- package/routes/scapi.js +19 -0
- package/routes/search.js +6 -1
- package/routes/sync.js +2 -1
- package/routes/tables.js +14 -4
- package/serve.js +17 -3
- package/tests/fields.test.js +32 -0
- package/tests/plugin_install.test.js +235 -0
- package/tests/plugins.test.js +140 -0
package/routes/plugins.js
CHANGED
|
@@ -49,6 +49,8 @@ const {
|
|
|
49
49
|
input,
|
|
50
50
|
label,
|
|
51
51
|
text,
|
|
52
|
+
script,
|
|
53
|
+
domReady,
|
|
52
54
|
} = require("@saltcorn/markup/tags");
|
|
53
55
|
const { search_bar } = require("@saltcorn/markup/helpers");
|
|
54
56
|
const fs = require("fs");
|
|
@@ -59,6 +61,10 @@ const { sleep, removeNonWordChars } = require("@saltcorn/data/utils");
|
|
|
59
61
|
const { loadAllPlugins } = require("../load_plugins");
|
|
60
62
|
const npmFetch = require("npm-registry-fetch");
|
|
61
63
|
const PluginInstaller = require("@saltcorn/plugins-loader/plugin_installer");
|
|
64
|
+
const {
|
|
65
|
+
supportedVersion,
|
|
66
|
+
isVersionSupported,
|
|
67
|
+
} = require("@saltcorn/plugins-loader/stable_versioning");
|
|
62
68
|
|
|
63
69
|
/**
|
|
64
70
|
* @type {object}
|
|
@@ -177,6 +183,8 @@ const get_store_items = async () => {
|
|
|
177
183
|
has_auth: plugin.has_auth,
|
|
178
184
|
unsafe: plugin.unsafe,
|
|
179
185
|
source: plugin.source,
|
|
186
|
+
ready_for_mobile:
|
|
187
|
+
plugin.ready_for_mobile && plugin.ready_for_mobile(plugin.name),
|
|
180
188
|
}))
|
|
181
189
|
.filter((p) => !p.unsafe || isRoot || tenants_unsafe_plugins);
|
|
182
190
|
const local_logins = installed_plugins
|
|
@@ -190,6 +198,8 @@ const get_store_items = async () => {
|
|
|
190
198
|
github: plugin.source === "github",
|
|
191
199
|
git: plugin.source === "git",
|
|
192
200
|
local: plugin.source === "local",
|
|
201
|
+
ready_for_mobile:
|
|
202
|
+
plugin.ready_for_mobile && plugin.ready_for_mobile(plugin.name),
|
|
193
203
|
}));
|
|
194
204
|
|
|
195
205
|
const pack_items = packs_available.map((pack) => ({
|
|
@@ -274,7 +284,8 @@ const store_item_html = (req) => (item) => ({
|
|
|
274
284
|
item.github && badge("GitHub"),
|
|
275
285
|
item.git && badge("Git"),
|
|
276
286
|
item.local && badge(req.__("Local")),
|
|
277
|
-
item.installed && badge(req.__("Installed"))
|
|
287
|
+
item.installed && badge(req.__("Installed")),
|
|
288
|
+
item.ready_for_mobile && badge(req.__("Mobile"))
|
|
278
289
|
),
|
|
279
290
|
div(item.description || ""),
|
|
280
291
|
item.documentation_link
|
|
@@ -584,7 +595,9 @@ router.get(
|
|
|
584
595
|
error_catcher(async (req, res) => {
|
|
585
596
|
const { name } = req.params;
|
|
586
597
|
const withoutOrg = name.replace(/^@saltcorn\//, "");
|
|
587
|
-
|
|
598
|
+
let plugin = await Plugin.store_by_name(decodeURIComponent(withoutOrg));
|
|
599
|
+
if (!plugin)
|
|
600
|
+
plugin = await Plugin.findOne({ name: decodeURIComponent(name) });
|
|
588
601
|
if (!plugin) {
|
|
589
602
|
getState().log(
|
|
590
603
|
2,
|
|
@@ -603,12 +616,14 @@ router.get(
|
|
|
603
616
|
res.set("Page-Title", req.__("%s versions", text(withoutOrg)));
|
|
604
617
|
const versions = Object.keys(pkgInfo.versions);
|
|
605
618
|
if (versions.length === 0) throw new Error(req.__("No versions found"));
|
|
619
|
+
const tags = pkgInfo["dist-tags"] || {};
|
|
606
620
|
let selected = null;
|
|
607
621
|
if (getState().plugins[plugin.name]) {
|
|
608
622
|
const mod = await load_plugins.requirePlugin(plugin);
|
|
609
623
|
if (mod) selected = mod.version;
|
|
610
624
|
}
|
|
611
625
|
if (!selected) selected = versions[versions.length - 1];
|
|
626
|
+
const scVersion = getState().scVersion;
|
|
612
627
|
return res.send(
|
|
613
628
|
form(
|
|
614
629
|
{
|
|
@@ -618,6 +633,7 @@ router.get(
|
|
|
618
633
|
input({ type: "hidden", name: "_csrf", value: req.csrfToken() }),
|
|
619
634
|
div(
|
|
620
635
|
{ class: "form-group" },
|
|
636
|
+
// version
|
|
621
637
|
label(
|
|
622
638
|
{
|
|
623
639
|
for: "version_select",
|
|
@@ -631,14 +647,49 @@ router.get(
|
|
|
631
647
|
class: "form-control form-select",
|
|
632
648
|
name: "version",
|
|
633
649
|
},
|
|
634
|
-
versions
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
650
|
+
versions
|
|
651
|
+
.filter((v) =>
|
|
652
|
+
isVersionSupported(v, pkgInfo.versions, scVersion)
|
|
653
|
+
)
|
|
654
|
+
.map((version) =>
|
|
655
|
+
option({
|
|
656
|
+
id: `${version}_opt`,
|
|
657
|
+
value: version,
|
|
658
|
+
label: version,
|
|
659
|
+
selected: version === selected,
|
|
660
|
+
})
|
|
661
|
+
)
|
|
662
|
+
),
|
|
663
|
+
// tag
|
|
664
|
+
label(
|
|
665
|
+
{
|
|
666
|
+
for: "tag_select",
|
|
667
|
+
class: "form-label fw-bold mt-2",
|
|
668
|
+
},
|
|
669
|
+
req.__("Tags")
|
|
670
|
+
),
|
|
671
|
+
select(
|
|
672
|
+
{
|
|
673
|
+
id: "tag_select",
|
|
674
|
+
class: "form-control form-select",
|
|
675
|
+
},
|
|
676
|
+
option({
|
|
677
|
+
id: "empty_opt",
|
|
678
|
+
value: "",
|
|
679
|
+
label: req.__("Select tag"),
|
|
680
|
+
selected: true,
|
|
681
|
+
}),
|
|
682
|
+
Object.keys(tags)
|
|
683
|
+
.filter((tag) =>
|
|
684
|
+
isVersionSupported(tags[tag], pkgInfo.versions, scVersion)
|
|
685
|
+
)
|
|
686
|
+
.map((tag) =>
|
|
687
|
+
option({
|
|
688
|
+
id: `${tag}_opt`,
|
|
689
|
+
value: tags[tag],
|
|
690
|
+
label: `${tag} (${tags[tag]})`,
|
|
691
|
+
})
|
|
692
|
+
)
|
|
642
693
|
)
|
|
643
694
|
),
|
|
644
695
|
div(
|
|
@@ -660,7 +711,19 @@ router.get(
|
|
|
660
711
|
req.__("Install")
|
|
661
712
|
)
|
|
662
713
|
)
|
|
663
|
-
)
|
|
714
|
+
) +
|
|
715
|
+
script(
|
|
716
|
+
domReady(`
|
|
717
|
+
document.getElementById('tag_select').onchange = () => {
|
|
718
|
+
const version = document.getElementById('tag_select').value;
|
|
719
|
+
if (version) document.getElementById('version_select').value = version;
|
|
720
|
+
};
|
|
721
|
+
document.getElementById('version_select').onchange = () => {
|
|
722
|
+
const tagSelect = document.getElementById('tag_select');
|
|
723
|
+
tagSelect.value = '';
|
|
724
|
+
};
|
|
725
|
+
`)
|
|
726
|
+
)
|
|
664
727
|
);
|
|
665
728
|
} catch (error) {
|
|
666
729
|
getState().log(
|
|
@@ -1168,9 +1231,22 @@ router.get(
|
|
|
1168
1231
|
const update_permitted =
|
|
1169
1232
|
db.getTenantSchema() === db.connectObj.default_schema &&
|
|
1170
1233
|
plugin_db.source === "npm";
|
|
1171
|
-
|
|
1234
|
+
|
|
1235
|
+
let latest =
|
|
1172
1236
|
update_permitted &&
|
|
1173
1237
|
(await get_latest_npm_version(plugin_db.location, 1000));
|
|
1238
|
+
let engineInfos = await load_plugins.getEngineInfos(plugin_db); // with cache
|
|
1239
|
+
let forceFetch = true;
|
|
1240
|
+
if (latest && !engineInfos[latest]) {
|
|
1241
|
+
engineInfos = await load_plugins.getEngineInfos(plugin_db, forceFetch);
|
|
1242
|
+
forceFetch = false;
|
|
1243
|
+
}
|
|
1244
|
+
if (latest && !isVersionSupported(latest, engineInfos)) {
|
|
1245
|
+
latest = supportedVersion(
|
|
1246
|
+
latest,
|
|
1247
|
+
await load_plugins.getEngineInfos(plugin_db, forceFetch)
|
|
1248
|
+
);
|
|
1249
|
+
}
|
|
1174
1250
|
const can_update = update_permitted && latest && mod.version !== latest;
|
|
1175
1251
|
const can_select_version = update_permitted && plugin_db.source === "npm";
|
|
1176
1252
|
let pkgjson;
|
|
@@ -1316,8 +1392,8 @@ router.get(
|
|
|
1316
1392
|
error_catcher(async (req, res) => {
|
|
1317
1393
|
const schema = db.getTenantSchema();
|
|
1318
1394
|
if (schema === db.connectObj.default_schema) {
|
|
1319
|
-
await upgrade_all_tenants_plugins((p, f) =>
|
|
1320
|
-
load_plugins.loadPlugin(p, f)
|
|
1395
|
+
await upgrade_all_tenants_plugins((p, f, forceFetch) =>
|
|
1396
|
+
load_plugins.loadPlugin(p, f, forceFetch)
|
|
1321
1397
|
);
|
|
1322
1398
|
req.flash(
|
|
1323
1399
|
"success",
|
|
@@ -1328,7 +1404,9 @@ router.get(
|
|
|
1328
1404
|
} else {
|
|
1329
1405
|
const installed_plugins = await Plugin.find({});
|
|
1330
1406
|
for (const plugin of installed_plugins) {
|
|
1331
|
-
await plugin.upgrade_version((p, f) =>
|
|
1407
|
+
await plugin.upgrade_version((p, f, forceFetch) =>
|
|
1408
|
+
load_plugins.loadPlugin(p, f, forceFetch)
|
|
1409
|
+
);
|
|
1332
1410
|
}
|
|
1333
1411
|
req.flash("success", req.__(`Modules up-to-date`));
|
|
1334
1412
|
await restart_tenant(loadAllPlugins);
|
|
@@ -1354,7 +1432,12 @@ router.get(
|
|
|
1354
1432
|
const { name } = req.params;
|
|
1355
1433
|
|
|
1356
1434
|
const plugin = await Plugin.findOne({ name });
|
|
1357
|
-
|
|
1435
|
+
const versions = await load_plugins.getEngineInfos(plugin, true);
|
|
1436
|
+
|
|
1437
|
+
await plugin.upgrade_version(
|
|
1438
|
+
(p, f) => load_plugins.loadPlugin(p, f),
|
|
1439
|
+
supportedVersion("latest", versions, require("../package.json").version)
|
|
1440
|
+
);
|
|
1358
1441
|
req.flash("success", req.__(`Module up-to-date`));
|
|
1359
1442
|
|
|
1360
1443
|
res.redirect(`/plugins/info/${encodeURIComponent(plugin.name)}`);
|
|
@@ -1385,11 +1468,12 @@ router.post(
|
|
|
1385
1468
|
res.redirect(`/plugins`);
|
|
1386
1469
|
} else {
|
|
1387
1470
|
try {
|
|
1388
|
-
await load_plugins.loadAndSaveNewPlugin(
|
|
1471
|
+
const msgs = await load_plugins.loadAndSaveNewPlugin(
|
|
1389
1472
|
plugin,
|
|
1390
1473
|
schema === db.connectObj.default_schema || plugin.source === "github"
|
|
1391
1474
|
);
|
|
1392
1475
|
req.flash("success", req.__(`Module %s installed`, plugin.name));
|
|
1476
|
+
for (const msg of msgs) req.flash("warning", msg);
|
|
1393
1477
|
res.redirect(`/plugins`);
|
|
1394
1478
|
} catch (e) {
|
|
1395
1479
|
req.flash("error", `${e.message}`);
|
|
@@ -1488,12 +1572,23 @@ router.post(
|
|
|
1488
1572
|
res.redirect(`/plugins`);
|
|
1489
1573
|
return;
|
|
1490
1574
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1575
|
+
|
|
1576
|
+
let msgs = null;
|
|
1577
|
+
try {
|
|
1578
|
+
msgs = await load_plugins.loadAndSaveNewPlugin(
|
|
1579
|
+
plugin,
|
|
1580
|
+
forceReInstall,
|
|
1581
|
+
undefined,
|
|
1582
|
+
req.__
|
|
1583
|
+
);
|
|
1584
|
+
} catch (e) {
|
|
1585
|
+
req.flash(
|
|
1586
|
+
"error",
|
|
1587
|
+
e.message || req.__("Error installing module %s", plugin.name)
|
|
1588
|
+
);
|
|
1589
|
+
res.redirect(`/plugins`);
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1497
1592
|
const plugin_module = getState().plugins[name];
|
|
1498
1593
|
await sleep(1000); // Allow other workers to load this plugin
|
|
1499
1594
|
await getState().refresh_views();
|
package/routes/scapi.js
CHANGED
|
@@ -25,6 +25,7 @@ const {
|
|
|
25
25
|
stateFieldsToWhere,
|
|
26
26
|
readState,
|
|
27
27
|
} = require("@saltcorn/data/plugin-helper");
|
|
28
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* @type {object}
|
|
@@ -307,3 +308,21 @@ router.get(
|
|
|
307
308
|
)(req, res, next);
|
|
308
309
|
})
|
|
309
310
|
);
|
|
311
|
+
|
|
312
|
+
router.get(
|
|
313
|
+
"/reload",
|
|
314
|
+
error_catcher(async (req, res, next) => {
|
|
315
|
+
await passport.authenticate(
|
|
316
|
+
"api-bearer",
|
|
317
|
+
{ session: false },
|
|
318
|
+
async function (err, user, info) {
|
|
319
|
+
if (accessAllowedRead(req, user)) {
|
|
320
|
+
await getState().refresh_plugins();
|
|
321
|
+
res.json({ success: true });
|
|
322
|
+
} else {
|
|
323
|
+
res.status(401).json({ error: req.__("Not authorized") });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
)(req, res, next);
|
|
327
|
+
})
|
|
328
|
+
);
|
package/routes/search.js
CHANGED
|
@@ -202,7 +202,12 @@ const runSearch = async ({ q, _page, table }, req, res) => {
|
|
|
202
202
|
let tablesWithResults = [];
|
|
203
203
|
let tablesConfigured = 0;
|
|
204
204
|
for (const [tableName, viewName] of Object.entries(cfg)) {
|
|
205
|
-
if (
|
|
205
|
+
if (
|
|
206
|
+
!viewName ||
|
|
207
|
+
viewName === "" ||
|
|
208
|
+
viewName === "search_table_description" ||
|
|
209
|
+
tableName === "search_table_description"
|
|
210
|
+
)
|
|
206
211
|
continue;
|
|
207
212
|
tablesConfigured += 1;
|
|
208
213
|
if (table && tableName !== table) continue;
|
package/routes/sync.js
CHANGED
|
@@ -335,13 +335,14 @@ router.post(
|
|
|
335
335
|
"/clean_sync_dir",
|
|
336
336
|
error_catcher(async (req, res) => {
|
|
337
337
|
const { dir_name } = req.body;
|
|
338
|
+
const safe_dir_name = File.normalise(dir_name);
|
|
338
339
|
try {
|
|
339
340
|
const rootFolder = await File.rootFolder();
|
|
340
341
|
const syncDir = path.join(
|
|
341
342
|
rootFolder.location,
|
|
342
343
|
"mobile_app",
|
|
343
344
|
"sync",
|
|
344
|
-
|
|
345
|
+
safe_dir_name
|
|
345
346
|
);
|
|
346
347
|
await fs.rm(syncDir, { recursive: true, force: true });
|
|
347
348
|
res.status(200).send("");
|
package/routes/tables.js
CHANGED
|
@@ -192,6 +192,9 @@ const tableForm = async (table, req) => {
|
|
|
192
192
|
"if(!this.checked && !confirm('Are you sure? This will delete all history')) {this.checked = true; return false}",
|
|
193
193
|
},
|
|
194
194
|
type: "Bool",
|
|
195
|
+
help: {
|
|
196
|
+
topic: "Table history",
|
|
197
|
+
},
|
|
195
198
|
},
|
|
196
199
|
...(table.name === "users"
|
|
197
200
|
? []
|
|
@@ -670,7 +673,10 @@ router.get(
|
|
|
670
673
|
* @param {string} lbl
|
|
671
674
|
* @returns {string}
|
|
672
675
|
*/
|
|
673
|
-
const badge = (col, lbl) =>
|
|
676
|
+
const badge = (col, lbl, title) =>
|
|
677
|
+
`<span ${
|
|
678
|
+
title ? `title="${title}" ` : ""
|
|
679
|
+
}class="badge bg-${col}">${lbl}</span> `;
|
|
674
680
|
|
|
675
681
|
/**
|
|
676
682
|
* @param {object} f
|
|
@@ -708,7 +714,11 @@ const attribBadges = (f) => {
|
|
|
708
714
|
].includes(k)
|
|
709
715
|
)
|
|
710
716
|
return;
|
|
711
|
-
if (v
|
|
717
|
+
if (Array.isArray(v) && !v.length) return;
|
|
718
|
+
const title = ["string", "number", "boolean"].includes(typeof v)
|
|
719
|
+
? `${v}`
|
|
720
|
+
: null;
|
|
721
|
+
if (v || v === 0) s += badge("secondary", k, title);
|
|
712
722
|
});
|
|
713
723
|
}
|
|
714
724
|
return s;
|
|
@@ -791,7 +801,7 @@ router.get(
|
|
|
791
801
|
key: (r) => attribBadges(r),
|
|
792
802
|
},
|
|
793
803
|
{ label: req.__("Variable name"), key: (t) => code(t.name) },
|
|
794
|
-
...(table.external
|
|
804
|
+
...(table.external
|
|
795
805
|
? []
|
|
796
806
|
: [
|
|
797
807
|
{
|
|
@@ -1911,7 +1921,7 @@ router.post(
|
|
|
1911
1921
|
const table = Table.findOne({ name });
|
|
1912
1922
|
|
|
1913
1923
|
try {
|
|
1914
|
-
await table.deleteRows({});
|
|
1924
|
+
await table.deleteRows({}, req.user);
|
|
1915
1925
|
req.flash("success", req.__("Deleted all rows"));
|
|
1916
1926
|
} catch (e) {
|
|
1917
1927
|
req.flash("error", e.message);
|
package/serve.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @category server
|
|
5
5
|
* @module serve
|
|
6
6
|
*/
|
|
7
|
-
const runScheduler = require("@saltcorn/data/models/scheduler");
|
|
7
|
+
const { runScheduler } = require("@saltcorn/data/models/scheduler");
|
|
8
8
|
const User = require("@saltcorn/data/models/user");
|
|
9
9
|
const Plugin = require("@saltcorn/data/models/plugin");
|
|
10
10
|
const db = require("@saltcorn/data/db");
|
|
@@ -27,7 +27,7 @@ const {
|
|
|
27
27
|
loadAndSaveNewPlugin,
|
|
28
28
|
loadPlugin,
|
|
29
29
|
} = require("./load_plugins");
|
|
30
|
-
const { getConfig } = require("@saltcorn/data/models/config");
|
|
30
|
+
const { getConfig, setConfig } = require("@saltcorn/data/models/config");
|
|
31
31
|
const { migrate } = require("@saltcorn/data/migrate");
|
|
32
32
|
const socketio = require("socket.io");
|
|
33
33
|
const { createAdapter, setupPrimary } = require("@socket.io/cluster-adapter");
|
|
@@ -77,6 +77,17 @@ const ensureJwtSecret = () => {
|
|
|
77
77
|
}
|
|
78
78
|
};
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Ensure the engines cache is up to date with the current sc version
|
|
82
|
+
*/
|
|
83
|
+
const ensureEnginesCache = async () => {
|
|
84
|
+
const cacheScVersion = await getConfig("engines_cache_sc_version", "");
|
|
85
|
+
if (!cacheScVersion || cacheScVersion !== getState().scVersion) {
|
|
86
|
+
await setConfig("engines_cache", {});
|
|
87
|
+
await setConfig("engines_cache_sc_version", getState().scVersion);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
80
91
|
// helpful https://gist.github.com/jpoehls/2232358
|
|
81
92
|
/**
|
|
82
93
|
* @param {object} opts
|
|
@@ -222,7 +233,10 @@ module.exports =
|
|
|
222
233
|
dev,
|
|
223
234
|
...appargs
|
|
224
235
|
} = {}) => {
|
|
225
|
-
|
|
236
|
+
if (cluster.isMaster) {
|
|
237
|
+
ensureJwtSecret();
|
|
238
|
+
await ensureEnginesCache();
|
|
239
|
+
}
|
|
226
240
|
process.on("unhandledRejection", (reason, p) => {
|
|
227
241
|
console.error(reason, "Unhandled Rejection at Promise");
|
|
228
242
|
});
|
package/tests/fields.test.js
CHANGED
|
@@ -350,6 +350,38 @@ describe("Field Endpoints", () => {
|
|
|
350
350
|
.set("Cookie", loginCookie)
|
|
351
351
|
.expect(toInclude(" is: <pre>2</pre>"));
|
|
352
352
|
});
|
|
353
|
+
it("should show on stored expression with joinfield", async () => {
|
|
354
|
+
const loginCookie = await getAdminLoginCookie();
|
|
355
|
+
const table = Table.findOne({ name: "books" });
|
|
356
|
+
|
|
357
|
+
const ctx = encodeURIComponent(JSON.stringify({ table_id: table.id }));
|
|
358
|
+
const app = await getApp({ disableCsrf: true });
|
|
359
|
+
await request(app)
|
|
360
|
+
.post("/field/test-formula")
|
|
361
|
+
.send({
|
|
362
|
+
formula: "publisher.name",
|
|
363
|
+
tablename: "books",
|
|
364
|
+
stored: true,
|
|
365
|
+
})
|
|
366
|
+
.set("Cookie", loginCookie)
|
|
367
|
+
.expect(toInclude(" is: <pre>"));
|
|
368
|
+
});
|
|
369
|
+
it("should fail on non-stored expression with joinfield", async () => {
|
|
370
|
+
const loginCookie = await getAdminLoginCookie();
|
|
371
|
+
const table = Table.findOne({ name: "books" });
|
|
372
|
+
|
|
373
|
+
const ctx = encodeURIComponent(JSON.stringify({ table_id: table.id }));
|
|
374
|
+
const app = await getApp({ disableCsrf: true });
|
|
375
|
+
await request(app)
|
|
376
|
+
.post("/field/test-formula")
|
|
377
|
+
.send({
|
|
378
|
+
formula: "publisher.name",
|
|
379
|
+
tablename: "books",
|
|
380
|
+
stored: false,
|
|
381
|
+
})
|
|
382
|
+
.set("Cookie", loginCookie)
|
|
383
|
+
.expect(400);
|
|
384
|
+
});
|
|
353
385
|
it("should show calculated", async () => {
|
|
354
386
|
const loginCookie = await getAdminLoginCookie();
|
|
355
387
|
const table = Table.findOne({ name: "books" });
|