@saltcorn/data 0.6.2 → 0.6.3-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base-plugin/fieldviews.d.ts.map +1 -1
- package/dist/base-plugin/fieldviews.js +2 -0
- package/dist/base-plugin/fieldviews.js.map +1 -1
- package/dist/base-plugin/index.d.ts +72 -67
- package/dist/base-plugin/index.d.ts.map +1 -1
- package/dist/base-plugin/types.d.ts.map +1 -1
- package/dist/base-plugin/types.js +9 -8
- package/dist/base-plugin/types.js.map +1 -1
- package/dist/base-plugin/viewtemplates/edit.d.ts +1 -1
- package/dist/base-plugin/viewtemplates/edit.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/edit.js +25 -2
- package/dist/base-plugin/viewtemplates/edit.js.map +1 -1
- package/dist/base-plugin/viewtemplates/feed.d.ts +1 -1
- package/dist/base-plugin/viewtemplates/feed.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/feed.js +11 -1
- package/dist/base-plugin/viewtemplates/feed.js.map +1 -1
- package/dist/base-plugin/viewtemplates/list.d.ts +1 -1
- package/dist/base-plugin/viewtemplates/list.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/list.js +23 -11
- package/dist/base-plugin/viewtemplates/list.js.map +1 -1
- package/dist/base-plugin/viewtemplates/show.d.ts +5 -0
- package/dist/base-plugin/viewtemplates/show.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/show.js +43 -7
- package/dist/base-plugin/viewtemplates/show.js.map +1 -1
- package/dist/base-plugin/viewtemplates/viewable_fields.d.ts +2 -1
- package/dist/base-plugin/viewtemplates/viewable_fields.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/viewable_fields.js +30 -10
- package/dist/base-plugin/viewtemplates/viewable_fields.js.map +1 -1
- package/dist/contracts.js +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/db/connect.d.ts.map +1 -1
- package/dist/db/connect.js +9 -2
- package/dist/db/connect.js.map +1 -1
- package/dist/db/index.d.ts +6 -0
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +6 -0
- package/dist/db/index.js.map +1 -1
- package/dist/db/state.d.ts +1 -0
- package/dist/db/state.d.ts.map +1 -1
- package/dist/db/state.js +13 -4
- package/dist/db/state.js.map +1 -1
- package/dist/migrate.js +1 -1
- package/dist/migrate.js.map +1 -1
- package/dist/migrations/202112282254.d.ts +3 -0
- package/dist/migrations/202112282254.d.ts.map +1 -0
- package/dist/migrations/202112282254.js +5 -0
- package/dist/migrations/202112282254.js.map +1 -0
- package/dist/models/backup.d.ts +8 -21
- package/dist/models/backup.d.ts.map +1 -1
- package/dist/models/backup.js +107 -103
- package/dist/models/backup.js.map +1 -1
- package/dist/models/config.d.ts +39 -179
- package/dist/models/config.d.ts.map +1 -1
- package/dist/models/config.js +77 -50
- package/dist/models/config.js.map +1 -1
- package/dist/models/crash.d.ts +40 -47
- package/dist/models/crash.d.ts.map +1 -1
- package/dist/models/crash.js +40 -39
- package/dist/models/crash.js.map +1 -1
- package/dist/models/email.d.ts +11 -13
- package/dist/models/email.d.ts.map +1 -1
- package/dist/models/email.js +19 -15
- package/dist/models/email.js.map +1 -1
- package/dist/models/eventlog.d.ts +39 -38
- package/dist/models/eventlog.d.ts.map +1 -1
- package/dist/models/eventlog.js +14 -29
- package/dist/models/eventlog.js.map +1 -1
- package/dist/models/expression.d.ts +37 -31
- package/dist/models/expression.d.ts.map +1 -1
- package/dist/models/expression.js +47 -23
- package/dist/models/expression.js.map +1 -1
- package/dist/models/field.d.ts +125 -121
- package/dist/models/field.d.ts.map +1 -1
- package/dist/models/field.js +125 -147
- package/dist/models/field.js.map +1 -1
- package/dist/models/fieldrepeat.d.ts +25 -20
- package/dist/models/fieldrepeat.d.ts.map +1 -1
- package/dist/models/fieldrepeat.js +8 -14
- package/dist/models/fieldrepeat.js.map +1 -1
- package/dist/models/file.d.ts +56 -58
- package/dist/models/file.d.ts.map +1 -1
- package/dist/models/file.js +25 -52
- package/dist/models/file.js.map +1 -1
- package/dist/models/form.d.ts +69 -50
- package/dist/models/form.d.ts.map +1 -1
- package/dist/models/form.js +49 -49
- package/dist/models/form.js.map +1 -1
- package/dist/models/index.d.ts +39 -0
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js.map +1 -1
- package/dist/models/layout.d.ts +16 -31
- package/dist/models/layout.d.ts.map +1 -1
- package/dist/models/layout.js +2 -4
- package/dist/models/layout.js.map +1 -1
- package/dist/models/library.d.ts +21 -19
- package/dist/models/library.d.ts.map +1 -1
- package/dist/models/library.js +10 -8
- package/dist/models/library.js.map +1 -1
- package/dist/models/pack.d.ts +37 -87
- package/dist/models/pack.d.ts.map +1 -1
- package/dist/models/pack.js +120 -87
- package/dist/models/pack.js.map +1 -1
- package/dist/models/page.d.ts +23 -47
- package/dist/models/page.d.ts.map +1 -1
- package/dist/models/page.js +16 -40
- package/dist/models/page.js.map +1 -1
- package/dist/models/plugin.d.ts +41 -65
- package/dist/models/plugin.d.ts.map +1 -1
- package/dist/models/plugin.js +17 -39
- package/dist/models/plugin.js.map +1 -1
- package/dist/models/role.d.ts +18 -11
- package/dist/models/role.d.ts.map +1 -1
- package/dist/models/role.js +16 -8
- package/dist/models/role.js.map +1 -1
- package/dist/models/scheduler.d.ts +8 -8
- package/dist/models/scheduler.d.ts.map +1 -1
- package/dist/models/scheduler.js +17 -14
- package/dist/models/scheduler.js.map +1 -1
- package/dist/models/table.d.ts +87 -133
- package/dist/models/table.d.ts.map +1 -1
- package/dist/models/table.js +237 -182
- package/dist/models/table.js.map +1 -1
- package/dist/models/table_constraints.d.ts +44 -39
- package/dist/models/table_constraints.d.ts.map +1 -1
- package/dist/models/table_constraints.js +13 -32
- package/dist/models/table_constraints.js.map +1 -1
- package/dist/models/tenant.d.ts +6 -0
- package/dist/models/tenant.d.ts.map +1 -1
- package/dist/models/tenant.js +9 -0
- package/dist/models/tenant.js.map +1 -1
- package/dist/models/trigger.d.ts +50 -69
- package/dist/models/trigger.d.ts.map +1 -1
- package/dist/models/trigger.js +8 -34
- package/dist/models/trigger.js.map +1 -1
- package/dist/models/user.d.ts +108 -142
- package/dist/models/user.d.ts.map +1 -1
- package/dist/models/user.js +51 -71
- package/dist/models/user.js.map +1 -1
- package/dist/models/view.d.ts +94 -98
- package/dist/models/view.d.ts.map +1 -1
- package/dist/models/view.js +103 -80
- package/dist/models/view.js.map +1 -1
- package/dist/models/workflow.d.ts +23 -21
- package/dist/models/workflow.d.ts.map +1 -1
- package/dist/models/workflow.js +9 -17
- package/dist/models/workflow.js.map +1 -1
- package/dist/plugin-helper.d.ts.map +1 -1
- package/dist/plugin-helper.js +30 -12
- package/dist/plugin-helper.js.map +1 -1
- package/dist/tests/calc.test.js +13 -0
- package/dist/tests/calc.test.js.map +1 -1
- package/dist/tests/exact_views.test.js +214 -0
- package/dist/tests/exact_views.test.js.map +1 -1
- package/dist/tests/table.test.js +57 -2
- package/dist/tests/table.test.js.map +1 -1
- package/dist/tsconfig.ref.tsbuildinfo +1 -1
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +26 -0
- package/dist/utils.js.map +1 -1
- package/package.json +17 -5
- package/dist/coverage/lcov-report/block-navigation.d.ts +0 -2
- package/dist/coverage/lcov-report/block-navigation.d.ts.map +0 -1
- package/dist/coverage/lcov-report/block-navigation.js +0 -66
- package/dist/coverage/lcov-report/block-navigation.js.map +0 -1
- package/dist/coverage/lcov-report/prettify.d.ts +0 -1
- package/dist/coverage/lcov-report/prettify.d.ts.map +0 -1
- package/dist/coverage/lcov-report/prettify.js +0 -478
- package/dist/coverage/lcov-report/prettify.js.map +0 -1
- package/dist/coverage/lcov-report/sorter.d.ts +0 -2
- package/dist/coverage/lcov-report/sorter.d.ts.map +0 -1
- package/dist/coverage/lcov-report/sorter.js +0 -141
- package/dist/coverage/lcov-report/sorter.js.map +0 -1
package/dist/models/table.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
/**
|
|
3
6
|
* Table Database Access Layer
|
|
4
7
|
* @category saltcorn-data
|
|
5
8
|
* @module models/table
|
|
6
9
|
* @subcategory models
|
|
7
10
|
*/
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
11
|
+
const db_1 = __importDefault(require("../db"));
|
|
12
|
+
const internal_1 = require("@saltcorn/db-common/internal");
|
|
13
|
+
const field_1 = __importDefault(require("./field"));
|
|
14
|
+
const common_types_1 = require("@saltcorn/types/common_types");
|
|
15
|
+
const trigger_1 = __importDefault(require("./trigger"));
|
|
12
16
|
const { apply_calculated_fields, apply_calculated_fields_stored, recalculate_for_stored, get_expression_function, } = require("./expression");
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
17
|
+
const csvtojson_1 = __importDefault(require("csvtojson"));
|
|
18
|
+
const moment_1 = __importDefault(require("moment"));
|
|
19
|
+
const fs_1 = require("fs");
|
|
20
|
+
const promises_1 = require("fs/promises");
|
|
21
|
+
const utils_1 = require("../utils");
|
|
18
22
|
const { InvalidConfiguration, InvalidAdminAction, satisfies, structuredClone, getLines, } = require("../utils");
|
|
19
23
|
/**
|
|
20
24
|
* Transponce Objects
|
|
@@ -40,7 +44,7 @@ const transposeObjects = (objs) => {
|
|
|
40
44
|
return res;
|
|
41
45
|
};
|
|
42
46
|
// todo support also other date formats https://momentjs.com/docs/
|
|
43
|
-
const dateFormats = [
|
|
47
|
+
const dateFormats = [moment_1.default.ISO_8601];
|
|
44
48
|
// todo refactor - move to separated data utils module?
|
|
45
49
|
/**
|
|
46
50
|
* Is Valid Date of format moment.ISO_8601,
|
|
@@ -50,7 +54,7 @@ const dateFormats = [moment.ISO_8601];
|
|
|
50
54
|
* @returns {boolean}
|
|
51
55
|
*/
|
|
52
56
|
const isDate = function (date) {
|
|
53
|
-
return
|
|
57
|
+
return (0, moment_1.default)(date, dateFormats, true).isValid();
|
|
54
58
|
};
|
|
55
59
|
// todo resolve database specific
|
|
56
60
|
/**
|
|
@@ -59,7 +63,7 @@ const isDate = function (date) {
|
|
|
59
63
|
* @returns {string}
|
|
60
64
|
*/
|
|
61
65
|
// todo refactor
|
|
62
|
-
const normalise_error_message = (msg) =>
|
|
66
|
+
const normalise_error_message = (msg) => db_1.default.isSQLite
|
|
63
67
|
? msg.replace(/SQLITE_CONSTRAINT: UNIQUE constraint failed: (.*?)\.(.*?)/, "Duplicate value for unique field: $2")
|
|
64
68
|
: msg.replace(/duplicate key value violates unique constraint "(.*?)_(.*?)_unique"/, "Duplicate value for unique field: $2");
|
|
65
69
|
/**
|
|
@@ -82,8 +86,7 @@ class Table {
|
|
|
82
86
|
this.external = false;
|
|
83
87
|
this.description = o.description;
|
|
84
88
|
if (o.fields)
|
|
85
|
-
this.fields = o.fields.map((f) => new
|
|
86
|
-
contract.class(this);
|
|
89
|
+
this.fields = o.fields.map((f) => new field_1.default(f));
|
|
87
90
|
}
|
|
88
91
|
/**
|
|
89
92
|
*
|
|
@@ -125,7 +128,7 @@ class Table {
|
|
|
125
128
|
const { getState } = require("../db/state");
|
|
126
129
|
return getState().tables.map((t) => new Table(t));
|
|
127
130
|
}
|
|
128
|
-
const tbls = await
|
|
131
|
+
const tbls = await db_1.default.select("_sc_tables", where, selectopts);
|
|
129
132
|
return tbls.map((t) => new Table(t));
|
|
130
133
|
}
|
|
131
134
|
/**
|
|
@@ -144,7 +147,7 @@ class Table {
|
|
|
144
147
|
}
|
|
145
148
|
if (external !== true) {
|
|
146
149
|
//do include db tables
|
|
147
|
-
const tbls = await
|
|
150
|
+
const tbls = await db_1.default.select("_sc_tables", where, selectopts);
|
|
148
151
|
dbs = tbls.map((t) => new Table(t));
|
|
149
152
|
}
|
|
150
153
|
return [...dbs, ...externals];
|
|
@@ -155,10 +158,10 @@ class Table {
|
|
|
155
158
|
* @returns {null|*} null or owner column name
|
|
156
159
|
*/
|
|
157
160
|
owner_fieldname_from_fields(fields) {
|
|
158
|
-
if (!this.ownership_field_id)
|
|
161
|
+
if (!this.ownership_field_id || !fields)
|
|
159
162
|
return null;
|
|
160
163
|
const field = fields.find((f) => f.id === this.ownership_field_id);
|
|
161
|
-
return field
|
|
164
|
+
return field?.name;
|
|
162
165
|
}
|
|
163
166
|
/**
|
|
164
167
|
* Get owner column name
|
|
@@ -185,7 +188,9 @@ class Table {
|
|
|
185
188
|
return f(row, user);
|
|
186
189
|
}
|
|
187
190
|
const field_name = this.owner_fieldname();
|
|
188
|
-
|
|
191
|
+
if (!field_name && this.name === "users")
|
|
192
|
+
return user && user.id && row && `${row.id}` === `${user.id}`;
|
|
193
|
+
return typeof field_name === "string" && row[field_name] === user.id;
|
|
189
194
|
}
|
|
190
195
|
/**
|
|
191
196
|
* Create table
|
|
@@ -194,9 +199,9 @@ class Table {
|
|
|
194
199
|
* @returns {Promise<Table>} table
|
|
195
200
|
*/
|
|
196
201
|
static async create(name, options = {}) {
|
|
197
|
-
const schema =
|
|
202
|
+
const schema = db_1.default.getTenantSchemaPrefix();
|
|
198
203
|
// create table in database
|
|
199
|
-
await
|
|
204
|
+
await db_1.default.query(`create table ${schema}"${(0, internal_1.sqlsanitize)(name)}" (id ${db_1.default.isSQLite ? "integer" : "serial"} primary key)`);
|
|
200
205
|
// populate table definition row
|
|
201
206
|
const tblrow = {
|
|
202
207
|
name,
|
|
@@ -208,9 +213,9 @@ class Table {
|
|
|
208
213
|
description: options.description || "",
|
|
209
214
|
};
|
|
210
215
|
// insert table defintion into _sc_tables
|
|
211
|
-
const id = await
|
|
216
|
+
const id = await db_1.default.insert("_sc_tables", tblrow);
|
|
212
217
|
// add primary key columnt ID
|
|
213
|
-
await
|
|
218
|
+
await db_1.default.query(`insert into ${schema}_sc_fields(table_id, name, label, type, attributes, required, is_unique,primary_key)
|
|
214
219
|
values($1,'id','ID','Integer', '{}', true, true, true)`, [id]);
|
|
215
220
|
// create table
|
|
216
221
|
const table = new Table({ ...tblrow, id });
|
|
@@ -226,20 +231,20 @@ class Table {
|
|
|
226
231
|
* @returns {Promise<void>}
|
|
227
232
|
*/
|
|
228
233
|
async delete(only_forget = false) {
|
|
229
|
-
const schema =
|
|
230
|
-
const is_sqlite =
|
|
234
|
+
const schema = db_1.default.getTenantSchemaPrefix();
|
|
235
|
+
const is_sqlite = db_1.default.isSQLite;
|
|
231
236
|
await this.update({ ownership_field_id: null });
|
|
232
|
-
const client = is_sqlite ?
|
|
237
|
+
const client = is_sqlite ? db_1.default : await db_1.default.getClient();
|
|
233
238
|
await client.query(`BEGIN`);
|
|
234
239
|
try {
|
|
235
240
|
if (!only_forget)
|
|
236
|
-
await client.query(`drop table if exists ${schema}"${sqlsanitize(this.name)}"`);
|
|
241
|
+
await client.query(`drop table if exists ${schema}"${(0, internal_1.sqlsanitize)(this.name)}"`);
|
|
237
242
|
await client.query(`delete FROM ${schema}_sc_fields WHERE table_id = $1`, [this.id]);
|
|
238
243
|
await client.query(`delete FROM ${schema}_sc_tables WHERE id = $1`, [
|
|
239
244
|
this.id,
|
|
240
245
|
]);
|
|
241
246
|
if (this.versioned)
|
|
242
|
-
await client.query(`drop table if exists ${schema}"${sqlsanitize(this.name)}__history"`);
|
|
247
|
+
await client.query(`drop table if exists ${schema}"${(0, internal_1.sqlsanitize)(this.name)}__history"`);
|
|
243
248
|
await client.query(`COMMIT`);
|
|
244
249
|
}
|
|
245
250
|
catch (e) {
|
|
@@ -257,7 +262,18 @@ class Table {
|
|
|
257
262
|
* @type {string}
|
|
258
263
|
*/
|
|
259
264
|
get sql_name() {
|
|
260
|
-
return `${
|
|
265
|
+
return `${db_1.default.getTenantSchemaPrefix()}"${(0, internal_1.sqlsanitize)(this.name)}"`;
|
|
266
|
+
}
|
|
267
|
+
async resetSequence() {
|
|
268
|
+
const fields = await this.getFields();
|
|
269
|
+
const pk = fields.find((f) => f.primary_key);
|
|
270
|
+
if (!pk) {
|
|
271
|
+
throw new Error("Unable to find a field with a primary key.");
|
|
272
|
+
}
|
|
273
|
+
if (db_1.default.reset_sequence &&
|
|
274
|
+
(0, common_types_1.instanceOfType)(pk.type) &&
|
|
275
|
+
pk.type.name === "Integer")
|
|
276
|
+
await db_1.default.reset_sequence(this.name);
|
|
261
277
|
}
|
|
262
278
|
/**
|
|
263
279
|
* Delete rows from table
|
|
@@ -266,7 +282,7 @@ class Table {
|
|
|
266
282
|
*/
|
|
267
283
|
async deleteRows(where) {
|
|
268
284
|
// get triggers on delete
|
|
269
|
-
const triggers = await
|
|
285
|
+
const triggers = await trigger_1.default.getTableTriggers("Delete", this);
|
|
270
286
|
if (triggers.length > 0) {
|
|
271
287
|
const rows = await this.getRows(where);
|
|
272
288
|
for (const trigger of triggers) {
|
|
@@ -276,7 +292,8 @@ class Table {
|
|
|
276
292
|
}
|
|
277
293
|
}
|
|
278
294
|
}
|
|
279
|
-
await
|
|
295
|
+
await db_1.default.deleteWhere(this.name, where);
|
|
296
|
+
await this.resetSequence();
|
|
280
297
|
}
|
|
281
298
|
/**
|
|
282
299
|
* Returns row with only fields that can be read from db (readFromDB flag)
|
|
@@ -284,9 +301,11 @@ class Table {
|
|
|
284
301
|
* @returns {*}
|
|
285
302
|
*/
|
|
286
303
|
readFromDB(row) {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
304
|
+
if (this.fields) {
|
|
305
|
+
for (const f of this.fields) {
|
|
306
|
+
if (f.type && (0, common_types_1.instanceOfType)(f.type) && f.type.readFromDB)
|
|
307
|
+
row[f.name] = f.type.readFromDB(row[f.name]);
|
|
308
|
+
}
|
|
290
309
|
}
|
|
291
310
|
return row;
|
|
292
311
|
}
|
|
@@ -297,7 +316,7 @@ class Table {
|
|
|
297
316
|
*/
|
|
298
317
|
async getRow(where = {}) {
|
|
299
318
|
await this.getFields();
|
|
300
|
-
const row = await
|
|
319
|
+
const row = await db_1.default.selectMaybeOne(this.name, where);
|
|
301
320
|
if (!row)
|
|
302
321
|
return null;
|
|
303
322
|
return apply_calculated_fields([this.readFromDB(row)], this.fields)[0];
|
|
@@ -310,7 +329,7 @@ class Table {
|
|
|
310
329
|
*/
|
|
311
330
|
async getRows(where = {}, selopts) {
|
|
312
331
|
await this.getFields();
|
|
313
|
-
const rows = await
|
|
332
|
+
const rows = await db_1.default.select(this.name, where, selopts);
|
|
314
333
|
return apply_calculated_fields(rows.map((r) => this.readFromDB(r)), this.fields);
|
|
315
334
|
}
|
|
316
335
|
/**
|
|
@@ -319,7 +338,7 @@ class Table {
|
|
|
319
338
|
* @returns {Promise<number>}
|
|
320
339
|
*/
|
|
321
340
|
async countRows(where) {
|
|
322
|
-
return await
|
|
341
|
+
return await db_1.default.count(this.name, where);
|
|
323
342
|
}
|
|
324
343
|
/**
|
|
325
344
|
* Return distinct Values for column in table
|
|
@@ -328,7 +347,7 @@ class Table {
|
|
|
328
347
|
* @returns {Promise<Object[]>}
|
|
329
348
|
*/
|
|
330
349
|
async distinctValues(fieldnm) {
|
|
331
|
-
const res = await
|
|
350
|
+
const res = await db_1.default.query(`select distinct "${db_1.default.sqlsanitize(fieldnm)}" from ${this.sql_name}`);
|
|
332
351
|
return res.rows.map((r) => r[fieldnm]);
|
|
333
352
|
}
|
|
334
353
|
/**
|
|
@@ -344,15 +363,15 @@ class Table {
|
|
|
344
363
|
const fields = await this.getFields();
|
|
345
364
|
const pk_name = this.pk_name;
|
|
346
365
|
if (fields.some((f) => f.calculated && f.stored)) {
|
|
347
|
-
existing = await
|
|
366
|
+
existing = await db_1.default.selectOne(this.name, { [pk_name]: id });
|
|
348
367
|
v = await apply_calculated_fields_stored({ ...existing, ...v_in }, this.fields);
|
|
349
368
|
}
|
|
350
369
|
else
|
|
351
370
|
v = v_in;
|
|
352
371
|
if (this.versioned) {
|
|
353
372
|
if (!existing)
|
|
354
|
-
existing = await
|
|
355
|
-
await
|
|
373
|
+
existing = await db_1.default.selectOne(this.name, { [pk_name]: id });
|
|
374
|
+
await db_1.default.insert(this.name + "__history", {
|
|
356
375
|
...existing,
|
|
357
376
|
...v,
|
|
358
377
|
[pk_name]: id,
|
|
@@ -363,14 +382,14 @@ class Table {
|
|
|
363
382
|
_userid,
|
|
364
383
|
});
|
|
365
384
|
}
|
|
366
|
-
await
|
|
385
|
+
await db_1.default.update(this.name, v, id, { pk_name });
|
|
367
386
|
if (typeof existing === "undefined") {
|
|
368
|
-
const triggers = await
|
|
387
|
+
const triggers = await trigger_1.default.getTableTriggers("Update", this);
|
|
369
388
|
if (triggers.length > 0)
|
|
370
|
-
existing = await
|
|
389
|
+
existing = await db_1.default.selectOne(this.name, { [pk_name]: id });
|
|
371
390
|
}
|
|
372
391
|
const newRow = { ...existing, ...v, [pk_name]: id };
|
|
373
|
-
await
|
|
392
|
+
await trigger_1.default.runTableTriggers("Update", this, newRow);
|
|
374
393
|
}
|
|
375
394
|
/**
|
|
376
395
|
* Try to Update row
|
|
@@ -395,11 +414,13 @@ class Table {
|
|
|
395
414
|
* @returns {Promise<void>}
|
|
396
415
|
*/
|
|
397
416
|
async toggleBool(id, field_name) {
|
|
398
|
-
const schema =
|
|
399
|
-
await
|
|
400
|
-
const triggers = await
|
|
417
|
+
const schema = db_1.default.getTenantSchemaPrefix();
|
|
418
|
+
await db_1.default.query(`update ${schema}"${(0, internal_1.sqlsanitize)(this.name)}" set "${(0, internal_1.sqlsanitize)(field_name)}"=NOT coalesce("${(0, internal_1.sqlsanitize)(field_name)}", false) where id=$1`, [id]);
|
|
419
|
+
const triggers = await trigger_1.default.getTableTriggers("Update", this);
|
|
401
420
|
if (triggers.length > 0) {
|
|
402
421
|
const row = await this.getRow({ id });
|
|
422
|
+
if (!row)
|
|
423
|
+
throw new Error(`Unable to find row with id: ${id}`);
|
|
403
424
|
for (const trigger of triggers) {
|
|
404
425
|
await trigger.run(row);
|
|
405
426
|
}
|
|
@@ -410,7 +431,11 @@ class Table {
|
|
|
410
431
|
* @type {string}
|
|
411
432
|
*/
|
|
412
433
|
get pk_name() {
|
|
413
|
-
|
|
434
|
+
const pkField = this.fields?.find((f) => f.primary_key)?.name;
|
|
435
|
+
if (!pkField) {
|
|
436
|
+
throw new Error("A primary key field is mandatory");
|
|
437
|
+
}
|
|
438
|
+
return pkField;
|
|
414
439
|
}
|
|
415
440
|
/**
|
|
416
441
|
* Insert row
|
|
@@ -422,16 +447,16 @@ class Table {
|
|
|
422
447
|
await this.getFields();
|
|
423
448
|
const v = await apply_calculated_fields_stored(v_in, this.fields);
|
|
424
449
|
const pk_name = this.pk_name;
|
|
425
|
-
const id = await
|
|
450
|
+
const id = await db_1.default.insert(this.name, v, { pk_name });
|
|
426
451
|
if (this.versioned)
|
|
427
|
-
await
|
|
452
|
+
await db_1.default.insert(this.name + "__history", {
|
|
428
453
|
...v,
|
|
429
454
|
[pk_name]: id,
|
|
430
455
|
_version: 1,
|
|
431
456
|
_userid,
|
|
432
457
|
_time: new Date(),
|
|
433
458
|
});
|
|
434
|
-
|
|
459
|
+
trigger_1.default.runTableTriggers("Insert", this, { [pk_name]: id, ...v });
|
|
435
460
|
return id;
|
|
436
461
|
}
|
|
437
462
|
/**
|
|
@@ -455,7 +480,10 @@ class Table {
|
|
|
455
480
|
*/
|
|
456
481
|
async getFields() {
|
|
457
482
|
if (!this.fields) {
|
|
458
|
-
this.fields = await
|
|
483
|
+
this.fields = await field_1.default.find({ table_id: this.id }, { orderBy: "id" });
|
|
484
|
+
for (let field of this.fields) {
|
|
485
|
+
field.table = this;
|
|
486
|
+
}
|
|
459
487
|
}
|
|
460
488
|
return this.fields;
|
|
461
489
|
}
|
|
@@ -465,12 +493,15 @@ class Table {
|
|
|
465
493
|
*/
|
|
466
494
|
// todo create function that returns history table name for table
|
|
467
495
|
async create_history_table() {
|
|
468
|
-
const schemaPrefix =
|
|
496
|
+
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
469
497
|
const fields = await this.getFields();
|
|
470
|
-
const flds = fields.map((f) => `,"${sqlsanitize(f.name)}" ${f.sql_bare_type}`);
|
|
471
|
-
const pk = fields.find((f) => f.primary_key)
|
|
498
|
+
const flds = fields.map((f) => `,"${(0, internal_1.sqlsanitize)(f.name)}" ${f.sql_bare_type}`);
|
|
499
|
+
const pk = fields.find((f) => f.primary_key)?.name;
|
|
500
|
+
if (!pk) {
|
|
501
|
+
throw new Error("Unable to find a field with a primary key.");
|
|
502
|
+
}
|
|
472
503
|
// create history table
|
|
473
|
-
await
|
|
504
|
+
await db_1.default.query(`create table ${schemaPrefix}"${(0, internal_1.sqlsanitize)(this.name)}__history" (
|
|
474
505
|
_version integer,
|
|
475
506
|
_time timestamp,
|
|
476
507
|
_userid integer
|
|
@@ -483,9 +514,9 @@ class Table {
|
|
|
483
514
|
* @returns {Promise<void>}
|
|
484
515
|
*/
|
|
485
516
|
async drop_history_table() {
|
|
486
|
-
const schemaPrefix =
|
|
487
|
-
await
|
|
488
|
-
drop table ${schemaPrefix}"${sqlsanitize(this.name)}__history";`);
|
|
517
|
+
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
518
|
+
await db_1.default.query(`
|
|
519
|
+
drop table ${schemaPrefix}"${(0, internal_1.sqlsanitize)(this.name)}__history";`);
|
|
489
520
|
}
|
|
490
521
|
/**
|
|
491
522
|
* Rename table
|
|
@@ -494,19 +525,19 @@ class Table {
|
|
|
494
525
|
*/
|
|
495
526
|
async rename(new_name) {
|
|
496
527
|
//in transaction
|
|
497
|
-
if (
|
|
528
|
+
if (db_1.default.isSQLite)
|
|
498
529
|
throw new InvalidAdminAction("Cannot rename table on SQLite");
|
|
499
|
-
const schemaPrefix =
|
|
500
|
-
const client = await
|
|
530
|
+
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
531
|
+
const client = await db_1.default.getClient();
|
|
501
532
|
await client.query(`BEGIN`);
|
|
502
533
|
try {
|
|
503
534
|
//rename table
|
|
504
|
-
await
|
|
535
|
+
await db_1.default.query(`alter table ${schemaPrefix}"${(0, internal_1.sqlsanitize)(this.name)}" rename to "${(0, internal_1.sqlsanitize)(new_name)}";`);
|
|
505
536
|
//change refs
|
|
506
|
-
await
|
|
537
|
+
await db_1.default.query(`update ${schemaPrefix}_sc_fields set reftable_name=$1 where reftable_name=$2`, [(0, internal_1.sqlsanitize)(new_name), (0, internal_1.sqlsanitize)(this.name)]);
|
|
507
538
|
//rename history
|
|
508
539
|
if (this.versioned)
|
|
509
|
-
await
|
|
540
|
+
await db_1.default.query(`alter table ${schemaPrefix}"${(0, internal_1.sqlsanitize)(this.name)}__history" rename to "${(0, internal_1.sqlsanitize)(new_name)}__history";`);
|
|
510
541
|
//1. change record
|
|
511
542
|
await this.update({ name: new_name });
|
|
512
543
|
await client.query(`COMMIT`);
|
|
@@ -529,17 +560,25 @@ class Table {
|
|
|
529
560
|
if (new_table_rec.ownership_field_id === "")
|
|
530
561
|
delete new_table_rec.ownership_field_id;
|
|
531
562
|
const existing = await Table.findOne({ id: this.id });
|
|
563
|
+
if (!existing) {
|
|
564
|
+
throw new Error(`Unable to find table with id: ${this.id}`);
|
|
565
|
+
}
|
|
532
566
|
const { external, fields, ...upd_rec } = new_table_rec;
|
|
533
|
-
await
|
|
567
|
+
await db_1.default.update("_sc_tables", upd_rec, this.id);
|
|
534
568
|
await require("../db/state").getState().refresh_tables();
|
|
535
569
|
const new_table = await Table.findOne({ id: this.id });
|
|
536
|
-
if (new_table
|
|
537
|
-
|
|
570
|
+
if (!new_table) {
|
|
571
|
+
throw new Error(`Unable to find table with id: ${this.id}`);
|
|
538
572
|
}
|
|
539
|
-
else
|
|
540
|
-
|
|
573
|
+
else {
|
|
574
|
+
if (new_table.versioned && !existing.versioned) {
|
|
575
|
+
await new_table.create_history_table();
|
|
576
|
+
}
|
|
577
|
+
else if (!new_table.versioned && existing.versioned) {
|
|
578
|
+
await new_table.drop_history_table();
|
|
579
|
+
}
|
|
580
|
+
Object.assign(this, new_table_rec);
|
|
541
581
|
}
|
|
542
|
-
Object.assign(this, new_table_rec);
|
|
543
582
|
}
|
|
544
583
|
/**
|
|
545
584
|
* Get table history data
|
|
@@ -547,7 +586,7 @@ class Table {
|
|
|
547
586
|
* @returns {Promise<*>}
|
|
548
587
|
*/
|
|
549
588
|
async get_history(id) {
|
|
550
|
-
return await
|
|
589
|
+
return await db_1.default.select(`${(0, internal_1.sqlsanitize)(this.name)}__history`, { id }, { orderBy: "_version" });
|
|
551
590
|
}
|
|
552
591
|
/**
|
|
553
592
|
* Enable constraints
|
|
@@ -568,7 +607,7 @@ class Table {
|
|
|
568
607
|
let rows;
|
|
569
608
|
try {
|
|
570
609
|
const s = await getLines(filePath, 500);
|
|
571
|
-
rows = await
|
|
610
|
+
rows = await (0, csvtojson_1.default)().fromString(s); // todo agrument type unknown
|
|
572
611
|
}
|
|
573
612
|
catch (e) {
|
|
574
613
|
return { error: `Error processing CSV file` };
|
|
@@ -595,14 +634,14 @@ class Table {
|
|
|
595
634
|
type = "String";
|
|
596
635
|
const label = (k.charAt(0).toUpperCase() + k.slice(1)).replace(/_/g, " ");
|
|
597
636
|
//can fail here if: non integer i d, duplicate headers, invalid name
|
|
598
|
-
const fld = new
|
|
599
|
-
name:
|
|
637
|
+
const fld = new field_1.default({
|
|
638
|
+
name: field_1.default.labelToName(k),
|
|
600
639
|
required,
|
|
601
640
|
type,
|
|
602
641
|
table,
|
|
603
642
|
label,
|
|
604
643
|
});
|
|
605
|
-
if (
|
|
644
|
+
if (db_1.default.sqlsanitize(k.toLowerCase()) === "id") {
|
|
606
645
|
if (type !== "Integer") {
|
|
607
646
|
await table.delete();
|
|
608
647
|
return { error: `Columns named "id" must have only integers` };
|
|
@@ -613,14 +652,14 @@ class Table {
|
|
|
613
652
|
}
|
|
614
653
|
continue;
|
|
615
654
|
}
|
|
616
|
-
if (
|
|
655
|
+
if (db_1.default.sqlsanitize(fld.name) === "") {
|
|
617
656
|
await table.delete();
|
|
618
657
|
return {
|
|
619
658
|
error: `Invalid column name ${k} - Use A-Z, a-z, 0-9, _ only`,
|
|
620
659
|
};
|
|
621
660
|
}
|
|
622
661
|
try {
|
|
623
|
-
await
|
|
662
|
+
await field_1.default.create(fld);
|
|
624
663
|
}
|
|
625
664
|
catch (e) {
|
|
626
665
|
await table.delete();
|
|
@@ -628,7 +667,7 @@ class Table {
|
|
|
628
667
|
}
|
|
629
668
|
}
|
|
630
669
|
const parse_res = await table.import_csv_file(filePath);
|
|
631
|
-
if (
|
|
670
|
+
if ((0, common_types_1.instanceOfErrorMsg)(parse_res)) {
|
|
632
671
|
await table.delete();
|
|
633
672
|
return { error: parse_res.error };
|
|
634
673
|
}
|
|
@@ -648,7 +687,7 @@ class Table {
|
|
|
648
687
|
const { readStateStrict } = require("../plugin-helper");
|
|
649
688
|
try {
|
|
650
689
|
const s = await getLines(filePath, 1);
|
|
651
|
-
[headers] = await
|
|
690
|
+
[headers] = await (0, csvtojson_1.default)({
|
|
652
691
|
output: "csv",
|
|
653
692
|
noheader: true,
|
|
654
693
|
}).fromString(s); // todo agrument type unknown
|
|
@@ -680,15 +719,15 @@ class Table {
|
|
|
680
719
|
const colRe = new RegExp(`(${Object.keys(okHeaders).join("|")})`);
|
|
681
720
|
let i = 1;
|
|
682
721
|
let rejects = 0;
|
|
683
|
-
const client =
|
|
684
|
-
const stats = await
|
|
722
|
+
const client = db_1.default.isSQLite ? db_1.default : await db_1.default.getClient();
|
|
723
|
+
const stats = await (0, promises_1.stat)(filePath);
|
|
685
724
|
const fileSizeInMegabytes = stats.size / (1024 * 1024);
|
|
686
725
|
await client.query("BEGIN");
|
|
687
|
-
const readStream =
|
|
726
|
+
const readStream = (0, fs_1.createReadStream)(filePath);
|
|
688
727
|
try {
|
|
689
|
-
if (
|
|
728
|
+
if (db_1.default.copyFrom && fileSizeInMegabytes > 1) {
|
|
690
729
|
let theError;
|
|
691
|
-
const copyres = await
|
|
730
|
+
const copyres = await db_1.default
|
|
692
731
|
.copyFrom(readStream, this.name, fieldNames, client)
|
|
693
732
|
.catch((cate) => {
|
|
694
733
|
theError = cate;
|
|
@@ -704,7 +743,7 @@ class Table {
|
|
|
704
743
|
}
|
|
705
744
|
else {
|
|
706
745
|
await new Promise((resolve, reject) => {
|
|
707
|
-
|
|
746
|
+
(0, csvtojson_1.default)({
|
|
708
747
|
includeColumns: colRe,
|
|
709
748
|
})
|
|
710
749
|
.fromStream(readStream)
|
|
@@ -719,7 +758,7 @@ class Table {
|
|
|
719
758
|
});
|
|
720
759
|
const rowOk = readStateStrict(rec, fields);
|
|
721
760
|
if (rowOk)
|
|
722
|
-
await
|
|
761
|
+
await db_1.default.insert(this.name, rec, {
|
|
723
762
|
noid: true,
|
|
724
763
|
client,
|
|
725
764
|
pk_name,
|
|
@@ -729,7 +768,7 @@ class Table {
|
|
|
729
768
|
}
|
|
730
769
|
catch (e) {
|
|
731
770
|
await client.query("ROLLBACK");
|
|
732
|
-
if (!
|
|
771
|
+
if (!db_1.default.isSQLite)
|
|
733
772
|
await client.release(true);
|
|
734
773
|
reject({ error: `${e.message} in row ${i}` });
|
|
735
774
|
}
|
|
@@ -748,12 +787,12 @@ class Table {
|
|
|
748
787
|
};
|
|
749
788
|
}
|
|
750
789
|
await client.query("COMMIT");
|
|
751
|
-
if (!
|
|
790
|
+
if (!db_1.default.isSQLite)
|
|
752
791
|
await client.release(true);
|
|
753
|
-
|
|
754
|
-
if (
|
|
755
|
-
|
|
756
|
-
|
|
792
|
+
await this.resetSequence();
|
|
793
|
+
if (recalc_stored &&
|
|
794
|
+
this.fields &&
|
|
795
|
+
this.fields.some((f) => f.calculated && f.stored)) {
|
|
757
796
|
await recalculate_for_stored(this);
|
|
758
797
|
}
|
|
759
798
|
return {
|
|
@@ -768,12 +807,12 @@ class Table {
|
|
|
768
807
|
*/
|
|
769
808
|
async import_json_file(filePath, skip_first_data_row) {
|
|
770
809
|
// todo argument type buffer is not assignable for type String...
|
|
771
|
-
const file_rows = JSON.parse(await
|
|
810
|
+
const file_rows = JSON.parse(await (await (0, promises_1.readFile)(filePath)).toString());
|
|
772
811
|
const fields = await this.getFields();
|
|
773
812
|
const pk_name = this.pk_name;
|
|
774
813
|
const { readState } = require("../plugin-helper");
|
|
775
814
|
let i = 1;
|
|
776
|
-
const client =
|
|
815
|
+
const client = db_1.default.isSQLite ? db_1.default : await db_1.default.getClient();
|
|
777
816
|
await client.query("BEGIN");
|
|
778
817
|
for (const rec of file_rows) {
|
|
779
818
|
i += 1;
|
|
@@ -788,21 +827,19 @@ class Table {
|
|
|
788
827
|
});
|
|
789
828
|
try {
|
|
790
829
|
readState(rec, fields);
|
|
791
|
-
await
|
|
830
|
+
await db_1.default.insert(this.name, rec, { noid: true, client, pk_name });
|
|
792
831
|
}
|
|
793
832
|
catch (e) {
|
|
794
833
|
await client.query("ROLLBACK");
|
|
795
|
-
if (!
|
|
834
|
+
if (!db_1.default.isSQLite)
|
|
796
835
|
await client.release(true);
|
|
797
836
|
return { error: `${e.message} in row ${i}` };
|
|
798
837
|
}
|
|
799
838
|
}
|
|
800
839
|
await client.query("COMMIT");
|
|
801
|
-
if (!
|
|
840
|
+
if (!db_1.default.isSQLite)
|
|
802
841
|
await client.release(true);
|
|
803
|
-
|
|
804
|
-
if (db.reset_sequence && pk.type.name === "Integer")
|
|
805
|
-
await db.reset_sequence(this.name);
|
|
842
|
+
await this.resetSequence();
|
|
806
843
|
return {
|
|
807
844
|
success: `Imported ${file_rows.length} rows into table ${this.name}`,
|
|
808
845
|
};
|
|
@@ -819,21 +856,47 @@ class Table {
|
|
|
819
856
|
for (const f of fields) {
|
|
820
857
|
if (f.is_fkey && f.type !== "File") {
|
|
821
858
|
const table = await Table.findOne({ name: f.reftable_name });
|
|
859
|
+
if (!table)
|
|
860
|
+
throw new Error(`Unable to find table '${f.reftable_name}`);
|
|
822
861
|
await table.getFields();
|
|
862
|
+
if (!table.fields)
|
|
863
|
+
throw new Error(`The table '${f.reftable_name} has no fields.`);
|
|
823
864
|
for (const pf of table.fields.filter((f) => !f.calculated || f.stored)) {
|
|
824
865
|
parent_field_list.push(`${f.name}.${pf.name}`);
|
|
825
866
|
if (pf.is_fkey && pf.type !== "File" && allow_double) {
|
|
826
867
|
const table1 = await Table.findOne({ name: pf.reftable_name });
|
|
868
|
+
if (!table1)
|
|
869
|
+
throw new Error(`Unable to find table '${pf.reftable_name}`);
|
|
827
870
|
await table1.getFields();
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
871
|
+
if (!table1.fields)
|
|
872
|
+
throw new Error(`The table '${pf.reftable_name} has no fields.`);
|
|
873
|
+
if (table1.fields)
|
|
874
|
+
for (const gpf of table1.fields.filter((f) => !f.calculated || f.stored)) {
|
|
875
|
+
parent_field_list.push(`${f.name}.${pf.name}.${gpf.name}`);
|
|
876
|
+
}
|
|
831
877
|
parent_relations.push({ key_field: pf, through: f, table: table1 });
|
|
832
878
|
}
|
|
833
879
|
}
|
|
834
880
|
parent_relations.push({ key_field: f, table });
|
|
835
881
|
}
|
|
836
882
|
}
|
|
883
|
+
const o2o_rels = await field_1.default.find({
|
|
884
|
+
reftable_name: this.name,
|
|
885
|
+
is_unique: true,
|
|
886
|
+
});
|
|
887
|
+
for (const relation of o2o_rels) {
|
|
888
|
+
const related_table = await Table.findOne({ id: relation.table_id });
|
|
889
|
+
if (related_table) {
|
|
890
|
+
const relfields = await related_table.getFields();
|
|
891
|
+
for (const relfield of relfields) {
|
|
892
|
+
parent_field_list.push(`${related_table.name}.${relation.name}->${relfield.name}`);
|
|
893
|
+
parent_relations.push({
|
|
894
|
+
key_field: relation,
|
|
895
|
+
ontable: related_table,
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}
|
|
837
900
|
return { parent_relations, parent_field_list };
|
|
838
901
|
}
|
|
839
902
|
/**
|
|
@@ -841,12 +904,15 @@ class Table {
|
|
|
841
904
|
* @returns {Promise<{child_relations: object[], child_field_list: object[]}>}
|
|
842
905
|
*/
|
|
843
906
|
async get_child_relations() {
|
|
844
|
-
const cfields = await
|
|
907
|
+
const cfields = await field_1.default.find({ reftable_name: this.name });
|
|
845
908
|
let child_relations = [];
|
|
846
909
|
let child_field_list = [];
|
|
847
910
|
for (const f of cfields) {
|
|
848
911
|
if (f.is_fkey) {
|
|
849
912
|
const table = await Table.findOne({ id: f.table_id });
|
|
913
|
+
if (!table) {
|
|
914
|
+
throw new Error(`Unable to find table with id: ${f.table_id}`);
|
|
915
|
+
}
|
|
850
916
|
child_field_list.push(`${table.name}.${f.name}`);
|
|
851
917
|
await table.getFields();
|
|
852
918
|
child_relations.push({ key_field: f, table });
|
|
@@ -865,7 +931,7 @@ class Table {
|
|
|
865
931
|
let joinq = "";
|
|
866
932
|
let joinTables = [];
|
|
867
933
|
let joinFields = opts.joinFields || [];
|
|
868
|
-
const schema =
|
|
934
|
+
const schema = db_1.default.getTenantSchemaPrefix();
|
|
869
935
|
fields
|
|
870
936
|
.filter((f) => f.type === "File")
|
|
871
937
|
.forEach((f) => {
|
|
@@ -875,17 +941,29 @@ class Table {
|
|
|
875
941
|
target: `filename`,
|
|
876
942
|
};
|
|
877
943
|
});
|
|
878
|
-
for (const [fldnm, { ref, target, through }] of Object.entries(joinFields)) {
|
|
879
|
-
|
|
944
|
+
for (const [fldnm, { ref, target, through, ontable }] of Object.entries(joinFields)) {
|
|
945
|
+
let reffield;
|
|
946
|
+
if (ontable) {
|
|
947
|
+
const ontableTbl = Table.findOne({ name: ontable });
|
|
948
|
+
if (!ontableTbl)
|
|
949
|
+
throw new InvalidConfiguration(`Related table ${ontable} not found in table ${this.name}`);
|
|
950
|
+
reffield = (await ontableTbl.getFields()).find((f) => f.name === ref);
|
|
951
|
+
}
|
|
952
|
+
else {
|
|
953
|
+
reffield = fields.find((f) => f.name === ref);
|
|
954
|
+
}
|
|
880
955
|
if (!reffield)
|
|
881
956
|
throw new InvalidConfiguration(`Key field ${ref} not found in table ${this.name}`);
|
|
882
|
-
const reftable = reffield.reftable_name;
|
|
957
|
+
const reftable = ontable || reffield.reftable_name;
|
|
883
958
|
if (!reftable)
|
|
884
959
|
throw new InvalidConfiguration(`Field ${ref} is not a key field`);
|
|
885
|
-
const jtNm = `${sqlsanitize(reftable)}_jt_${sqlsanitize(ref)}`;
|
|
960
|
+
const jtNm = `${(0, internal_1.sqlsanitize)(reftable)}_jt_${(0, internal_1.sqlsanitize)(ref)}`;
|
|
886
961
|
if (!joinTables.includes(jtNm)) {
|
|
887
962
|
joinTables.push(jtNm);
|
|
888
|
-
|
|
963
|
+
if (ontable)
|
|
964
|
+
joinq += ` left join ${schema}"${(0, internal_1.sqlsanitize)(reftable)}" ${jtNm} on ${jtNm}."${(0, internal_1.sqlsanitize)(ref)}"=a."${reffield.refname}"`;
|
|
965
|
+
else
|
|
966
|
+
joinq += ` left join ${schema}"${(0, internal_1.sqlsanitize)(reftable)}" ${jtNm} on ${jtNm}."${reffield.refname}"=a."${(0, internal_1.sqlsanitize)(ref)}"`;
|
|
889
967
|
}
|
|
890
968
|
if (through) {
|
|
891
969
|
const throughTable = await Table.findOne({
|
|
@@ -898,48 +976,42 @@ class Table {
|
|
|
898
976
|
if (!throughRefField)
|
|
899
977
|
throw new InvalidConfiguration(`Reference field field ${through} not found in table ${throughTable.name}`);
|
|
900
978
|
const finalTable = throughRefField.reftable_name;
|
|
901
|
-
const jtNm1 = `${sqlsanitize(reftable)}_jt_${sqlsanitize(through)}_jt_${sqlsanitize(ref)}`;
|
|
979
|
+
const jtNm1 = `${(0, internal_1.sqlsanitize)(reftable)}_jt_${(0, internal_1.sqlsanitize)(through)}_jt_${(0, internal_1.sqlsanitize)(ref)}`;
|
|
902
980
|
if (!joinTables.includes(jtNm1)) {
|
|
981
|
+
if (!finalTable)
|
|
982
|
+
throw new Error("Unable to build a joind without a reftable_name.");
|
|
903
983
|
joinTables.push(jtNm1);
|
|
904
|
-
joinq += ` left join ${schema}"${sqlsanitize(finalTable)}" ${jtNm1} on ${jtNm1}.id=${jtNm}."${sqlsanitize(through)}"`;
|
|
984
|
+
joinq += ` left join ${schema}"${(0, internal_1.sqlsanitize)(finalTable)}" ${jtNm1} on ${jtNm1}.id=${jtNm}."${(0, internal_1.sqlsanitize)(through)}"`;
|
|
905
985
|
}
|
|
906
|
-
fldNms.push(`${jtNm1}.${sqlsanitize(target)} as ${sqlsanitize(fldnm)}`);
|
|
986
|
+
fldNms.push(`${jtNm1}.${(0, internal_1.sqlsanitize)(target)} as ${(0, internal_1.sqlsanitize)(fldnm)}`);
|
|
907
987
|
}
|
|
908
988
|
else {
|
|
909
|
-
fldNms.push(`${jtNm}.${sqlsanitize(target)} as ${sqlsanitize(fldnm)}`);
|
|
989
|
+
fldNms.push(`${jtNm}.${(0, internal_1.sqlsanitize)(target)} as ${(0, internal_1.sqlsanitize)(fldnm)}`);
|
|
910
990
|
}
|
|
911
991
|
}
|
|
912
992
|
for (const f of fields.filter((f) => !f.calculated || f.stored)) {
|
|
913
|
-
fldNms.push(`a."${sqlsanitize(f.name)}"`);
|
|
993
|
+
fldNms.push(`a."${(0, internal_1.sqlsanitize)(f.name)}"`);
|
|
914
994
|
}
|
|
915
995
|
Object.entries(opts.aggregations || {}).forEach(([fldnm, { table, ref, field, where, aggregate, subselect }]) => {
|
|
916
996
|
if (aggregate.startsWith("Latest ")) {
|
|
917
997
|
const dateField = aggregate.replace("Latest ", "");
|
|
918
|
-
fldNms.push(`(select "${sqlsanitize(field)}" from ${schema}"${sqlsanitize(table)}" where ${dateField}=(select max(${dateField}) from ${schema}"${sqlsanitize(table)}" where "${sqlsanitize(ref)}"=a.id${where ? ` and ${where}` : ""}) and "${sqlsanitize(ref)}"=a.id) ${sqlsanitize(fldnm)}`);
|
|
998
|
+
fldNms.push(`(select "${(0, internal_1.sqlsanitize)(field)}" from ${schema}"${(0, internal_1.sqlsanitize)(table)}" where ${dateField}=(select max(${dateField}) from ${schema}"${(0, internal_1.sqlsanitize)(table)}" where "${(0, internal_1.sqlsanitize)(ref)}"=a.id${where ? ` and ${where}` : ""}) and "${(0, internal_1.sqlsanitize)(ref)}"=a.id) ${(0, internal_1.sqlsanitize)(fldnm)}`);
|
|
919
999
|
}
|
|
920
1000
|
else if (subselect)
|
|
921
|
-
fldNms.push(`(select ${sqlsanitize(aggregate)}(${field ? `"${sqlsanitize(field)}"` : "*"}) from ${schema}"${sqlsanitize(table)}" where ${sqlsanitize(ref)} in (select "${subselect.field}" from ${schema}"${subselect.table.name}" where "${subselect.whereField}"=a.id)) ${sqlsanitize(fldnm)}`);
|
|
1001
|
+
fldNms.push(`(select ${(0, internal_1.sqlsanitize)(aggregate)}(${field ? `"${(0, internal_1.sqlsanitize)(field)}"` : "*"}) from ${schema}"${(0, internal_1.sqlsanitize)(table)}" where ${(0, internal_1.sqlsanitize)(ref)} in (select "${subselect.field}" from ${schema}"${subselect.table.name}" where "${subselect.whereField}"=a.id)) ${(0, internal_1.sqlsanitize)(fldnm)}`);
|
|
922
1002
|
else
|
|
923
|
-
fldNms.push(`(select ${sqlsanitize(aggregate)}(${field ? `"${sqlsanitize(field)}"` : "*"}) from ${schema}"${sqlsanitize(table)}" where "${sqlsanitize(ref)}"=a.id${where ? ` and ${where}` : ""}) ${sqlsanitize(fldnm)}`);
|
|
1003
|
+
fldNms.push(`(select ${(0, internal_1.sqlsanitize)(aggregate)}(${field ? `"${(0, internal_1.sqlsanitize)(field)}"` : "*"}) from ${schema}"${(0, internal_1.sqlsanitize)(table)}" where "${(0, internal_1.sqlsanitize)(ref)}"=a.id${where ? ` and ${where}` : ""}) ${(0, internal_1.sqlsanitize)(fldnm)}`);
|
|
924
1004
|
});
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
Object.keys(opts.where).forEach((k) => {
|
|
928
|
-
if (k === "_fts")
|
|
929
|
-
whereObj[k] = { table: "a", ...opts.where[k] };
|
|
930
|
-
else
|
|
931
|
-
whereObj[`a."${k}"`] = opts.where[k];
|
|
932
|
-
});
|
|
933
|
-
}
|
|
934
|
-
const { where, values } = mkWhere(whereObj, db.isSQLite);
|
|
1005
|
+
const whereObj = (0, utils_1.prefixFieldsInWhere)(opts.where, "a");
|
|
1006
|
+
const { where, values } = (0, internal_1.mkWhere)(whereObj, db_1.default.isSQLite);
|
|
935
1007
|
const selectopts = {
|
|
936
1008
|
limit: opts.limit,
|
|
937
1009
|
orderBy: opts.orderBy &&
|
|
938
|
-
(opts.orderBy
|
|
1010
|
+
((0, internal_1.orderByIsObject)(opts.orderBy) ? opts.orderBy : "a." + opts.orderBy),
|
|
939
1011
|
orderDesc: opts.orderDesc,
|
|
940
1012
|
offset: opts.offset,
|
|
941
1013
|
};
|
|
942
|
-
const sql = `SELECT ${fldNms.join()} FROM ${schema}"${sqlsanitize(this.name)}" a ${joinq} ${where} ${mkSelectOptions(selectopts)}`;
|
|
1014
|
+
const sql = `SELECT ${fldNms.join()} FROM ${schema}"${(0, internal_1.sqlsanitize)(this.name)}" a ${joinq} ${where} ${(0, internal_1.mkSelectOptions)(selectopts)}`;
|
|
943
1015
|
return { sql, values };
|
|
944
1016
|
}
|
|
945
1017
|
/**
|
|
@@ -949,58 +1021,41 @@ class Table {
|
|
|
949
1021
|
async getJoinedRows(opts = {}) {
|
|
950
1022
|
const fields = await this.getFields();
|
|
951
1023
|
const { sql, values } = await this.getJoinedQuery(opts);
|
|
952
|
-
const res = await
|
|
1024
|
+
const res = await db_1.default.query(sql, values);
|
|
953
1025
|
return apply_calculated_fields(res.rows, fields);
|
|
954
1026
|
}
|
|
1027
|
+
async slug_options() {
|
|
1028
|
+
const fields = await this.getFields();
|
|
1029
|
+
const unique_fields = fields.filter((f) => f.is_unique);
|
|
1030
|
+
const opts = [];
|
|
1031
|
+
unique_fields.forEach((f) => {
|
|
1032
|
+
const label = (0, common_types_1.instanceOfType)(f.type) && f.type.name === "String"
|
|
1033
|
+
? `/slugify-${f.name}`
|
|
1034
|
+
: `/:${f.name}`;
|
|
1035
|
+
opts.push({
|
|
1036
|
+
label,
|
|
1037
|
+
steps: [
|
|
1038
|
+
{
|
|
1039
|
+
field: f.name,
|
|
1040
|
+
unique: true,
|
|
1041
|
+
transform: (0, common_types_1.instanceOfType)(f.type) && f.type.name === "String"
|
|
1042
|
+
? "slugify"
|
|
1043
|
+
: null,
|
|
1044
|
+
},
|
|
1045
|
+
],
|
|
1046
|
+
});
|
|
1047
|
+
});
|
|
1048
|
+
opts.unshift({ label: "", steps: [] });
|
|
1049
|
+
return opts;
|
|
1050
|
+
}
|
|
1051
|
+
static async allSlugOptions() {
|
|
1052
|
+
const tables = await Table.find({});
|
|
1053
|
+
const options = {};
|
|
1054
|
+
for (const table of tables) {
|
|
1055
|
+
options[table.name] = await table.slug_options();
|
|
1056
|
+
}
|
|
1057
|
+
return options;
|
|
1058
|
+
}
|
|
955
1059
|
}
|
|
956
|
-
/**
|
|
957
|
-
* Table contract
|
|
958
|
-
* @type {{variables: {name: ((function(*=): *)|*)}, methods: {updateRow: ((function(*=): *)|*), get_history: ((function(*=): *)|*), tryUpdateRow: ((function(*=): *)|*), deleteRows: ((function(*=): *)|*), update: ((function(*=): *)|*), getRows: ((function(*=): *)|*), getRow: ((function(*=): *)|*), delete: ((function(*=): *)|*), get_parent_relations: ((function(*=): *)|*), get_child_relations: ((function(*=): *)|*), tryInsertRow: ((function(*=): *)|*), getFields: ((function(*=): *)|*), insertRow: ((function(*=): *)|*), toggleBool: ((function(*=): *)|*), getJoinedRows: ((function(*=): *)|*), countRows: ((function(*=): *)|*), distinctValues: ((function(*=): *)|*), sql_name: ((function(*=): *)|*), import_csv_file: ((function(*=): *)|*)}, static_methods: {find: ((function(*=): *)|*), create_from_csv: ((function(*=): *)|*), findOne: ((function(*=): *)|*), find_with_external: ((function(*=): *)|*), create: ((function(*=): *)|*)}, constructs: {name: ((function(*=): *)|*)}}}
|
|
959
|
-
*/
|
|
960
|
-
Table.contract = {
|
|
961
|
-
constructs: { name: is.str },
|
|
962
|
-
variables: { name: is.str },
|
|
963
|
-
methods: {
|
|
964
|
-
delete: is.fun([], is.promise(is.eq(undefined))),
|
|
965
|
-
update: is.fun(is.obj(), is.promise(is.eq(undefined))),
|
|
966
|
-
deleteRows: is.fun(is.obj(), is.promise(is.eq(undefined))),
|
|
967
|
-
getRow: is.fun(is.maybe(is.obj()), is.promise(is.maybe(is.obj()))),
|
|
968
|
-
getRows: is.fun(is.maybe(is.obj()), is.promise(is.array(is.obj()))),
|
|
969
|
-
countRows: is.fun(is.maybe(is.obj()), is.promise(is.posint)),
|
|
970
|
-
updateRow: is.fun([is.obj(), is.any], is.promise(is.eq(undefined))),
|
|
971
|
-
toggleBool: is.fun([is.any, is.str], is.promise(is.eq(undefined))),
|
|
972
|
-
insertRow: is.fun(is.obj(), is.promise(is.any)),
|
|
973
|
-
get_history: is.fun(is.posint, is.promise(is.array(is.obj()))),
|
|
974
|
-
distinctValues: is.fun(is.str, is.promise(is.array(is.any))),
|
|
975
|
-
tryInsertRow: is.fun([is.obj(), is.maybe(is.posint)], is.promise(is.or(is.obj({ error: is.str }), is.obj({ success: is.any })))),
|
|
976
|
-
tryUpdateRow: is.fun([is.obj(), is.any, is.maybe(is.posint)], is.promise(is.or(is.obj({ error: is.str }), is.obj({ success: is.eq(true) })))),
|
|
977
|
-
sql_name: is.getter(is.str),
|
|
978
|
-
getFields: is.fun([], is.promise(is.array(is.class("Field")))),
|
|
979
|
-
get_parent_relations: is.fun([], is.promise(is.obj({
|
|
980
|
-
parent_relations: is.array(is.obj({
|
|
981
|
-
key_field: is.class("Field"),
|
|
982
|
-
table: is.class("Table"),
|
|
983
|
-
})),
|
|
984
|
-
parent_field_list: is.array(is.str),
|
|
985
|
-
}))),
|
|
986
|
-
get_child_relations: is.fun([], is.promise(is.obj({
|
|
987
|
-
child_relations: is.array(is.obj({
|
|
988
|
-
key_field: is.class("Field"),
|
|
989
|
-
table: is.class("Table"),
|
|
990
|
-
})),
|
|
991
|
-
child_field_list: is.array(is.str),
|
|
992
|
-
}))),
|
|
993
|
-
import_csv_file: is.fun(is.str, is.promise(is.or(is.obj({ success: is.str }), is.obj({ error: is.str })))),
|
|
994
|
-
getJoinedRows: is.fun(is.maybe(is_table_query), is.promise(is.array(is.obj({})))),
|
|
995
|
-
},
|
|
996
|
-
static_methods: {
|
|
997
|
-
find: is.fun([is.maybe(is.obj()), is.maybe(is.obj())], is.promise(is.array(is.class("Table")))),
|
|
998
|
-
find_with_external: is.fun([is.maybe(is.obj()), is.maybe(is.obj())], is.promise(is.array(is.or(is.class("Table"), is.obj({ external: is.eq(true) }))))),
|
|
999
|
-
findOne: is.fun(is.or(is.obj(), is.str, is.posint), is.maybe(is.or(is.class("Table"), is.obj({ external: is.eq(true) })))),
|
|
1000
|
-
create: is.fun(is.str, is.promise(is.class("Table"))),
|
|
1001
|
-
create_from_csv: is.fun([is.str, is.str], is.promise(is.or(is.obj({ success: is.str, table: is.class("Table") }), is.obj({ error: is.str })))),
|
|
1002
|
-
//update: is.fun([is.posint, is.obj({})], is.promise(is.eq(undefined)))
|
|
1003
|
-
},
|
|
1004
|
-
};
|
|
1005
1060
|
module.exports = Table;
|
|
1006
1061
|
//# sourceMappingURL=table.js.map
|