@saltcorn/data 0.8.7 → 0.8.8-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/dist/base-plugin/actions.d.ts.map +1 -1
- package/dist/base-plugin/actions.js +2 -3
- package/dist/base-plugin/actions.js.map +1 -1
- package/dist/base-plugin/index.d.ts +5 -8
- package/dist/base-plugin/index.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/edit.d.ts +2 -1
- package/dist/base-plugin/viewtemplates/edit.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/edit.js +14 -3
- package/dist/base-plugin/viewtemplates/edit.js.map +1 -1
- package/dist/base-plugin/viewtemplates/filter.d.ts +3 -7
- package/dist/base-plugin/viewtemplates/filter.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/filter.js +145 -20
- package/dist/base-plugin/viewtemplates/filter.js.map +1 -1
- package/dist/base-plugin/viewtemplates/viewable_fields.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/viewable_fields.js +4 -2
- package/dist/base-plugin/viewtemplates/viewable_fields.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 +18 -0
- package/dist/db/state.js.map +1 -1
- package/dist/migrations/202305031518.d.ts +3 -0
- package/dist/migrations/202305031518.d.ts.map +1 -0
- package/dist/migrations/202305031518.js +52 -0
- package/dist/migrations/202305031518.js.map +1 -0
- package/dist/model-helper.d.ts +20 -0
- package/dist/model-helper.d.ts.map +1 -0
- package/dist/model-helper.js +111 -0
- package/dist/model-helper.js.map +1 -0
- package/dist/models/config.d.ts +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/crash.js +1 -1
- package/dist/models/crash.js.map +1 -1
- package/dist/models/expression.d.ts.map +1 -1
- package/dist/models/expression.js +8 -6
- package/dist/models/expression.js.map +1 -1
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/model.d.ts +54 -0
- package/dist/models/model.d.ts.map +1 -0
- package/dist/models/model.js +172 -0
- package/dist/models/model.js.map +1 -0
- package/dist/models/model_instance.d.ts +57 -0
- package/dist/models/model_instance.d.ts.map +1 -0
- package/dist/models/model_instance.js +122 -0
- package/dist/models/model_instance.js.map +1 -0
- package/dist/models/table.d.ts +4 -1
- package/dist/models/table.d.ts.map +1 -1
- package/dist/models/table.js +105 -40
- package/dist/models/table.js.map +1 -1
- package/dist/models/trigger.d.ts.map +1 -1
- package/dist/models/trigger.js +1 -0
- package/dist/models/trigger.js.map +1 -1
- package/dist/models/user.d.ts.map +1 -1
- package/dist/models/user.js +6 -6
- package/dist/models/user.js.map +1 -1
- package/dist/plugin-helper.d.ts +2 -2
- package/dist/plugin-helper.d.ts.map +1 -1
- package/dist/plugin-helper.js +39 -32
- package/dist/plugin-helper.js.map +1 -1
- package/dist/tests/models.test.js +35 -0
- package/dist/tests/models.test.js.map +1 -1
- package/dist/tests/table.test.js +67 -5
- package/dist/tests/table.test.js.map +1 -1
- package/dist/tests/user.test.js +87 -1
- package/dist/tests/user.test.js.map +1 -1
- package/package.json +8 -8
package/dist/models/table.js
CHANGED
|
@@ -233,6 +233,13 @@ class Table {
|
|
|
233
233
|
}
|
|
234
234
|
return [...dbs, ...externals];
|
|
235
235
|
}
|
|
236
|
+
async get_models(opts) {
|
|
237
|
+
const Model = require("./model");
|
|
238
|
+
if (typeof opts === "string")
|
|
239
|
+
return await Model.find({ name: opts, table_id: this.id });
|
|
240
|
+
else
|
|
241
|
+
return await Model.find({ ...(opts || {}), table_id: this.id });
|
|
242
|
+
}
|
|
236
243
|
/**
|
|
237
244
|
* Get owner column name
|
|
238
245
|
* @param fields - fields list
|
|
@@ -275,7 +282,7 @@ class Table {
|
|
|
275
282
|
return typeof field_name === "string" && row[field_name] === user.id;
|
|
276
283
|
}
|
|
277
284
|
async ownership_options() {
|
|
278
|
-
const fields =
|
|
285
|
+
const fields = this.fields;
|
|
279
286
|
//start with userfields
|
|
280
287
|
const opts = fields
|
|
281
288
|
.filter((f) => f.reftable_name === "users")
|
|
@@ -491,7 +498,7 @@ class Table {
|
|
|
491
498
|
return `${db_1.default.getTenantSchemaPrefix()}"${(0, internal_1.sqlsanitize)(this.name)}"`;
|
|
492
499
|
}
|
|
493
500
|
async resetSequence() {
|
|
494
|
-
const fields =
|
|
501
|
+
const fields = this.fields;
|
|
495
502
|
const pk = fields.find((f) => f.primary_key);
|
|
496
503
|
if (!pk) {
|
|
497
504
|
throw new Error("Unable to find a field with a primary key.");
|
|
@@ -526,7 +533,7 @@ class Table {
|
|
|
526
533
|
async deleteRows(where, user) {
|
|
527
534
|
// get triggers on delete
|
|
528
535
|
const triggers = await trigger_1.default.getTableTriggers("Delete", this);
|
|
529
|
-
const fields =
|
|
536
|
+
const fields = this.fields;
|
|
530
537
|
if (this.updateWhereWithOwnership(where, fields, user)?.notAuthorized) {
|
|
531
538
|
const state = require("../db/state").getState();
|
|
532
539
|
state.log(4, `Not authorized to deleteRows in table ${this.name}.`);
|
|
@@ -595,7 +602,7 @@ class Table {
|
|
|
595
602
|
* @returns {Promise<null|*>}
|
|
596
603
|
*/
|
|
597
604
|
async getRow(where = {}, selopts = {}) {
|
|
598
|
-
const fields =
|
|
605
|
+
const fields = this.fields;
|
|
599
606
|
const { forUser, forPublic, ...selopts1 } = selopts;
|
|
600
607
|
const role = forUser ? forUser.role_id : forPublic ? 100 : null;
|
|
601
608
|
const row = await db_1.default.selectMaybeOne(this.name, where, selopts1);
|
|
@@ -628,7 +635,7 @@ class Table {
|
|
|
628
635
|
* @returns {Promise<void>}
|
|
629
636
|
*/
|
|
630
637
|
async getRows(where = {}, selopts = {}) {
|
|
631
|
-
const fields =
|
|
638
|
+
const fields = this.fields;
|
|
632
639
|
if (!this.fields)
|
|
633
640
|
return [];
|
|
634
641
|
const { forUser, forPublic, ...selopts1 } = selopts;
|
|
@@ -700,7 +707,7 @@ class Table {
|
|
|
700
707
|
async updateRow(v_in, id, user, noTrigger, resultCollector, restore_of_version) {
|
|
701
708
|
let existing;
|
|
702
709
|
let v = { ...v_in };
|
|
703
|
-
const fields =
|
|
710
|
+
const fields = this.fields;
|
|
704
711
|
const pk_name = this.pk_name;
|
|
705
712
|
const role = user?.role_id;
|
|
706
713
|
const state = require("../db/state").getState();
|
|
@@ -710,9 +717,11 @@ class Table {
|
|
|
710
717
|
}
|
|
711
718
|
if (this.ownership_formula)
|
|
712
719
|
add_free_variables_to_joinfields(freeVariables(this.ownership_formula), joinFields, fields);
|
|
713
|
-
if (user &&
|
|
714
|
-
|
|
715
|
-
|
|
720
|
+
if (user &&
|
|
721
|
+
role &&
|
|
722
|
+
(role > this.min_role_write || role > this.min_role_read)) {
|
|
723
|
+
if (role === 100)
|
|
724
|
+
return "Not authorized"; //no possibility of ownership
|
|
716
725
|
if (this.ownership_field_id) {
|
|
717
726
|
const owner_field = fields.find((f) => f.id === this.ownership_field_id);
|
|
718
727
|
if (!owner_field)
|
|
@@ -762,6 +771,11 @@ class Table {
|
|
|
762
771
|
if (constraint_check)
|
|
763
772
|
return constraint_check;
|
|
764
773
|
}
|
|
774
|
+
if (user) {
|
|
775
|
+
let field_write_check = this.check_field_write_role(v, user);
|
|
776
|
+
if (field_write_check)
|
|
777
|
+
return field_write_check;
|
|
778
|
+
}
|
|
765
779
|
if (fields.some((f) => f.calculated && f.stored)) {
|
|
766
780
|
//if any freevars are join fields, update row in db first
|
|
767
781
|
const freeVarFKFields = new Set(Object.values(joinFields).map((jf) => jf.ref));
|
|
@@ -837,7 +851,7 @@ class Table {
|
|
|
837
851
|
return { success: true };
|
|
838
852
|
}
|
|
839
853
|
catch (e) {
|
|
840
|
-
return { error: normalise_error_message(e.message) };
|
|
854
|
+
return { error: this.normalise_error_message(e.message) };
|
|
841
855
|
}
|
|
842
856
|
}
|
|
843
857
|
/**
|
|
@@ -846,22 +860,10 @@ class Table {
|
|
|
846
860
|
* @param field_name
|
|
847
861
|
* @returns {Promise<void>}
|
|
848
862
|
*/
|
|
849
|
-
async toggleBool(id, field_name) {
|
|
850
|
-
const
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
if (fields.some((f) => f.calculated && f.stored)) {
|
|
854
|
-
await this.updateRow({}, id, undefined, false);
|
|
855
|
-
}
|
|
856
|
-
const triggers = await trigger_1.default.getTableTriggers("Update", this);
|
|
857
|
-
if (triggers.length > 0) {
|
|
858
|
-
const row = await this.getRow({ id });
|
|
859
|
-
if (!row)
|
|
860
|
-
throw new Error(`Unable to find row with id: ${id}`);
|
|
861
|
-
for (const trigger of triggers) {
|
|
862
|
-
trigger.run(row);
|
|
863
|
-
}
|
|
864
|
-
}
|
|
863
|
+
async toggleBool(id, field_name, user) {
|
|
864
|
+
const row = await this.getRow({ [this.pk_name]: id });
|
|
865
|
+
if (row)
|
|
866
|
+
await this.updateRow({ [field_name]: !row[field_name] }, id, user);
|
|
865
867
|
}
|
|
866
868
|
/**
|
|
867
869
|
* Get primary key field
|
|
@@ -884,6 +886,15 @@ class Table {
|
|
|
884
886
|
}
|
|
885
887
|
return undefined;
|
|
886
888
|
}
|
|
889
|
+
check_field_write_role(row, user) {
|
|
890
|
+
for (const field of this.fields) {
|
|
891
|
+
if (typeof row[field.name] !== "undefined" &&
|
|
892
|
+
field.attributes?.min_role_write &&
|
|
893
|
+
user.role_id > field.attributes?.min_role_write)
|
|
894
|
+
return "Not authorized";
|
|
895
|
+
}
|
|
896
|
+
return undefined;
|
|
897
|
+
}
|
|
887
898
|
/**
|
|
888
899
|
* Insert row
|
|
889
900
|
* @param v_in
|
|
@@ -892,7 +903,7 @@ class Table {
|
|
|
892
903
|
* @returns {Promise<*>}
|
|
893
904
|
*/
|
|
894
905
|
async insertRow(v_in, user, resultCollector) {
|
|
895
|
-
const fields =
|
|
906
|
+
const fields = this.fields;
|
|
896
907
|
const pk_name = this.pk_name;
|
|
897
908
|
const joinFields = this.storedExpressionJoinFields();
|
|
898
909
|
if (this.ownership_formula)
|
|
@@ -917,6 +928,11 @@ class Table {
|
|
|
917
928
|
let constraint_check = this.check_table_constraints(v_in);
|
|
918
929
|
if (constraint_check)
|
|
919
930
|
throw new Error(constraint_check);
|
|
931
|
+
if (user) {
|
|
932
|
+
let field_write_check = this.check_field_write_role(v_in, user);
|
|
933
|
+
if (field_write_check)
|
|
934
|
+
return field_write_check;
|
|
935
|
+
}
|
|
920
936
|
if (Object.keys(joinFields).length > 0) {
|
|
921
937
|
id = await db_1.default.insert(this.name, v_in, { pk_name });
|
|
922
938
|
let existing = await this.getJoinedRows({
|
|
@@ -979,12 +995,58 @@ class Table {
|
|
|
979
995
|
async tryInsertRow(v, user, resultCollector) {
|
|
980
996
|
try {
|
|
981
997
|
const id = await this.insertRow(v, user, resultCollector);
|
|
998
|
+
if (id?.includes?.("Not authorized"))
|
|
999
|
+
return { error: id };
|
|
1000
|
+
if (id.error)
|
|
1001
|
+
return id;
|
|
982
1002
|
return { success: id };
|
|
983
1003
|
}
|
|
984
1004
|
catch (e) {
|
|
985
|
-
return { error: normalise_error_message(e.message) };
|
|
1005
|
+
return { error: this.normalise_error_message(e.message) };
|
|
986
1006
|
}
|
|
987
1007
|
}
|
|
1008
|
+
normalise_error_message(msg) {
|
|
1009
|
+
let fieldnm = "";
|
|
1010
|
+
if (msg.toLowerCase().includes("unique constraint")) {
|
|
1011
|
+
if (db_1.default.isSQLite) {
|
|
1012
|
+
fieldnm = msg.replace(`SQLITE_CONSTRAINT: UNIQUE constraint failed: ${this.name}.`, "");
|
|
1013
|
+
}
|
|
1014
|
+
else {
|
|
1015
|
+
const m = msg.match(/duplicate key value violates unique constraint "(.*?)_(.*?)_unique"/);
|
|
1016
|
+
if (m)
|
|
1017
|
+
fieldnm = m[2];
|
|
1018
|
+
}
|
|
1019
|
+
if (fieldnm) {
|
|
1020
|
+
const field = this.fields.find((f) => f.name === fieldnm);
|
|
1021
|
+
if (field?.attributes?.unique_error_msg)
|
|
1022
|
+
return field?.attributes?.unique_error_msg;
|
|
1023
|
+
else {
|
|
1024
|
+
const tc_unique = this.constraints.find((c) => {
|
|
1025
|
+
if (c.type !== "Unique")
|
|
1026
|
+
return false;
|
|
1027
|
+
let conNm = "";
|
|
1028
|
+
if (db_1.default.isSQLite) {
|
|
1029
|
+
// SQLITE_CONSTRAINT: UNIQUE constraint failed: books.author, books.pages
|
|
1030
|
+
// first table name stripped by replace
|
|
1031
|
+
let [field1, ...rest_fields] = c.configuration.fields;
|
|
1032
|
+
conNm = [
|
|
1033
|
+
field1,
|
|
1034
|
+
...rest_fields.map((fnm) => `${this.name}.${fnm}`),
|
|
1035
|
+
].join(", ");
|
|
1036
|
+
}
|
|
1037
|
+
else {
|
|
1038
|
+
conNm = c.configuration.fields.join("_");
|
|
1039
|
+
}
|
|
1040
|
+
return c.configuration.errormsg && conNm === fieldnm;
|
|
1041
|
+
});
|
|
1042
|
+
if (tc_unique)
|
|
1043
|
+
return tc_unique.configuration.errormsg;
|
|
1044
|
+
return `Duplicate value for unique field: ${field?.label || fieldnm}`;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
return msg;
|
|
1049
|
+
}
|
|
988
1050
|
/**
|
|
989
1051
|
* Get Fields list for table
|
|
990
1052
|
* @returns {Promise<Field[]>}
|
|
@@ -1040,7 +1102,7 @@ class Table {
|
|
|
1040
1102
|
// todo create function that returns history table name for table
|
|
1041
1103
|
async create_history_table() {
|
|
1042
1104
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
1043
|
-
const fields =
|
|
1105
|
+
const fields = this.fields;
|
|
1044
1106
|
const flds = fields.map((f) => `,"${(0, internal_1.sqlsanitize)(f.name)}" ${f.sql_bare_type}`);
|
|
1045
1107
|
const pk = fields.find((f) => f.primary_key)?.name;
|
|
1046
1108
|
if (!pk) {
|
|
@@ -1182,7 +1244,7 @@ class Table {
|
|
|
1182
1244
|
* @returns {Promise<void>}
|
|
1183
1245
|
*/
|
|
1184
1246
|
async enable_fkey_constraints() {
|
|
1185
|
-
const fields =
|
|
1247
|
+
const fields = this.fields;
|
|
1186
1248
|
for (const f of fields)
|
|
1187
1249
|
await f.enable_fkey_constraint(this);
|
|
1188
1250
|
}
|
|
@@ -1570,7 +1632,7 @@ class Table {
|
|
|
1570
1632
|
async import_json_file(filePath, skip_first_data_row) {
|
|
1571
1633
|
// todo argument type buffer is not assignable for type String...
|
|
1572
1634
|
const file_rows = JSON.parse((await (0, promises_1.readFile)(filePath)).toString());
|
|
1573
|
-
const fields =
|
|
1635
|
+
const fields = this.fields;
|
|
1574
1636
|
const pk_name = this.pk_name;
|
|
1575
1637
|
const { readState } = require("../plugin-helper");
|
|
1576
1638
|
let i = 1;
|
|
@@ -1615,7 +1677,7 @@ class Table {
|
|
|
1615
1677
|
* @returns
|
|
1616
1678
|
*/
|
|
1617
1679
|
async get_join_field_options(allow_double, allow_triple) {
|
|
1618
|
-
const fields =
|
|
1680
|
+
const fields = this.fields;
|
|
1619
1681
|
const result = [];
|
|
1620
1682
|
for (const f of fields) {
|
|
1621
1683
|
if (f.is_fkey && f.type !== "File") {
|
|
@@ -1715,7 +1777,7 @@ class Table {
|
|
|
1715
1777
|
* @returns {Promise<{parent_relations: object[], parent_field_list: object[]}>}
|
|
1716
1778
|
*/
|
|
1717
1779
|
async get_parent_relations(allow_double, allow_triple) {
|
|
1718
|
-
const fields =
|
|
1780
|
+
const fields = this.fields;
|
|
1719
1781
|
let parent_relations = [];
|
|
1720
1782
|
let parent_field_list = [];
|
|
1721
1783
|
for (const f of fields) {
|
|
@@ -1776,7 +1838,7 @@ class Table {
|
|
|
1776
1838
|
return { parent_relations, parent_field_list };
|
|
1777
1839
|
}
|
|
1778
1840
|
async field_options(nrecurse = 0, fieldWhere = () => true, prefix = "") {
|
|
1779
|
-
const fields =
|
|
1841
|
+
const fields = this.fields;
|
|
1780
1842
|
const these = fields.filter(fieldWhere).map((f) => prefix + f.name);
|
|
1781
1843
|
const those = [];
|
|
1782
1844
|
if (nrecurse > 0)
|
|
@@ -1810,7 +1872,7 @@ class Table {
|
|
|
1810
1872
|
}
|
|
1811
1873
|
}
|
|
1812
1874
|
if (allow_join_aggregations) {
|
|
1813
|
-
const fields =
|
|
1875
|
+
const fields = this.fields;
|
|
1814
1876
|
for (const f of fields) {
|
|
1815
1877
|
if (f.is_fkey && f.type !== "File") {
|
|
1816
1878
|
const refTable = Table.findOne({ name: f.reftable_name });
|
|
@@ -1832,7 +1894,7 @@ class Table {
|
|
|
1832
1894
|
* @returns {Promise<{values, sql: string}>}
|
|
1833
1895
|
*/
|
|
1834
1896
|
async getJoinedQuery(opts = {}) {
|
|
1835
|
-
const fields =
|
|
1897
|
+
const fields = this.fields;
|
|
1836
1898
|
let fldNms = [];
|
|
1837
1899
|
let joinq = "";
|
|
1838
1900
|
let joinTables = [];
|
|
@@ -1937,6 +1999,9 @@ class Table {
|
|
|
1937
1999
|
const aggTable = Table.findOne({ name: table });
|
|
1938
2000
|
const aggField = aggTable?.fields?.find((f) => f.name === field);
|
|
1939
2001
|
const ownField = through ? (0, internal_1.sqlsanitize)(through) : this.pk_name;
|
|
2002
|
+
const agg_and_field = aggregate.toLowerCase() === "countunique"
|
|
2003
|
+
? `count(distinct ${field ? `"${(0, internal_1.sqlsanitize)(field)}"` : "*"})`
|
|
2004
|
+
: `${(0, internal_1.sqlsanitize)(aggregate)}(${field ? `"${(0, internal_1.sqlsanitize)(field)}"` : "*"})`;
|
|
1940
2005
|
if (aggField?.is_fkey &&
|
|
1941
2006
|
aggField.attributes.summary_field &&
|
|
1942
2007
|
aggregate.toLowerCase() === "array_agg") {
|
|
@@ -1950,9 +2015,9 @@ class Table {
|
|
|
1950
2015
|
fldNms.push(`(select "${(0, internal_1.sqlsanitize)(field)}" from ${schema}"${(0, internal_1.sqlsanitize)(table)}" where "${dateField}"=(select ${isLatest ? `max` : `min`}("${dateField}") from ${schema}"${(0, internal_1.sqlsanitize)(table)}" where "${(0, internal_1.sqlsanitize)(ref)}"=a."${ownField}"${whereStr ? ` and ${whereStr}` : ""}) and "${(0, internal_1.sqlsanitize)(ref)}"=a."${ownField}" limit 1) ${(0, internal_1.sqlsanitize)(fldnm)}`);
|
|
1951
2016
|
}
|
|
1952
2017
|
else if (subselect)
|
|
1953
|
-
fldNms.push(`(select ${
|
|
2018
|
+
fldNms.push(`(select ${agg_and_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."${ownField}")) ${(0, internal_1.sqlsanitize)(fldnm)}`);
|
|
1954
2019
|
else
|
|
1955
|
-
fldNms.push(`(select ${
|
|
2020
|
+
fldNms.push(`(select ${agg_and_field} from ${schema}"${(0, internal_1.sqlsanitize)(table)}" where "${(0, internal_1.sqlsanitize)(ref)}"=a."${ownField}"${whereStr ? ` and ${whereStr}` : ""}) ${(0, internal_1.sqlsanitize)(fldnm)}`);
|
|
1956
2021
|
});
|
|
1957
2022
|
const selectopts = {
|
|
1958
2023
|
limit: opts.limit,
|
|
@@ -1983,7 +2048,7 @@ class Table {
|
|
|
1983
2048
|
* @returns {Promise<object[]>}
|
|
1984
2049
|
*/
|
|
1985
2050
|
async getJoinedRows(opts = {}) {
|
|
1986
|
-
const fields =
|
|
2051
|
+
const fields = this.fields;
|
|
1987
2052
|
const { forUser, forPublic, ...selopts1 } = opts;
|
|
1988
2053
|
const role = forUser ? forUser.role_id : forPublic ? 100 : null;
|
|
1989
2054
|
const { sql, values, notAuthorized, joinFields } = await this.getJoinedQuery(opts);
|
|
@@ -2063,7 +2128,7 @@ class Table {
|
|
|
2063
2128
|
return calcRow;
|
|
2064
2129
|
}
|
|
2065
2130
|
async slug_options() {
|
|
2066
|
-
const fields =
|
|
2131
|
+
const fields = this.fields;
|
|
2067
2132
|
const unique_fields = fields.filter((f) => f.is_unique);
|
|
2068
2133
|
const opts = [];
|
|
2069
2134
|
unique_fields.forEach((f) => {
|