@saltcorn/data 0.6.1-beta.1 → 0.6.2-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/base-plugin/fieldviews.js +33 -28
- package/base-plugin/types.js +223 -94
- package/base-plugin/viewtemplates/edit.js +21 -17
- package/base-plugin/viewtemplates/filter.js +62 -15
- package/base-plugin/viewtemplates/list.js +16 -16
- package/base-plugin/viewtemplates/listshowlist.js +24 -10
- package/base-plugin/viewtemplates/room.js +24 -22
- package/base-plugin/viewtemplates/show.js +27 -26
- package/base-plugin/viewtemplates/viewable_fields.js +25 -26
- package/db/db.test.js +0 -154
- package/db/index.js +15 -10
- package/db/state.js +36 -14
- package/models/config.js +7 -1
- package/models/expression.js +37 -25
- package/models/field.js +19 -13
- package/models/file.js +9 -2
- package/models/form.js +7 -6
- package/models/page.js +5 -1
- package/models/table.js +10 -2
- package/models/tenant.js +29 -5
- package/models/view.js +26 -22
- package/package.json +14 -7
- package/plugin-helper.js +31 -11
- package/tests/calc.test.js +22 -0
- package/db/internal.js +0 -302
- package/db/multi-tenant.js +0 -44
- package/db/pg.js +0 -384
- package/db/single-tenant.js +0 -26
- package/db/sqlite.js +0 -297
- package/db/tenants.js +0 -12
|
@@ -16,7 +16,7 @@ const { traverseSync } = require("../../models/layout");
|
|
|
16
16
|
const { structuredClone } = require("../../utils");
|
|
17
17
|
const db = require("../../db");
|
|
18
18
|
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
20
|
* @function
|
|
21
21
|
* @param {string} viewname
|
|
22
22
|
* @param {Table|object} table
|
|
@@ -42,8 +42,8 @@ const action_url = contract(
|
|
|
42
42
|
);
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
-
* @param {string} url
|
|
46
|
-
* @param {object} req
|
|
45
|
+
* @param {string} url
|
|
46
|
+
* @param {object} req
|
|
47
47
|
* @param {object} opts
|
|
48
48
|
* @param {string} opts.action_name
|
|
49
49
|
* @param {string} opts.action_label
|
|
@@ -55,7 +55,7 @@ const action_url = contract(
|
|
|
55
55
|
* @param {string} opts.action_bgcol
|
|
56
56
|
* @param {string} opts.action_bordercol
|
|
57
57
|
* @param {string} opts.action_textcol
|
|
58
|
-
* @param {*} __
|
|
58
|
+
* @param {*} __
|
|
59
59
|
* @returns {object}
|
|
60
60
|
*/
|
|
61
61
|
const action_link = (
|
|
@@ -174,7 +174,7 @@ const make_link = contract(
|
|
|
174
174
|
);
|
|
175
175
|
|
|
176
176
|
/**
|
|
177
|
-
* @param {string} s
|
|
177
|
+
* @param {string} s
|
|
178
178
|
* @returns {object}
|
|
179
179
|
*/
|
|
180
180
|
const parse_view_select = (s) => {
|
|
@@ -197,7 +197,7 @@ const parse_view_select = (s) => {
|
|
|
197
197
|
};
|
|
198
198
|
|
|
199
199
|
//todo: use above to simplify code
|
|
200
|
-
/**
|
|
200
|
+
/**
|
|
201
201
|
* @function
|
|
202
202
|
* @param {object} opts
|
|
203
203
|
* @param {string} opts.view,
|
|
@@ -338,7 +338,7 @@ const view_linker = contract(
|
|
|
338
338
|
);
|
|
339
339
|
|
|
340
340
|
/**
|
|
341
|
-
* @param {string} nm
|
|
341
|
+
* @param {string} nm
|
|
342
342
|
* @returns {boolean}
|
|
343
343
|
*/
|
|
344
344
|
const action_requires_write = (nm) => {
|
|
@@ -347,7 +347,7 @@ const action_requires_write = (nm) => {
|
|
|
347
347
|
if (nm.startsWith("Toggle")) return true;
|
|
348
348
|
};
|
|
349
349
|
|
|
350
|
-
/**
|
|
350
|
+
/**
|
|
351
351
|
* @function
|
|
352
352
|
* @param {string} viewname
|
|
353
353
|
* @param {Table|object} table
|
|
@@ -527,8 +527,8 @@ const get_viewable_fields = contract(
|
|
|
527
527
|
);
|
|
528
528
|
|
|
529
529
|
/**
|
|
530
|
-
* @param {string} fname
|
|
531
|
-
* @param {object} req
|
|
530
|
+
* @param {string} fname
|
|
531
|
+
* @param {object} req
|
|
532
532
|
* @returns {string}
|
|
533
533
|
*/
|
|
534
534
|
const sortlinkForName = (fname, req) => {
|
|
@@ -543,10 +543,10 @@ const sortlinkForName = (fname, req) => {
|
|
|
543
543
|
};
|
|
544
544
|
|
|
545
545
|
/**
|
|
546
|
-
* @param {object} column
|
|
547
|
-
* @param {object} f
|
|
548
|
-
* @param {object} req
|
|
549
|
-
* @param {*} __
|
|
546
|
+
* @param {object} column
|
|
547
|
+
* @param {object} f
|
|
548
|
+
* @param {object} req
|
|
549
|
+
* @param {*} __
|
|
550
550
|
* @returns {string}
|
|
551
551
|
*/
|
|
552
552
|
const headerLabelForName = (column, f, req, __) => {
|
|
@@ -563,7 +563,7 @@ const headerLabelForName = (column, f, req, __) => {
|
|
|
563
563
|
return label + arrow;
|
|
564
564
|
};
|
|
565
565
|
|
|
566
|
-
/**
|
|
566
|
+
/**
|
|
567
567
|
* @function
|
|
568
568
|
* @param {Field[]} fields
|
|
569
569
|
* @param {object} state
|
|
@@ -597,12 +597,12 @@ const splitUniques = contract(
|
|
|
597
597
|
);
|
|
598
598
|
|
|
599
599
|
/**
|
|
600
|
-
* @param {object} table
|
|
601
|
-
* @param {string} viewname
|
|
602
|
-
* @param {object[]} [columns]
|
|
603
|
-
* @param {object} layout0
|
|
604
|
-
* @param {boolean} id
|
|
605
|
-
* @param {object} req
|
|
600
|
+
* @param {object} table
|
|
601
|
+
* @param {string} viewname
|
|
602
|
+
* @param {object[]} [columns]
|
|
603
|
+
* @param {object} layout0
|
|
604
|
+
* @param {boolean} id
|
|
605
|
+
* @param {object} req
|
|
606
606
|
* @returns {Promise<Form>}
|
|
607
607
|
*/
|
|
608
608
|
const getForm = async (table, viewname, columns, layout0, id, req) => {
|
|
@@ -626,7 +626,7 @@ const getForm = async (table, viewname, columns, layout0, id, req) => {
|
|
|
626
626
|
}
|
|
627
627
|
if (f.calculated)
|
|
628
628
|
f.sourceURL = `/field/show-calculated/${table.name}/${f.name}/${f.fieldview}`;
|
|
629
|
-
|
|
629
|
+
f.attributes = { ...column.configuration, ...f.attributes };
|
|
630
630
|
return f;
|
|
631
631
|
} else if (table.name === "users" && column.field_name === "password") {
|
|
632
632
|
return new Field({
|
|
@@ -669,15 +669,14 @@ const getForm = async (table, viewname, columns, layout0, id, req) => {
|
|
|
669
669
|
fields: tfields,
|
|
670
670
|
layout,
|
|
671
671
|
});
|
|
672
|
-
await form.fill_fkey_options();
|
|
673
672
|
if (id) form.hidden("id");
|
|
674
673
|
return form;
|
|
675
674
|
};
|
|
676
675
|
|
|
677
676
|
/**
|
|
678
|
-
* @param {object} table
|
|
679
|
-
* @param {object} req
|
|
680
|
-
* @param {object} fixed
|
|
677
|
+
* @param {object} table
|
|
678
|
+
* @param {object} req
|
|
679
|
+
* @param {object} fixed
|
|
681
680
|
* @returns {Promise<object>}
|
|
682
681
|
*/
|
|
683
682
|
const fill_presets = async (table, req, fixed) => {
|
package/db/db.test.js
CHANGED
|
@@ -1,162 +1,8 @@
|
|
|
1
|
-
const { sqlsanitize, mkWhere, sqlsanitizeAllowDots } = require("./internal");
|
|
2
1
|
const db = require("./index.js");
|
|
3
2
|
const Table = require("../models/table");
|
|
4
3
|
|
|
5
4
|
afterAll(db.close);
|
|
6
5
|
|
|
7
|
-
describe("sqlsanitize", () => {
|
|
8
|
-
it("should not alter valid name", () => {
|
|
9
|
-
expect(sqlsanitize("ffoo_oo")).toBe("ffoo_oo");
|
|
10
|
-
});
|
|
11
|
-
it("should remove spaces", () => {
|
|
12
|
-
expect(sqlsanitize(" ")).toBe("");
|
|
13
|
-
});
|
|
14
|
-
it("should remove chars from invalid name", () => {
|
|
15
|
-
expect(sqlsanitize("ffoo--oo--uu")).toBe("ffoooouu");
|
|
16
|
-
});
|
|
17
|
-
it("should not allow dots", () => {
|
|
18
|
-
expect(sqlsanitize("ffoo.oo")).toBe("ffoooo");
|
|
19
|
-
});
|
|
20
|
-
it("should allow dots when specified", () => {
|
|
21
|
-
expect(sqlsanitizeAllowDots("ffoo.oo")).toBe("ffoo.oo");
|
|
22
|
-
});
|
|
23
|
-
it("should allow quotes when dots specified", () => {
|
|
24
|
-
expect(sqlsanitizeAllowDots('ffoo."oo"')).toBe('ffoo."oo"');
|
|
25
|
-
});
|
|
26
|
-
it("should allow numbers", () => {
|
|
27
|
-
expect(sqlsanitize("ff1oo_oo")).toBe("ff1oo_oo");
|
|
28
|
-
});
|
|
29
|
-
it("should not allow numbers in initial position", () => {
|
|
30
|
-
expect(sqlsanitize("1ffoo_o1o")).toBe("_1ffoo_o1o");
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
describe("mkWhere", () => {
|
|
35
|
-
it("should empty on no arg", () => {
|
|
36
|
-
expect(mkWhere()).toStrictEqual({ values: [], where: "" });
|
|
37
|
-
});
|
|
38
|
-
it("should empty on null obj arg", () => {
|
|
39
|
-
expect(mkWhere({})).toStrictEqual({ values: [], where: "" });
|
|
40
|
-
});
|
|
41
|
-
it("should query json", () => {
|
|
42
|
-
expect(mkWhere({ foo: { json: ["bar", 5] } })).toStrictEqual({
|
|
43
|
-
values: [5],
|
|
44
|
-
where: `where "foo"->>'bar'=$1`,
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("should set id", () => {
|
|
49
|
-
expect(mkWhere({ id: 5 })).toStrictEqual({
|
|
50
|
-
values: [5],
|
|
51
|
-
where: 'where "id"=$1',
|
|
52
|
-
});
|
|
53
|
-
expect(mkWhere({ id: 5, hello: "world" })).toStrictEqual({
|
|
54
|
-
values: [5, "world"],
|
|
55
|
-
where: 'where "id"=$1 and "hello"=$2',
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
it("should query null", () => {
|
|
59
|
-
expect(mkWhere({ id: null })).toStrictEqual({
|
|
60
|
-
values: [],
|
|
61
|
-
where: 'where "id" is null',
|
|
62
|
-
});
|
|
63
|
-
expect(mkWhere({ id: null, foo: 1 })).toStrictEqual({
|
|
64
|
-
values: [1],
|
|
65
|
-
where: 'where "id" is null and "foo"=$1',
|
|
66
|
-
});
|
|
67
|
-
expect(mkWhere({ foo: 1, id: null })).toStrictEqual({
|
|
68
|
-
values: [1],
|
|
69
|
-
where: 'where "foo"=$1 and "id" is null',
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
it("should query lt/gt", () => {
|
|
73
|
-
expect(mkWhere({ id: { lt: 5 } })).toStrictEqual({
|
|
74
|
-
values: [5],
|
|
75
|
-
where: 'where "id"<$1',
|
|
76
|
-
});
|
|
77
|
-
expect(mkWhere({ id: { gt: 8 } })).toStrictEqual({
|
|
78
|
-
values: [8],
|
|
79
|
-
where: 'where "id">$1',
|
|
80
|
-
});
|
|
81
|
-
expect(mkWhere({ id: { lt: 5, equal: true } })).toStrictEqual({
|
|
82
|
-
values: [5],
|
|
83
|
-
where: 'where "id"<=$1',
|
|
84
|
-
});
|
|
85
|
-
expect(mkWhere({ id: { gt: 8, equal: true } })).toStrictEqual({
|
|
86
|
-
values: [8],
|
|
87
|
-
where: 'where "id">=$1',
|
|
88
|
-
});
|
|
89
|
-
expect(mkWhere({ id: [{ gt: 0 }, { lt: 10 }] })).toStrictEqual({
|
|
90
|
-
values: [0, 10],
|
|
91
|
-
where: 'where "id">$1 and "id"<$2',
|
|
92
|
-
});
|
|
93
|
-
expect(mkWhere({ id: { or: [{ gt: 10 }, { lt: 5 }] } })).toStrictEqual({
|
|
94
|
-
values: [10, 5],
|
|
95
|
-
where: 'where ("id">$1 or "id"<$2)',
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
it("should query subselect", () => {
|
|
99
|
-
expect(
|
|
100
|
-
mkWhere({
|
|
101
|
-
id: [{ inSelect: { table: "foo", field: "bar", where: { baz: 7 } } }],
|
|
102
|
-
})
|
|
103
|
-
).toStrictEqual({
|
|
104
|
-
values: [7],
|
|
105
|
-
where: 'where "id" in (select bar from foo where "baz"=$1)',
|
|
106
|
-
});
|
|
107
|
-
expect(
|
|
108
|
-
mkWhere({
|
|
109
|
-
age: 45,
|
|
110
|
-
id: [{ inSelect: { table: "foo", field: "bar", where: { baz: 7 } } }],
|
|
111
|
-
name: "Alice",
|
|
112
|
-
})
|
|
113
|
-
).toStrictEqual({
|
|
114
|
-
values: [45, 7, "Alice"],
|
|
115
|
-
where: `where "age"=$1 and "id" in (select bar from foo where "baz"=$2) and "name"=$3`,
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
it("should query or", () => {
|
|
119
|
-
expect(mkWhere({ or: [{ id: 5 }, { x: 7 }] })).toStrictEqual({
|
|
120
|
-
values: [5, 7],
|
|
121
|
-
where: 'where ("id"=$1 or "x"=$2)',
|
|
122
|
-
});
|
|
123
|
-
expect(mkWhere({ or: [{ id: 5 }, { x: { gt: 7 } }] })).toStrictEqual({
|
|
124
|
-
values: [5, 7],
|
|
125
|
-
where: 'where ("id"=$1 or "x">$2)',
|
|
126
|
-
});
|
|
127
|
-
expect(mkWhere({ or: [{ id: 5 }, { x: 7, y: 8 }] })).toStrictEqual({
|
|
128
|
-
values: [5, 7, 8],
|
|
129
|
-
where: 'where ("id"=$1 or "x"=$2 and "y"=$3)',
|
|
130
|
-
});
|
|
131
|
-
expect(mkWhere({ not: { id: 5 } })).toStrictEqual({
|
|
132
|
-
values: [5],
|
|
133
|
-
where: 'where not ("id"=$1)',
|
|
134
|
-
});
|
|
135
|
-
expect(mkWhere({ not: { id: 5, y: 1 } })).toStrictEqual({
|
|
136
|
-
values: [5, 1],
|
|
137
|
-
where: 'where not ("id"=$1 and "y"=$2)',
|
|
138
|
-
});
|
|
139
|
-
expect(mkWhere({ not: { y: { in: [1, 2, 3] } } })).toStrictEqual({
|
|
140
|
-
values: [[1, 2, 3]],
|
|
141
|
-
where: 'where not ("y" = ANY ($1))',
|
|
142
|
-
});
|
|
143
|
-
expect(mkWhere({ y: { in: [1, 2, 3] } })).toStrictEqual({
|
|
144
|
-
values: [[1, 2, 3]],
|
|
145
|
-
where: 'where "y" = ANY ($1)',
|
|
146
|
-
});
|
|
147
|
-
/*
|
|
148
|
-
TODO
|
|
149
|
-
expect(mkWhere([{ id: 5 }, { x: 7 }])).toStrictEqual({
|
|
150
|
-
values: [5, 7],
|
|
151
|
-
where: 'where "id"=$1 and "x"=$2',
|
|
152
|
-
});
|
|
153
|
-
expect(mkWhere([{ or: [{ id: 5 }, { x: 7 }] }, { z: 9 }])).toStrictEqual({
|
|
154
|
-
values: [5, 7, 9],
|
|
155
|
-
where: 'where ("id"=$1 or "x"=$2) and "z"=$3',
|
|
156
|
-
});*/
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
|
|
160
6
|
describe("where", () => {
|
|
161
7
|
it("should support in", async () => {
|
|
162
8
|
await Table.create("myothertable");
|
package/db/index.js
CHANGED
|
@@ -10,28 +10,33 @@
|
|
|
10
10
|
* @namespace db_overview
|
|
11
11
|
* @property {module:db/connect} connect
|
|
12
12
|
* @property {module:db/fixtures} fixtures
|
|
13
|
-
* @property {module:db/internal} internal
|
|
14
|
-
* @property {module:db/multi-tenant} multi-tenant
|
|
15
|
-
* @property {module:db/pg} pg
|
|
16
13
|
* @property {module:db/reset_schema} reset_schema
|
|
17
|
-
* @property {module:db/single-tenant} single-tenant
|
|
18
|
-
* @property {module:db/sqlite} sqlite
|
|
19
14
|
* @property {module:db/state} state
|
|
20
15
|
* @property {module:db/connect} connect
|
|
21
|
-
* @property {module:db/tenants} tenants
|
|
22
16
|
*
|
|
23
17
|
* @category saltcorn-data
|
|
24
18
|
* @subcategory db
|
|
25
19
|
*/
|
|
26
20
|
const { getConnectObject, is_sqlite } = require("./connect");
|
|
27
|
-
const { sqlsanitize, mkWhere } = require("
|
|
21
|
+
const { sqlsanitize, mkWhere } = require("@saltcorn/db-common/internal");
|
|
28
22
|
var connectObj = getConnectObject();
|
|
29
23
|
|
|
30
|
-
/** @type {db/sqlite|db/pg} */
|
|
31
|
-
|
|
24
|
+
/** @type {db/sqlite|db/pg|null} */
|
|
25
|
+
let dbmodule = null;
|
|
26
|
+
try {
|
|
27
|
+
if(is_sqlite(connectObj)) {
|
|
28
|
+
dbmodule = require("@saltcorn/sqlite/sqlite");
|
|
29
|
+
dbmodule.init(getConnectObject);
|
|
30
|
+
}
|
|
31
|
+
else
|
|
32
|
+
dbmodule = require("@saltcorn/postgres/postgres")(getConnectObject);
|
|
33
|
+
} catch(e) {
|
|
34
|
+
console.log("No database package found.")
|
|
35
|
+
throw e;
|
|
36
|
+
}
|
|
32
37
|
|
|
33
38
|
/** @type {db/tenant} */
|
|
34
|
-
const tenant = require("
|
|
39
|
+
const tenant = require("@saltcorn/db-common/tenants")(getConnectObject());
|
|
35
40
|
|
|
36
41
|
/** @type {boolean} */
|
|
37
42
|
const isSQLite = is_sqlite(connectObj);
|
package/db/state.js
CHANGED
|
@@ -21,7 +21,11 @@ const { migrate } = require("../migrate");
|
|
|
21
21
|
const File = require("../models/file");
|
|
22
22
|
const Trigger = require("../models/trigger");
|
|
23
23
|
const View = require("../models/view");
|
|
24
|
-
const {
|
|
24
|
+
const {
|
|
25
|
+
getAllTenants,
|
|
26
|
+
createTenant,
|
|
27
|
+
copy_tenant_template,
|
|
28
|
+
} = require("../models/tenant");
|
|
25
29
|
const {
|
|
26
30
|
getAllConfigOrDefaults,
|
|
27
31
|
setConfig,
|
|
@@ -35,7 +39,7 @@ const path = require("path");
|
|
|
35
39
|
const fs = require("fs");
|
|
36
40
|
|
|
37
41
|
/**
|
|
38
|
-
* @param {object} v
|
|
42
|
+
* @param {object} v
|
|
39
43
|
* @returns {void}
|
|
40
44
|
*/
|
|
41
45
|
const process_send = (v) => {
|
|
@@ -43,14 +47,14 @@ const process_send = (v) => {
|
|
|
43
47
|
};
|
|
44
48
|
|
|
45
49
|
/**
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
* State Class
|
|
51
|
+
* @category saltcorn-data
|
|
52
|
+
*/
|
|
49
53
|
class State {
|
|
50
54
|
/**
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
* State constructor
|
|
56
|
+
* @param {string} tenant description
|
|
57
|
+
*/
|
|
54
58
|
constructor(tenant) {
|
|
55
59
|
this.tenant = tenant;
|
|
56
60
|
this.views = [];
|
|
@@ -483,16 +487,16 @@ class State {
|
|
|
483
487
|
}
|
|
484
488
|
|
|
485
489
|
/**
|
|
486
|
-
*
|
|
487
|
-
* @param {function} f
|
|
490
|
+
*
|
|
491
|
+
* @param {function} f
|
|
488
492
|
*/
|
|
489
493
|
setRoomEmitter(f) {
|
|
490
494
|
this.roomEmitter = f;
|
|
491
495
|
}
|
|
492
496
|
|
|
493
497
|
/**
|
|
494
|
-
*
|
|
495
|
-
* @param {*} args
|
|
498
|
+
*
|
|
499
|
+
* @param {*} args
|
|
496
500
|
*/
|
|
497
501
|
emitRoom(...args) {
|
|
498
502
|
if (this.roomEmitter) this.roomEmitter(...args);
|
|
@@ -610,11 +614,29 @@ const init_multi_tenant = async (plugin_loader, disableMigrate) => {
|
|
|
610
614
|
* @param {boolean} noSignalOrDB
|
|
611
615
|
* @returns {Promise<void>}
|
|
612
616
|
*/
|
|
613
|
-
const create_tenant = async (
|
|
617
|
+
const create_tenant = async (
|
|
618
|
+
t,
|
|
619
|
+
plugin_loader,
|
|
620
|
+
newurl,
|
|
621
|
+
noSignalOrDB,
|
|
622
|
+
loadAndSaveNewPlugin
|
|
623
|
+
) => {
|
|
614
624
|
if (!noSignalOrDB) await createTenant(t, newurl);
|
|
615
625
|
tenants[t] = new State(t);
|
|
616
626
|
await db.runWithTenant(t, plugin_loader);
|
|
617
|
-
if (!noSignalOrDB)
|
|
627
|
+
if (!noSignalOrDB) {
|
|
628
|
+
const tenant_template = singleton.getConfig("tenant_template");
|
|
629
|
+
if (tenant_template) {
|
|
630
|
+
//create backup
|
|
631
|
+
await copy_tenant_template({
|
|
632
|
+
tenant_template,
|
|
633
|
+
target: t,
|
|
634
|
+
state: tenants[t],
|
|
635
|
+
loadAndSaveNewPlugin,
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
process_send({ createTenant: t });
|
|
639
|
+
}
|
|
618
640
|
};
|
|
619
641
|
/**
|
|
620
642
|
* Restart tenant
|
package/models/config.js
CHANGED
|
@@ -16,7 +16,7 @@ const defaultTimezone = moment.tz.guess();
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Config variables types
|
|
19
|
-
* @namespace
|
|
19
|
+
* @namespace
|
|
20
20
|
* @category saltcorn-data
|
|
21
21
|
*/
|
|
22
22
|
const configTypes = {
|
|
@@ -237,6 +237,12 @@ const configTypes = {
|
|
|
237
237
|
"Show a warning to users creating a tenant disclaiming warrenty of availability or security",
|
|
238
238
|
},
|
|
239
239
|
/** @type {object} */
|
|
240
|
+
tenant_template: {
|
|
241
|
+
type: "Tenant",
|
|
242
|
+
label: "New tenant template",
|
|
243
|
+
blurb: "Copy site structure for new tenants from this tenant",
|
|
244
|
+
},
|
|
245
|
+
/** @type {object} */
|
|
240
246
|
development_mode: {
|
|
241
247
|
type: "Bool",
|
|
242
248
|
label: "Development mode",
|
package/models/expression.js
CHANGED
|
@@ -9,7 +9,7 @@ const estraverse = require("estraverse");
|
|
|
9
9
|
const astring = require("astring");
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* @param {string} s
|
|
12
|
+
* @param {string} s
|
|
13
13
|
* @returns {boolean|void}
|
|
14
14
|
*/
|
|
15
15
|
function expressionValidator(s) {
|
|
@@ -23,7 +23,7 @@ function expressionValidator(s) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* @param {string} expression
|
|
26
|
+
* @param {string} expression
|
|
27
27
|
* @returns {string}
|
|
28
28
|
*/
|
|
29
29
|
function jsexprToSQL(expression) {
|
|
@@ -32,11 +32,11 @@ function jsexprToSQL(expression) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
* @param {string} expression
|
|
35
|
+
* @param {string} expression
|
|
36
36
|
* @throws {Error}
|
|
37
37
|
* @returns {object}
|
|
38
38
|
*/
|
|
39
|
-
function jsexprToWhere(expression) {
|
|
39
|
+
function jsexprToWhere(expression, extraCtx = {}) {
|
|
40
40
|
if (!expression) return {};
|
|
41
41
|
try {
|
|
42
42
|
const ast = acorn.parseExpressionAt(expression, 0, {
|
|
@@ -47,18 +47,26 @@ function jsexprToWhere(expression) {
|
|
|
47
47
|
const compile = (node) =>
|
|
48
48
|
({
|
|
49
49
|
BinaryExpression() {
|
|
50
|
+
const cleft = compile(node.left);
|
|
51
|
+
const cright = compile(node.right);
|
|
52
|
+
const cmp =
|
|
53
|
+
typeof cleft === "string" || cleft === null
|
|
54
|
+
? { eq: [cleft, cright] }
|
|
55
|
+
: typeof cleft === "symbol"
|
|
56
|
+
? { [cleft.description]: cright }
|
|
57
|
+
: { [cleft]: cright };
|
|
50
58
|
return {
|
|
51
|
-
"=="(
|
|
52
|
-
return
|
|
59
|
+
"=="() {
|
|
60
|
+
return cmp;
|
|
53
61
|
},
|
|
54
|
-
"==="(
|
|
55
|
-
return
|
|
62
|
+
"==="() {
|
|
63
|
+
return cmp;
|
|
56
64
|
},
|
|
57
|
-
"!="(
|
|
58
|
-
return { not:
|
|
65
|
+
"!="() {
|
|
66
|
+
return { not: cmp };
|
|
59
67
|
},
|
|
60
|
-
"!=="(
|
|
61
|
-
return { not:
|
|
68
|
+
"!=="() {
|
|
69
|
+
return { not: cmp };
|
|
62
70
|
},
|
|
63
71
|
}[node.operator](node);
|
|
64
72
|
},
|
|
@@ -83,14 +91,18 @@ function jsexprToWhere(expression) {
|
|
|
83
91
|
}[node.operator](node);
|
|
84
92
|
},
|
|
85
93
|
Identifier({ name }) {
|
|
86
|
-
|
|
94
|
+
if (name[0] === "$") {
|
|
95
|
+
return extraCtx[name.substring(1)] || null;
|
|
96
|
+
}
|
|
97
|
+
return Symbol(name);
|
|
87
98
|
},
|
|
88
99
|
Literal({ value }) {
|
|
89
100
|
return value;
|
|
90
101
|
},
|
|
91
102
|
}[node.type](node));
|
|
92
103
|
return compile(ast);
|
|
93
|
-
} catch {
|
|
104
|
+
} catch (e) {
|
|
105
|
+
console.error(e);
|
|
94
106
|
throw new Error(
|
|
95
107
|
`Expression "${expression}" is too complicated, I do not understand`
|
|
96
108
|
);
|
|
@@ -98,8 +110,8 @@ function jsexprToWhere(expression) {
|
|
|
98
110
|
}
|
|
99
111
|
|
|
100
112
|
/**
|
|
101
|
-
* @param {string} expression
|
|
102
|
-
* @param {object[]} statefuns
|
|
113
|
+
* @param {string} expression
|
|
114
|
+
* @param {object[]} statefuns
|
|
103
115
|
* @returns {object}
|
|
104
116
|
*/
|
|
105
117
|
function transform_for_async(expression, statefuns) {
|
|
@@ -125,8 +137,8 @@ function transform_for_async(expression, statefuns) {
|
|
|
125
137
|
}
|
|
126
138
|
|
|
127
139
|
/**
|
|
128
|
-
* @param {string} expression
|
|
129
|
-
* @param {object[]} fields
|
|
140
|
+
* @param {string} expression
|
|
141
|
+
* @param {object[]} fields
|
|
130
142
|
* @returns {any}
|
|
131
143
|
*/
|
|
132
144
|
function get_expression_function(expression, fields) {
|
|
@@ -142,8 +154,8 @@ function get_expression_function(expression, fields) {
|
|
|
142
154
|
}
|
|
143
155
|
|
|
144
156
|
/**
|
|
145
|
-
* @param {string} expression
|
|
146
|
-
* @param {object[]} fields
|
|
157
|
+
* @param {string} expression
|
|
158
|
+
* @param {object[]} fields
|
|
147
159
|
* @param {object} [extraContext = {}]
|
|
148
160
|
* @returns {any}
|
|
149
161
|
*/
|
|
@@ -162,9 +174,9 @@ function get_async_expression_function(expression, fields, extraContext = {}) {
|
|
|
162
174
|
}
|
|
163
175
|
|
|
164
176
|
/**
|
|
165
|
-
* @param {object[]} rows
|
|
166
|
-
* @param {object[]} fields
|
|
167
|
-
* @returns {object[]}
|
|
177
|
+
* @param {object[]} rows
|
|
178
|
+
* @param {object[]} fields
|
|
179
|
+
* @returns {object[]}
|
|
168
180
|
*/
|
|
169
181
|
function apply_calculated_fields(rows, fields) {
|
|
170
182
|
let hasExprs = false;
|
|
@@ -196,8 +208,8 @@ function apply_calculated_fields(rows, fields) {
|
|
|
196
208
|
}
|
|
197
209
|
|
|
198
210
|
/**
|
|
199
|
-
* @param {*} row
|
|
200
|
-
* @param {*} fields
|
|
211
|
+
* @param {*} row
|
|
212
|
+
* @param {*} fields
|
|
201
213
|
* @returns {Promise<any>}
|
|
202
214
|
*/
|
|
203
215
|
const apply_calculated_fields_stored = async (row, fields) => {
|