@saltcorn/data 0.8.2 → 0.8.3-alpha.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/dist/base-plugin/actions.d.ts.map +1 -1
- package/dist/base-plugin/actions.js +5 -3
- package/dist/base-plugin/actions.js.map +1 -1
- package/dist/base-plugin/index.d.ts +115 -122
- 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 +11 -3
- package/dist/base-plugin/types.js.map +1 -1
- package/dist/base-plugin/viewtemplates/edit.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/edit.js +24 -7
- 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 +16 -1
- package/dist/base-plugin/viewtemplates/feed.js.map +1 -1
- package/dist/base-plugin/viewtemplates/list.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/list.js +5 -15
- package/dist/base-plugin/viewtemplates/list.js.map +1 -1
- package/dist/base-plugin/viewtemplates/listshowlist.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/listshowlist.js +5 -1
- package/dist/base-plugin/viewtemplates/listshowlist.js.map +1 -1
- package/dist/base-plugin/viewtemplates/room.d.ts +1 -1
- package/dist/base-plugin/viewtemplates/room.js +2 -2
- package/dist/base-plugin/viewtemplates/room.js.map +1 -1
- package/dist/base-plugin/viewtemplates/show.d.ts +0 -7
- package/dist/base-plugin/viewtemplates/show.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/show.js +35 -32
- package/dist/base-plugin/viewtemplates/show.js.map +1 -1
- package/dist/base-plugin/viewtemplates/viewable_fields.js +1 -1
- package/dist/base-plugin/viewtemplates/viewable_fields.js.map +1 -1
- package/dist/db/fixtures.d.ts.map +1 -1
- package/dist/db/fixtures.js +167 -0
- package/dist/db/fixtures.js.map +1 -1
- package/dist/db/state.js +1 -1
- package/dist/db/state.js.map +1 -1
- package/dist/diagram/cy_generate_utils.d.ts.map +1 -1
- package/dist/diagram/cy_generate_utils.js +6 -0
- package/dist/diagram/cy_generate_utils.js.map +1 -1
- package/dist/diagram/node_extract_utils.js +14 -5
- package/dist/diagram/node_extract_utils.js.map +1 -1
- package/dist/diagram/nodes/node.d.ts +2 -2
- package/dist/diagram/nodes/node.d.ts.map +1 -1
- package/dist/diagram/nodes/node.js +2 -2
- package/dist/diagram/nodes/node.js.map +1 -1
- package/dist/diagram/nodes/trigger_node.d.ts +1 -3
- package/dist/diagram/nodes/trigger_node.d.ts.map +1 -1
- package/dist/diagram/nodes/trigger_node.js +3 -3
- package/dist/diagram/nodes/trigger_node.js.map +1 -1
- package/dist/migrations/202301261705.d.ts +3 -0
- package/dist/migrations/202301261705.d.ts.map +1 -0
- package/dist/migrations/202301261705.js +5 -0
- package/dist/migrations/202301261705.js.map +1 -0
- package/dist/models/config.d.ts.map +1 -1
- package/dist/models/config.js +1 -0
- package/dist/models/config.js.map +1 -1
- package/dist/models/email.d.ts +2 -0
- package/dist/models/email.d.ts.map +1 -1
- package/dist/models/eventlog.d.ts.map +1 -1
- package/dist/models/eventlog.js +7 -2
- package/dist/models/eventlog.js.map +1 -1
- package/dist/models/expression.d.ts +2 -1
- package/dist/models/expression.d.ts.map +1 -1
- package/dist/models/expression.js +42 -0
- package/dist/models/expression.js.map +1 -1
- package/dist/models/field.d.ts.map +1 -1
- package/dist/models/field.js +15 -3
- package/dist/models/field.js.map +1 -1
- package/dist/models/random.d.ts.map +1 -1
- package/dist/models/random.js +5 -4
- package/dist/models/random.js.map +1 -1
- package/dist/models/table.d.ts +40 -8
- package/dist/models/table.d.ts.map +1 -1
- package/dist/models/table.js +351 -46
- package/dist/models/table.js.map +1 -1
- package/dist/models/user.d.ts.map +1 -1
- package/dist/models/user.js +5 -0
- package/dist/models/user.js.map +1 -1
- package/dist/models/workflow.d.ts.map +1 -1
- package/dist/models/workflow.js +31 -0
- package/dist/models/workflow.js.map +1 -1
- package/dist/plugin-helper.d.ts +12 -8
- package/dist/plugin-helper.d.ts.map +1 -1
- package/dist/plugin-helper.js +45 -47
- package/dist/plugin-helper.js.map +1 -1
- package/dist/tests/actions.test.js +2 -2
- package/dist/tests/actions.test.js.map +1 -1
- package/dist/tests/auth.test.js +193 -46
- package/dist/tests/auth.test.js.map +1 -1
- package/dist/tests/calc.test.js +2 -2
- package/dist/tests/calc.test.js.map +1 -1
- package/dist/tests/db.test.d.ts.map +1 -1
- package/dist/tests/db.test.js.map +1 -1
- package/dist/tests/exact_views.test.js +1 -1
- package/dist/tests/exact_views.test.js.map +1 -1
- package/dist/tests/field.test.js +3 -1
- package/dist/tests/field.test.js.map +1 -1
- package/dist/tests/mocks.d.ts +6 -1
- package/dist/tests/mocks.d.ts.map +1 -1
- package/dist/tests/mocks.js +23 -4
- package/dist/tests/mocks.js.map +1 -1
- package/dist/tests/remote_query_helper.d.ts.map +1 -1
- package/dist/tests/remote_query_helper.js +3 -0
- package/dist/tests/remote_query_helper.js.map +1 -1
- package/dist/tests/table.test.js +36 -9
- package/dist/tests/table.test.js.map +1 -1
- package/dist/tests/user.test.js +8 -0
- package/dist/tests/user.test.js.map +1 -1
- package/dist/tests/view.test.js +1 -1
- package/dist/tests/view.test.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js.map +1 -1
- package/package.json +10 -10
package/dist/models/table.js
CHANGED
|
@@ -37,7 +37,7 @@ const field_1 = __importDefault(require("./field"));
|
|
|
37
37
|
const common_types_1 = require("@saltcorn/types/common_types");
|
|
38
38
|
const trigger_1 = __importDefault(require("./trigger"));
|
|
39
39
|
const expression_1 = __importDefault(require("./expression"));
|
|
40
|
-
const { apply_calculated_fields, apply_calculated_fields_stored, recalculate_for_stored, get_expression_function, freeVariables, } = expression_1.default;
|
|
40
|
+
const { apply_calculated_fields, apply_calculated_fields_stored, recalculate_for_stored, get_expression_function, freeVariables, add_free_variables_to_joinfields, } = expression_1.default;
|
|
41
41
|
const csvtojson_1 = __importDefault(require("csvtojson"));
|
|
42
42
|
const moment_1 = __importDefault(require("moment"));
|
|
43
43
|
const fs_1 = require("fs");
|
|
@@ -45,9 +45,7 @@ const promises_1 = require("fs/promises");
|
|
|
45
45
|
const utils_1 = __importDefault(require("../utils"));
|
|
46
46
|
//import { num_between } from "@saltcorn/types/generators";
|
|
47
47
|
//import { devNull } from "os";
|
|
48
|
-
const { prefixFieldsInWhere } = utils_1.default;
|
|
49
|
-
const { InvalidConfiguration, InvalidAdminAction, satisfies, structuredClone, getLines, } = require("../utils");
|
|
50
|
-
//import type Tag from "../models/tag";
|
|
48
|
+
const { prefixFieldsInWhere, InvalidConfiguration, InvalidAdminAction, satisfies, structuredClone, getLines, mergeIntoWhere, } = utils_1.default;
|
|
51
49
|
/**
|
|
52
50
|
* Transponce Objects
|
|
53
51
|
* TODO more detailed explanation
|
|
@@ -114,8 +112,11 @@ class Table {
|
|
|
114
112
|
this.is_user_group = !!o.is_user_group;
|
|
115
113
|
this.external = false;
|
|
116
114
|
this.description = o.description;
|
|
117
|
-
if (o.fields)
|
|
118
|
-
|
|
115
|
+
if (!o.fields) {
|
|
116
|
+
console.trace("missing fields", o);
|
|
117
|
+
throw new Error("missing fields");
|
|
118
|
+
}
|
|
119
|
+
this.fields = o.fields.map((f) => new field_1.default(f));
|
|
119
120
|
}
|
|
120
121
|
/**
|
|
121
122
|
*
|
|
@@ -160,7 +161,13 @@ class Table {
|
|
|
160
161
|
return getState().tables.map((t) => new Table(t));
|
|
161
162
|
}
|
|
162
163
|
const tbls = await db_1.default.select("_sc_tables", where, selectopts);
|
|
163
|
-
|
|
164
|
+
const flds = await db_1.default.select("_sc_fields", db_1.default.isSQLite ? {} : { table_id: { in: tbls.map((t) => t.id) } }, selectopts);
|
|
165
|
+
return tbls.map((t) => {
|
|
166
|
+
t.fields = flds
|
|
167
|
+
.filter((f) => f.table_id === t.id)
|
|
168
|
+
.map((f) => new field_1.default(f));
|
|
169
|
+
return new Table(t);
|
|
170
|
+
});
|
|
164
171
|
}
|
|
165
172
|
/**
|
|
166
173
|
* Find Tables including external tables
|
|
@@ -179,7 +186,15 @@ class Table {
|
|
|
179
186
|
if (external !== true) {
|
|
180
187
|
//do include db tables
|
|
181
188
|
const tbls = await db_1.default.select("_sc_tables", where, selectopts);
|
|
182
|
-
|
|
189
|
+
const flds = await db_1.default.select("_sc_fields", db_1.default.isSQLite
|
|
190
|
+
? {}
|
|
191
|
+
: { table_id: { in: tbls.map((t) => t.id) } }, selectopts);
|
|
192
|
+
dbs = tbls.map((t) => {
|
|
193
|
+
t.fields = flds
|
|
194
|
+
.filter((f) => f.table_id === t.id)
|
|
195
|
+
.map((f) => new field_1.default(f));
|
|
196
|
+
return new Table(t);
|
|
197
|
+
});
|
|
183
198
|
}
|
|
184
199
|
return [...dbs, ...externals];
|
|
185
200
|
}
|
|
@@ -242,7 +257,7 @@ class Table {
|
|
|
242
257
|
if (ofield)
|
|
243
258
|
opts.push({
|
|
244
259
|
label: `Inherit ${field.label}`,
|
|
245
|
-
value: `Fml:${field.name}
|
|
260
|
+
value: `Fml:${field.name}?.${ofield.name}===user.id`,
|
|
246
261
|
});
|
|
247
262
|
}
|
|
248
263
|
if (refTable?.ownership_formula) {
|
|
@@ -256,7 +271,7 @@ class Table {
|
|
|
256
271
|
if (fldNms.has(path[0])) {
|
|
257
272
|
opts.push({
|
|
258
273
|
label: `Inherit ${field.label}`,
|
|
259
|
-
value: `Fml:${field.name}
|
|
274
|
+
value: `Fml:${field.name}?.${refFml}`,
|
|
260
275
|
});
|
|
261
276
|
}
|
|
262
277
|
}
|
|
@@ -271,7 +286,7 @@ class Table {
|
|
|
271
286
|
});
|
|
272
287
|
}
|
|
273
288
|
else {
|
|
274
|
-
const fml = refFml.replace(`.includes(${ref})`, `.includes(${field.name}
|
|
289
|
+
const fml = refFml.replace(`.includes(${ref})`, `.includes(${field.name}?.${ref})`);
|
|
275
290
|
opts.push({
|
|
276
291
|
label: `Inherit ${field.label}`,
|
|
277
292
|
value: `Fml:${fml}`,
|
|
@@ -333,15 +348,32 @@ class Table {
|
|
|
333
348
|
ownership_formula: options.ownership_formula,
|
|
334
349
|
description: options.description || "",
|
|
335
350
|
};
|
|
351
|
+
let pk_fld_id;
|
|
336
352
|
if (!id) {
|
|
337
353
|
// insert table definition into _sc_tables
|
|
338
354
|
id = await db_1.default.insert("_sc_tables", tblrow);
|
|
339
355
|
// add primary key column ID
|
|
340
|
-
await db_1.default.query(`insert into ${schema}_sc_fields(table_id, name, label, type, attributes, required, is_unique,primary_key)
|
|
341
|
-
values($1,'id','ID','Integer', '{}', true, true, true)`, [id]);
|
|
356
|
+
const insfldres = await db_1.default.query(`insert into ${schema}_sc_fields(table_id, name, label, type, attributes, required, is_unique,primary_key)
|
|
357
|
+
values($1,'id','ID','Integer', '{}', true, true, true) returning id`, [id]);
|
|
358
|
+
pk_fld_id = insfldres.rows[0].id;
|
|
342
359
|
}
|
|
343
360
|
// create table
|
|
344
|
-
const table = new Table({
|
|
361
|
+
const table = new Table({
|
|
362
|
+
...tblrow,
|
|
363
|
+
id,
|
|
364
|
+
fields: [
|
|
365
|
+
new field_1.default({
|
|
366
|
+
type: "Integer",
|
|
367
|
+
name: "id",
|
|
368
|
+
label: "ID",
|
|
369
|
+
primary_key: true,
|
|
370
|
+
required: true,
|
|
371
|
+
is_unique: true,
|
|
372
|
+
table_id: id,
|
|
373
|
+
id: pk_fld_id,
|
|
374
|
+
}),
|
|
375
|
+
],
|
|
376
|
+
});
|
|
345
377
|
// create table history
|
|
346
378
|
if (table.versioned)
|
|
347
379
|
await table.create_history_table();
|
|
@@ -398,20 +430,51 @@ class Table {
|
|
|
398
430
|
pk.type.name === "Integer")
|
|
399
431
|
await db_1.default.reset_sequence(this.name);
|
|
400
432
|
}
|
|
433
|
+
updateWhereWithOwnership(where, fields, user) {
|
|
434
|
+
const role = user?.role_id;
|
|
435
|
+
if (role &&
|
|
436
|
+
role > this.min_role_write &&
|
|
437
|
+
((!this.ownership_field_id && !this.ownership_formula) || role === 10))
|
|
438
|
+
return { notAuthorized: true };
|
|
439
|
+
if (user &&
|
|
440
|
+
role < 10 &&
|
|
441
|
+
role > this.min_role_write &&
|
|
442
|
+
this.ownership_field_id) {
|
|
443
|
+
const owner_field = fields.find((f) => f.id === this.ownership_field_id);
|
|
444
|
+
if (!owner_field)
|
|
445
|
+
throw new Error(`Owner field in table ${this.name} not found`);
|
|
446
|
+
mergeIntoWhere(where, {
|
|
447
|
+
[owner_field.name]: user.id,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
}
|
|
401
451
|
/**
|
|
402
452
|
* Delete rows from table
|
|
403
453
|
* @param where - condition
|
|
404
454
|
* @returns {Promise<void>}
|
|
405
455
|
*/
|
|
406
|
-
async deleteRows(where) {
|
|
456
|
+
async deleteRows(where, user) {
|
|
407
457
|
// get triggers on delete
|
|
408
458
|
const triggers = await trigger_1.default.getTableTriggers("Delete", this);
|
|
409
459
|
const fields = await this.getFields();
|
|
460
|
+
if (this.updateWhereWithOwnership(where, fields, user)?.notAuthorized) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
let rows;
|
|
464
|
+
if (user && user.role_id > this.min_role_write && this.ownership_formula) {
|
|
465
|
+
rows = await this.getJoinedRows({
|
|
466
|
+
where,
|
|
467
|
+
forUser: user,
|
|
468
|
+
});
|
|
469
|
+
}
|
|
410
470
|
const deleteFileFields = fields.filter((f) => f.type === "File" && f.attributes?.also_delete_file);
|
|
411
471
|
const deleteFiles = [];
|
|
412
472
|
if (triggers.length > 0 || deleteFileFields.length > 0) {
|
|
413
473
|
const File = require("./file");
|
|
414
|
-
|
|
474
|
+
if (!rows)
|
|
475
|
+
rows = await this.getJoinedRows({
|
|
476
|
+
where,
|
|
477
|
+
});
|
|
415
478
|
for (const trigger of triggers) {
|
|
416
479
|
for (const row of rows) {
|
|
417
480
|
// run triggers on delete
|
|
@@ -427,7 +490,12 @@ class Table {
|
|
|
427
490
|
}
|
|
428
491
|
}
|
|
429
492
|
}
|
|
430
|
-
|
|
493
|
+
if (rows)
|
|
494
|
+
await db_1.default.deleteWhere(this.name, {
|
|
495
|
+
[this.pk_name]: { in: rows.map((r) => r[this.pk_name]) },
|
|
496
|
+
});
|
|
497
|
+
else
|
|
498
|
+
await db_1.default.deleteWhere(this.name, where);
|
|
431
499
|
await this.resetSequence();
|
|
432
500
|
for (const file of deleteFiles) {
|
|
433
501
|
await file.delete();
|
|
@@ -454,10 +522,30 @@ class Table {
|
|
|
454
522
|
* @returns {Promise<null|*>}
|
|
455
523
|
*/
|
|
456
524
|
async getRow(where = {}, selopts = {}) {
|
|
457
|
-
await this.getFields();
|
|
458
|
-
const
|
|
525
|
+
const fields = await this.getFields();
|
|
526
|
+
const { forUser, forPublic, ...selopts1 } = selopts;
|
|
527
|
+
const role = forUser ? forUser.role_id : forPublic ? 10 : null;
|
|
528
|
+
const row = await db_1.default.selectMaybeOne(this.name, where, selopts1);
|
|
459
529
|
if (!row || !this.fields)
|
|
460
530
|
return null;
|
|
531
|
+
if (role && role > this.min_role_read) {
|
|
532
|
+
//check ownership
|
|
533
|
+
if (forPublic)
|
|
534
|
+
return null;
|
|
535
|
+
else if (this.ownership_field_id) {
|
|
536
|
+
const owner_field = fields.find((f) => f.id === this.ownership_field_id);
|
|
537
|
+
if (!owner_field)
|
|
538
|
+
throw new Error(`Owner field in table ${this.name} not found`);
|
|
539
|
+
if (row[owner_field.name] !== forUser.id)
|
|
540
|
+
return null;
|
|
541
|
+
}
|
|
542
|
+
else if (this.ownership_formula) {
|
|
543
|
+
if (!this.is_owner(forUser, row))
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
else
|
|
547
|
+
return null; //no ownership
|
|
548
|
+
}
|
|
461
549
|
return apply_calculated_fields([this.readFromDB(row)], this.fields)[0];
|
|
462
550
|
}
|
|
463
551
|
/**
|
|
@@ -467,10 +555,30 @@ class Table {
|
|
|
467
555
|
* @returns {Promise<void>}
|
|
468
556
|
*/
|
|
469
557
|
async getRows(where = {}, selopts = {}) {
|
|
470
|
-
await this.getFields();
|
|
471
|
-
const rows = await db_1.default.select(this.name, where, selopts);
|
|
558
|
+
const fields = await this.getFields();
|
|
472
559
|
if (!this.fields)
|
|
473
560
|
return [];
|
|
561
|
+
const { forUser, forPublic, ...selopts1 } = selopts;
|
|
562
|
+
const role = forUser ? forUser.role_id : forPublic ? 10 : null;
|
|
563
|
+
if (role &&
|
|
564
|
+
this.updateWhereWithOwnership(where, fields, forUser || { role_id: 10 })
|
|
565
|
+
?.notAuthorized) {
|
|
566
|
+
return [];
|
|
567
|
+
}
|
|
568
|
+
let rows = await db_1.default.select(this.name, where, selopts1);
|
|
569
|
+
if (role && role > this.min_role_read) {
|
|
570
|
+
//check ownership
|
|
571
|
+
if (forPublic)
|
|
572
|
+
return [];
|
|
573
|
+
else if (this.ownership_field_id) {
|
|
574
|
+
//already dealt with by changing where
|
|
575
|
+
}
|
|
576
|
+
else if (this.ownership_formula) {
|
|
577
|
+
rows = rows.filter((row) => this.is_owner(forUser, row));
|
|
578
|
+
}
|
|
579
|
+
else
|
|
580
|
+
return []; //no ownership
|
|
581
|
+
}
|
|
474
582
|
return apply_calculated_fields(rows.map((r) => this.readFromDB(r)), this.fields);
|
|
475
583
|
}
|
|
476
584
|
/**
|
|
@@ -515,6 +623,7 @@ class Table {
|
|
|
515
623
|
let v = { ...v_in };
|
|
516
624
|
const fields = await this.getFields();
|
|
517
625
|
const pk_name = this.pk_name;
|
|
626
|
+
const role = user?.role_id;
|
|
518
627
|
if (fields.some((f) => f.calculated && f.stored)) {
|
|
519
628
|
const joinFields = this.storedExpressionJoinFields();
|
|
520
629
|
//if any freevars are join fields, update row in db first
|
|
@@ -523,10 +632,10 @@ class Table {
|
|
|
523
632
|
if (need_to_update) {
|
|
524
633
|
await db_1.default.update(this.name, v, id, { pk_name });
|
|
525
634
|
}
|
|
526
|
-
existing =
|
|
635
|
+
existing = await this.getJoinedRow({
|
|
527
636
|
where: { [pk_name]: id },
|
|
528
637
|
joinFields,
|
|
529
|
-
})
|
|
638
|
+
});
|
|
530
639
|
let calced = await apply_calculated_fields_stored(need_to_update ? existing : { ...existing, ...v_in },
|
|
531
640
|
// @ts-ignore TODO ch throw ?
|
|
532
641
|
this.fields);
|
|
@@ -534,11 +643,42 @@ class Table {
|
|
|
534
643
|
if (f.calculated && f.stored)
|
|
535
644
|
v[f.name] = calced[f.name];
|
|
536
645
|
}
|
|
646
|
+
if (user && role && role > this.min_role_write) {
|
|
647
|
+
if (role === 10)
|
|
648
|
+
return;
|
|
649
|
+
if (this.ownership_field_id) {
|
|
650
|
+
const owner_field = fields.find((f) => f.id === this.ownership_field_id);
|
|
651
|
+
if (!owner_field)
|
|
652
|
+
throw new Error(`Owner field in table ${this.name} not found`);
|
|
653
|
+
if (v[owner_field.name] && v[owner_field.name] !== user.id)
|
|
654
|
+
return;
|
|
655
|
+
//need to check existing
|
|
656
|
+
if (!existing)
|
|
657
|
+
existing = await this.getJoinedRow({
|
|
658
|
+
where: { [pk_name]: id },
|
|
659
|
+
forUser: user,
|
|
660
|
+
});
|
|
661
|
+
if (!existing || existing?.[owner_field.name] !== user.id)
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
if (this.ownership_formula) {
|
|
665
|
+
if (!existing)
|
|
666
|
+
existing = await this.getJoinedRow({
|
|
667
|
+
where: { [pk_name]: id },
|
|
668
|
+
forUser: user,
|
|
669
|
+
});
|
|
670
|
+
if (!existing || !this.is_owner(user, existing))
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
if (!this.ownership_field_id && !this.ownership_formula)
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
537
676
|
if (this.versioned) {
|
|
677
|
+
const existing1 = await db_1.default.selectOne(this.name, { [pk_name]: id });
|
|
538
678
|
if (!existing)
|
|
539
|
-
existing =
|
|
679
|
+
existing = existing1;
|
|
540
680
|
await db_1.default.insert(this.name + "__history", {
|
|
541
|
-
...
|
|
681
|
+
...existing1,
|
|
542
682
|
...v,
|
|
543
683
|
[pk_name]: id,
|
|
544
684
|
_version: {
|
|
@@ -556,7 +696,7 @@ class Table {
|
|
|
556
696
|
}
|
|
557
697
|
const newRow = { ...existing, ...v, [pk_name]: id };
|
|
558
698
|
if (!noTrigger) {
|
|
559
|
-
const trigPromise = trigger_1.default.runTableTriggers("Update", this, newRow, resultCollector, user);
|
|
699
|
+
const trigPromise = trigger_1.default.runTableTriggers("Update", this, newRow, resultCollector, role === 10 ? undefined : user);
|
|
560
700
|
if (resultCollector)
|
|
561
701
|
await trigPromise;
|
|
562
702
|
}
|
|
@@ -623,13 +763,24 @@ class Table {
|
|
|
623
763
|
const fields = await this.getFields();
|
|
624
764
|
const pk_name = this.pk_name;
|
|
625
765
|
const joinFields = this.storedExpressionJoinFields();
|
|
626
|
-
let v;
|
|
627
|
-
|
|
766
|
+
let v, id;
|
|
767
|
+
if (user && user.role_id > this.min_role_write) {
|
|
768
|
+
if (this.ownership_field_id) {
|
|
769
|
+
const owner_field = fields.find((f) => f.id === this.ownership_field_id);
|
|
770
|
+
if (!owner_field)
|
|
771
|
+
throw new Error(`Owner field in table ${this.name} not found`);
|
|
772
|
+
if (v_in[owner_field.name] !== user.id)
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
if (!this.ownership_field_id && !this.ownership_formula)
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
628
778
|
if (Object.keys(joinFields).length > 0) {
|
|
629
779
|
id = await db_1.default.insert(this.name, v_in, { pk_name });
|
|
630
780
|
let existing = await this.getJoinedRows({
|
|
631
781
|
where: { [pk_name]: id },
|
|
632
782
|
joinFields,
|
|
783
|
+
forUser: user,
|
|
633
784
|
});
|
|
634
785
|
let calced = await apply_calculated_fields_stored(existing[0], fields);
|
|
635
786
|
v = { ...v_in };
|
|
@@ -642,6 +793,16 @@ class Table {
|
|
|
642
793
|
v = await apply_calculated_fields_stored(v_in, fields);
|
|
643
794
|
id = await db_1.default.insert(this.name, v, { pk_name });
|
|
644
795
|
}
|
|
796
|
+
if (user && user.role_id > this.min_role_write && this.ownership_formula) {
|
|
797
|
+
let existing = await this.getJoinedRow({
|
|
798
|
+
where: { [pk_name]: id },
|
|
799
|
+
forUser: user,
|
|
800
|
+
});
|
|
801
|
+
if (!existing || !this.is_owner(user, existing)) {
|
|
802
|
+
await this.deleteRows({ [pk_name]: id });
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
645
806
|
if (this.versioned)
|
|
646
807
|
await db_1.default.insert(this.name + "__history", {
|
|
647
808
|
...v,
|
|
@@ -677,13 +838,7 @@ class Table {
|
|
|
677
838
|
* Get Fields list for table
|
|
678
839
|
* @returns {Promise<Field[]>}
|
|
679
840
|
*/
|
|
680
|
-
|
|
681
|
-
if (!this.fields) {
|
|
682
|
-
this.fields = await field_1.default.find({ table_id: this.id }, { orderBy: "id" });
|
|
683
|
-
for (let field of this.fields) {
|
|
684
|
-
field.table = this;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
841
|
+
getFields() {
|
|
687
842
|
return this.fields;
|
|
688
843
|
}
|
|
689
844
|
/**
|
|
@@ -692,7 +847,17 @@ class Table {
|
|
|
692
847
|
*/
|
|
693
848
|
async getField(path) {
|
|
694
849
|
const fields = await this.getFields();
|
|
695
|
-
if (path.includes("
|
|
850
|
+
if (path.includes("->")) {
|
|
851
|
+
const joinPath = path.split(".");
|
|
852
|
+
const tableName = joinPath[0];
|
|
853
|
+
const joinTable = await Table.findOne({ name: tableName });
|
|
854
|
+
if (!joinTable)
|
|
855
|
+
throw new Error(`The table '${tableName}' does not exist.`);
|
|
856
|
+
const joinedField = joinPath[1].split("->")[1];
|
|
857
|
+
const fields = await joinTable.getFields();
|
|
858
|
+
return fields.find((f) => f.name === joinedField);
|
|
859
|
+
}
|
|
860
|
+
else if (path.includes(".")) {
|
|
696
861
|
const keypath = path.split(".");
|
|
697
862
|
let field, theFields = fields;
|
|
698
863
|
for (let i = 0; i < keypath.length; i++) {
|
|
@@ -1119,6 +1284,106 @@ class Table {
|
|
|
1119
1284
|
success: `Imported ${file_rows.length} rows into table ${this.name}`,
|
|
1120
1285
|
};
|
|
1121
1286
|
}
|
|
1287
|
+
/**
|
|
1288
|
+
* get join-field-options joined from a field in this table
|
|
1289
|
+
* @param allow_double
|
|
1290
|
+
* @param allow_triple
|
|
1291
|
+
* @returns
|
|
1292
|
+
*/
|
|
1293
|
+
async get_join_field_options(allow_double, allow_triple) {
|
|
1294
|
+
const fields = await this.getFields();
|
|
1295
|
+
const result = [];
|
|
1296
|
+
for (const f of fields) {
|
|
1297
|
+
if (f.is_fkey && f.type !== "File") {
|
|
1298
|
+
const table = await Table.findOne({ name: f.reftable_name });
|
|
1299
|
+
if (!table)
|
|
1300
|
+
throw new Error(`Unable to find table '${f.reftable_name}`);
|
|
1301
|
+
await table.getFields();
|
|
1302
|
+
if (!table.fields)
|
|
1303
|
+
throw new Error(`The table '${f.reftable_name} has no fields.`);
|
|
1304
|
+
const subOne = {
|
|
1305
|
+
name: f.name,
|
|
1306
|
+
table: table.name,
|
|
1307
|
+
subFields: new Array(),
|
|
1308
|
+
fieldPath: f.name,
|
|
1309
|
+
};
|
|
1310
|
+
for (const pf of table.fields.filter((f) => !f.calculated || f.stored)) {
|
|
1311
|
+
const subTwo = {
|
|
1312
|
+
name: pf.name,
|
|
1313
|
+
subFields: new Array(),
|
|
1314
|
+
fieldPath: `${f.name}.${pf.name}`,
|
|
1315
|
+
};
|
|
1316
|
+
if (pf.is_fkey && pf.type !== "File" && allow_double) {
|
|
1317
|
+
const table1 = await Table.findOne({ name: pf.reftable_name });
|
|
1318
|
+
if (!table1)
|
|
1319
|
+
throw new Error(`Unable to find table '${pf.reftable_name}`);
|
|
1320
|
+
await table1.getFields();
|
|
1321
|
+
subTwo.table = table1.name;
|
|
1322
|
+
if (!table1.fields)
|
|
1323
|
+
throw new Error(`The table '${pf.reftable_name} has no fields.`);
|
|
1324
|
+
if (table1.fields)
|
|
1325
|
+
for (const gpf of table1.fields.filter((f) => !f.calculated || f.stored)) {
|
|
1326
|
+
const subThree = {
|
|
1327
|
+
name: gpf.name,
|
|
1328
|
+
subFields: new Array(),
|
|
1329
|
+
fieldPath: `${f.name}.${pf.name}.${gpf.name}`,
|
|
1330
|
+
};
|
|
1331
|
+
if (allow_triple && gpf.is_fkey && gpf.type !== "File") {
|
|
1332
|
+
const gpfTbl = Table.findOne({
|
|
1333
|
+
name: gpf.reftable_name,
|
|
1334
|
+
});
|
|
1335
|
+
if (gpfTbl) {
|
|
1336
|
+
subThree.table = gpfTbl.name;
|
|
1337
|
+
const gpfFields = await gpfTbl.getFields();
|
|
1338
|
+
for (const ggpf of gpfFields.filter((f) => !f.calculated || f.stored)) {
|
|
1339
|
+
subThree.subFields.push({
|
|
1340
|
+
name: ggpf.name,
|
|
1341
|
+
fieldPath: `${f.name}.${pf.name}.${gpf.name}.${ggpf.name}`,
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
subTwo.subFields.push(subThree);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
subOne.subFields.push(subTwo);
|
|
1350
|
+
}
|
|
1351
|
+
result.push(subOne);
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
return result;
|
|
1355
|
+
}
|
|
1356
|
+
/**
|
|
1357
|
+
* get relation-options joined from a field of another table
|
|
1358
|
+
* @returns
|
|
1359
|
+
*/
|
|
1360
|
+
async get_relation_options() {
|
|
1361
|
+
return await Promise.all((await this.get_relation_data()).map(async ({ relationTable, relationField }) => {
|
|
1362
|
+
const path = `${relationTable.name}.${relationField.name}`;
|
|
1363
|
+
const relFields = await relationTable.getFields();
|
|
1364
|
+
const names = relFields
|
|
1365
|
+
.filter((f) => f.type !== "Key")
|
|
1366
|
+
.map((f) => f.name);
|
|
1367
|
+
return { relationPath: path, relationFields: names };
|
|
1368
|
+
}));
|
|
1369
|
+
}
|
|
1370
|
+
/**
|
|
1371
|
+
* get relation-data joined from a field of another table
|
|
1372
|
+
* @returns
|
|
1373
|
+
*/
|
|
1374
|
+
async get_relation_data() {
|
|
1375
|
+
const result = new Array();
|
|
1376
|
+
const o2o_rels = await field_1.default.find({
|
|
1377
|
+
reftable_name: this.name,
|
|
1378
|
+
is_unique: true,
|
|
1379
|
+
});
|
|
1380
|
+
for (const field of o2o_rels) {
|
|
1381
|
+
const relTbl = Table.findOne({ id: field.table_id });
|
|
1382
|
+
if (relTbl)
|
|
1383
|
+
result.push({ relationTable: relTbl, relationField: field });
|
|
1384
|
+
}
|
|
1385
|
+
return result;
|
|
1386
|
+
}
|
|
1122
1387
|
/**
|
|
1123
1388
|
* Get parent relations for table
|
|
1124
1389
|
* @param allow_double
|
|
@@ -1250,6 +1515,24 @@ class Table {
|
|
|
1250
1515
|
let joinFields = opts.joinFields || {};
|
|
1251
1516
|
let aggregations = opts.aggregations || {};
|
|
1252
1517
|
const schema = db_1.default.getTenantSchemaPrefix();
|
|
1518
|
+
const { forUser, forPublic } = opts;
|
|
1519
|
+
const role = forUser ? forUser.role_id : forPublic ? 10 : null;
|
|
1520
|
+
if (role && role > this.min_role_read && this.ownership_formula) {
|
|
1521
|
+
const freeVars = freeVariables(this.ownership_formula);
|
|
1522
|
+
add_free_variables_to_joinfields(freeVars, joinFields, fields);
|
|
1523
|
+
}
|
|
1524
|
+
if (role && role > this.min_role_read && this.ownership_field_id) {
|
|
1525
|
+
if (forPublic)
|
|
1526
|
+
return { notAuthorized: true };
|
|
1527
|
+
const owner_field = fields.find((f) => f.id === this.ownership_field_id);
|
|
1528
|
+
if (!owner_field)
|
|
1529
|
+
throw new Error(`Owner field in table ${this.name} not found`);
|
|
1530
|
+
if (!opts.where)
|
|
1531
|
+
opts.where = {};
|
|
1532
|
+
mergeIntoWhere(opts.where, {
|
|
1533
|
+
[owner_field.name]: forUser.id,
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1253
1536
|
for (const [fldnm, { ref, target, through, ontable }] of Object.entries(joinFields)) {
|
|
1254
1537
|
let reffield;
|
|
1255
1538
|
if (ontable) {
|
|
@@ -1361,7 +1644,15 @@ class Table {
|
|
|
1361
1644
|
offset: opts.offset,
|
|
1362
1645
|
};
|
|
1363
1646
|
const sql = `SELECT ${fldNms.join()} FROM ${schema}"${(0, internal_1.sqlsanitize)(this.name)}" a ${joinq} ${where} ${(0, internal_1.mkSelectOptions)(selectopts)}`;
|
|
1364
|
-
return { sql, values };
|
|
1647
|
+
return { sql, values, joinFields };
|
|
1648
|
+
}
|
|
1649
|
+
/**
|
|
1650
|
+
* @param {object} [opts = {}]
|
|
1651
|
+
* @returns {Promise<object[]>}
|
|
1652
|
+
*/
|
|
1653
|
+
async getJoinedRow(opts = {}) {
|
|
1654
|
+
const rows = await this.getJoinedRows(opts);
|
|
1655
|
+
return rows.length > 0 ? rows[0] : null;
|
|
1365
1656
|
}
|
|
1366
1657
|
/**
|
|
1367
1658
|
* @param {object} [opts = {}]
|
|
@@ -1369,17 +1660,19 @@ class Table {
|
|
|
1369
1660
|
*/
|
|
1370
1661
|
async getJoinedRows(opts = {}) {
|
|
1371
1662
|
const fields = await this.getFields();
|
|
1372
|
-
const {
|
|
1663
|
+
const { forUser, forPublic, ...selopts1 } = opts;
|
|
1664
|
+
const role = forUser ? forUser.role_id : forPublic ? 10 : null;
|
|
1665
|
+
const { sql, values, notAuthorized, joinFields } = await this.getJoinedQuery(opts);
|
|
1666
|
+
if (notAuthorized)
|
|
1667
|
+
return [];
|
|
1373
1668
|
const res = await db_1.default.query(sql, values);
|
|
1374
1669
|
if (res.length === 0)
|
|
1375
1670
|
return res; // check
|
|
1376
|
-
|
|
1377
|
-
//console.log(res.rows);
|
|
1378
|
-
const calcRow = apply_calculated_fields(res.rows, fields);
|
|
1671
|
+
let calcRow = apply_calculated_fields(res.rows, fields);
|
|
1379
1672
|
//rename joinfields
|
|
1380
|
-
if (Object.values(
|
|
1673
|
+
if (Object.values(joinFields || {}).some((jf) => jf.rename_object)) {
|
|
1381
1674
|
let f = (x) => x;
|
|
1382
|
-
Object.entries(
|
|
1675
|
+
Object.entries(joinFields || {}).forEach(([k, v]) => {
|
|
1383
1676
|
if (v.rename_object) {
|
|
1384
1677
|
if (v.rename_object.length === 2) {
|
|
1385
1678
|
const oldf = f;
|
|
@@ -1428,10 +1721,22 @@ class Table {
|
|
|
1428
1721
|
}
|
|
1429
1722
|
}
|
|
1430
1723
|
});
|
|
1431
|
-
|
|
1724
|
+
calcRow = calcRow.map(f);
|
|
1432
1725
|
}
|
|
1433
|
-
|
|
1434
|
-
|
|
1726
|
+
if (role && role > this.min_role_read) {
|
|
1727
|
+
//check ownership
|
|
1728
|
+
if (forPublic)
|
|
1729
|
+
return [];
|
|
1730
|
+
else if (this.ownership_field_id) {
|
|
1731
|
+
//already dealt with by changing where
|
|
1732
|
+
}
|
|
1733
|
+
else if (this.ownership_formula) {
|
|
1734
|
+
calcRow = calcRow.filter((row) => this.is_owner(forUser, row));
|
|
1735
|
+
}
|
|
1736
|
+
else
|
|
1737
|
+
return []; //no ownership
|
|
1738
|
+
}
|
|
1739
|
+
return calcRow;
|
|
1435
1740
|
}
|
|
1436
1741
|
async slug_options() {
|
|
1437
1742
|
const fields = await this.getFields();
|