@saltcorn/data 1.6.0-beta.1 → 1.6.0-beta.11
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 +191 -62
- package/dist/base-plugin/actions.d.ts.map +1 -1
- package/dist/base-plugin/actions.js +164 -66
- package/dist/base-plugin/actions.js.map +1 -1
- package/dist/base-plugin/fieldviews.d.ts +1 -0
- package/dist/base-plugin/fieldviews.d.ts.map +1 -1
- package/dist/base-plugin/fieldviews.js +5 -2
- package/dist/base-plugin/fieldviews.js.map +1 -1
- package/dist/base-plugin/fileviews.d.ts +6 -0
- package/dist/base-plugin/fileviews.d.ts.map +1 -1
- package/dist/base-plugin/fileviews.js +14 -0
- package/dist/base-plugin/fileviews.js.map +1 -1
- package/dist/base-plugin/index.d.ts +210 -65
- package/dist/base-plugin/index.d.ts.map +1 -1
- package/dist/base-plugin/types.d.ts +19 -9
- package/dist/base-plugin/types.d.ts.map +1 -1
- package/dist/base-plugin/types.js +41 -12
- 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 +12 -11
- package/dist/base-plugin/viewtemplates/edit.js.map +1 -1
- package/dist/base-plugin/viewtemplates/feed.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/feed.js +1 -1
- package/dist/base-plugin/viewtemplates/feed.js.map +1 -1
- package/dist/base-plugin/viewtemplates/filter.js +2 -2
- package/dist/base-plugin/viewtemplates/filter.js.map +1 -1
- package/dist/base-plugin/viewtemplates/list.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/list.js +14 -1
- package/dist/base-plugin/viewtemplates/list.js.map +1 -1
- package/dist/base-plugin/viewtemplates/room.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/room.js +1 -1
- package/dist/base-plugin/viewtemplates/room.js.map +1 -1
- package/dist/base-plugin/viewtemplates/show.d.ts.map +1 -1
- package/dist/db/fixtures.d.ts.map +1 -1
- package/dist/db/fixtures.js +31 -1
- package/dist/db/fixtures.js.map +1 -1
- package/dist/db/state.d.ts +5 -3
- package/dist/db/state.d.ts.map +1 -1
- package/dist/db/state.js +36 -12
- package/dist/db/state.js.map +1 -1
- package/dist/migrate.d.ts.map +1 -1
- package/dist/migrate.js +3 -1
- package/dist/migrate.js.map +1 -1
- package/dist/migrations/202604091531.d.ts +2 -0
- package/dist/migrations/202604091531.d.ts.map +1 -0
- package/dist/migrations/202604091531.js +9 -0
- package/dist/migrations/202604091531.js.map +1 -0
- package/dist/migrations/202604111200.d.ts +2 -0
- package/dist/migrations/202604111200.d.ts.map +1 -0
- package/dist/migrations/202604111200.js +15 -0
- package/dist/migrations/202604111200.js.map +1 -0
- package/dist/migrations/202604141200.d.ts +2 -0
- package/dist/migrations/202604141200.d.ts.map +1 -0
- package/dist/migrations/202604141200.js +14 -0
- package/dist/migrations/202604141200.js.map +1 -0
- package/dist/mobile-mocks/node/fs/promises.d.ts +1 -0
- package/dist/mobile-mocks/node/fs/promises.d.ts.map +1 -1
- package/dist/mobile-mocks/node/fs/promises.js +4 -0
- package/dist/mobile-mocks/node/fs/promises.js.map +1 -1
- package/dist/mobile-mocks/node/fs.d.ts +2 -0
- package/dist/mobile-mocks/node/fs.d.ts.map +1 -1
- package/dist/mobile-mocks/node/fs.js +36 -0
- package/dist/mobile-mocks/node/fs.js.map +1 -1
- package/dist/mobile-mocks/npm/npm-registry-fetch.d.ts +3 -0
- package/dist/mobile-mocks/npm/npm-registry-fetch.d.ts.map +1 -0
- package/dist/mobile-mocks/npm/npm-registry-fetch.js +3 -0
- package/dist/mobile-mocks/npm/npm-registry-fetch.js.map +1 -0
- package/dist/mobile-mocks/saltcorn/admin-models-tenant.d.ts +1 -0
- package/dist/mobile-mocks/saltcorn/admin-models-tenant.d.ts.map +1 -0
- package/dist/mobile-mocks/saltcorn/admin-models-tenant.js +2 -0
- package/dist/mobile-mocks/saltcorn/admin-models-tenant.js.map +1 -0
- package/dist/mobile-mocks/saltcorn/plugins-loader-plugin-installer.d.ts +1 -0
- package/dist/mobile-mocks/saltcorn/plugins-loader-plugin-installer.d.ts.map +1 -0
- package/dist/mobile-mocks/saltcorn/plugins-loader-plugin-installer.js +2 -0
- package/dist/mobile-mocks/saltcorn/plugins-loader-plugin-installer.js.map +1 -0
- package/dist/mobile-mocks/saltcorn/plugins-loader-stable-versioning.d.ts +1 -0
- package/dist/mobile-mocks/saltcorn/plugins-loader-stable-versioning.d.ts.map +1 -0
- package/dist/mobile-mocks/saltcorn/plugins-loader-stable-versioning.js +2 -0
- package/dist/mobile-mocks/saltcorn/plugins-loader-stable-versioning.js.map +1 -0
- package/dist/models/config.d.ts.map +1 -1
- package/dist/models/config.js +46 -1
- package/dist/models/config.js.map +1 -1
- package/dist/models/discovery.d.ts +14 -0
- package/dist/models/discovery.d.ts.map +1 -1
- package/dist/models/discovery.js +63 -0
- package/dist/models/discovery.js.map +1 -1
- package/dist/models/expression.d.ts +7 -4
- package/dist/models/expression.d.ts.map +1 -1
- package/dist/models/expression.js +28 -14
- package/dist/models/expression.js.map +1 -1
- package/dist/models/field.d.ts.map +1 -1
- package/dist/models/field.js +39 -9
- package/dist/models/field.js.map +1 -1
- package/dist/models/index.d.ts +12 -2
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +1 -1
- package/dist/models/index.js.map +1 -1
- package/dist/models/page.d.ts +1 -0
- package/dist/models/page.d.ts.map +1 -1
- package/dist/models/page.js +36 -20
- package/dist/models/page.js.map +1 -1
- package/dist/models/plugin.d.ts +57 -0
- package/dist/models/plugin.d.ts.map +1 -1
- package/dist/models/plugin.js +322 -1
- package/dist/models/plugin.js.map +1 -1
- package/dist/models/scheduler.d.ts.map +1 -1
- package/dist/models/scheduler.js +15 -5
- package/dist/models/scheduler.js.map +1 -1
- package/dist/models/table.d.ts +3 -1
- package/dist/models/table.d.ts.map +1 -1
- package/dist/models/table.js +148 -44
- package/dist/models/table.js.map +1 -1
- package/dist/models/trigger.d.ts +7 -3
- package/dist/models/trigger.d.ts.map +1 -1
- package/dist/models/trigger.js +63 -20
- package/dist/models/trigger.js.map +1 -1
- package/dist/models/user.d.ts.map +1 -1
- package/dist/models/user.js +2 -1
- package/dist/models/user.js.map +1 -1
- package/dist/models/view.d.ts +1 -0
- package/dist/models/view.d.ts.map +1 -1
- package/dist/models/view.js +74 -14
- package/dist/models/view.js.map +1 -1
- package/dist/models/workflow_run.d.ts.map +1 -1
- package/dist/models/workflow_run.js +3 -2
- package/dist/models/workflow_run.js.map +1 -1
- package/dist/models/workflow_step.d.ts +1 -1
- package/dist/models/workflow_step.d.ts.map +1 -1
- package/dist/models/workflow_step.js +2 -1
- package/dist/models/workflow_step.js.map +1 -1
- package/dist/plugin-helper.d.ts +3 -11
- package/dist/plugin-helper.d.ts.map +1 -1
- package/dist/plugin-helper.js +102 -60
- package/dist/plugin-helper.js.map +1 -1
- package/dist/standard-menu.d.ts.map +1 -1
- package/dist/standard-menu.js +19 -0
- package/dist/standard-menu.js.map +1 -1
- package/dist/tests/remote_query_helper.js +1 -1
- package/dist/tests/remote_query_helper.js.map +1 -1
- package/dist/utils.d.ts +15 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +102 -5
- package/dist/utils.js.map +1 -1
- package/dist/viewable_fields.d.ts +1 -1
- package/dist/viewable_fields.d.ts.map +1 -1
- package/dist/viewable_fields.js +48 -9
- package/dist/viewable_fields.js.map +1 -1
- package/dist/web-mobile-commons.d.ts.map +1 -1
- package/dist/web-mobile-commons.js +2 -1
- package/dist/web-mobile-commons.js.map +1 -1
- package/package.json +12 -9
- package/webpack.config.js +11 -0
package/dist/models/table.js
CHANGED
|
@@ -47,7 +47,7 @@ const field_1 = __importDefault(require("./field"));
|
|
|
47
47
|
const common_types_1 = require("@saltcorn/types/common_types");
|
|
48
48
|
const trigger_1 = __importDefault(require("./trigger"));
|
|
49
49
|
const expression_1 = __importDefault(require("./expression"));
|
|
50
|
-
const { apply_calculated_fields, apply_calculated_fields_stored, recalculate_for_stored, get_expression_function, eval_expression, freeVariables, add_free_variables_to_joinfields, removeComments, jsexprToWhere, } = expression_1.default;
|
|
50
|
+
const { apply_calculated_fields, apply_calculated_fields_stored, recalculate_for_stored, get_expression_function, get_async_expression_function, eval_expression, freeVariables, add_free_variables_to_joinfields, removeComments, jsexprToWhere, } = expression_1.default;
|
|
51
51
|
const uuid_1 = require("uuid");
|
|
52
52
|
const csvtojson_1 = __importDefault(require("csvtojson"));
|
|
53
53
|
const moment_1 = __importDefault(require("moment"));
|
|
@@ -201,6 +201,9 @@ class Table {
|
|
|
201
201
|
this.provider_cfg = stringToJSON(o.provider_cfg);
|
|
202
202
|
this.provider_name = o.provider_name;
|
|
203
203
|
this.fields = o.fields.map((f) => new field_1.default(f));
|
|
204
|
+
this.updated_at = ["string", "number"].includes(typeof o.updated_at)
|
|
205
|
+
? new Date(o.updated_at)
|
|
206
|
+
: o.updated_at;
|
|
204
207
|
}
|
|
205
208
|
static subClass({ user, read_only, } = {}) {
|
|
206
209
|
var _a;
|
|
@@ -242,6 +245,7 @@ class Table {
|
|
|
242
245
|
const { fields, constraints, ...updDB } = upd_rec;
|
|
243
246
|
if (updDB.ownership_field_id === "")
|
|
244
247
|
delete updDB.ownership_field_id;
|
|
248
|
+
updDB.updated_at = new Date();
|
|
245
249
|
await db_1.default.update("_sc_tables", updDB, tbl.id);
|
|
246
250
|
//limited refresh if we do not have a client
|
|
247
251
|
if (!db_1.default.getRequestContext()?.client)
|
|
@@ -255,6 +259,7 @@ class Table {
|
|
|
255
259
|
if (!db_1.default.getRequestContext()?.client)
|
|
256
260
|
await Table.state_refresh(true);
|
|
257
261
|
};
|
|
262
|
+
//console.log({tbl});
|
|
258
263
|
return t;
|
|
259
264
|
}
|
|
260
265
|
/**
|
|
@@ -324,7 +329,7 @@ class Table {
|
|
|
324
329
|
const { getState } = require("../db/state");
|
|
325
330
|
const provider = getState().table_providers[t.provider_name];
|
|
326
331
|
if (provider)
|
|
327
|
-
t.fields = await applyAsync(provider.fields, t.provider_cfg);
|
|
332
|
+
t.fields = await applyAsync(provider.fields, stringToJSON(t.provider_cfg));
|
|
328
333
|
else
|
|
329
334
|
t.fields = [];
|
|
330
335
|
}
|
|
@@ -600,6 +605,7 @@ class Table {
|
|
|
600
605
|
description: options.description || "",
|
|
601
606
|
provider_name: options.provider_name,
|
|
602
607
|
provider_cfg: options.provider_cfg,
|
|
608
|
+
updated_at: new Date(),
|
|
603
609
|
};
|
|
604
610
|
let pk_fld_id;
|
|
605
611
|
if (!id) {
|
|
@@ -780,25 +786,50 @@ class Table {
|
|
|
780
786
|
}
|
|
781
787
|
}
|
|
782
788
|
}
|
|
783
|
-
async addDeleteSyncInfo(ids, timestamp) {
|
|
789
|
+
async addDeleteSyncInfo(ids, timestamp, ownerFieldName, ownershipFormula) {
|
|
784
790
|
if (this.constructor.read_only)
|
|
785
791
|
throw new Error("Read-only access");
|
|
792
|
+
// Top-level keys referenced by the formula (strip join path suffixes,
|
|
793
|
+
// drop "user"). Stored in owner_fields so is_owner() can re-evaluate later.
|
|
794
|
+
const formulaTopKeys = ownershipFormula
|
|
795
|
+
? [
|
|
796
|
+
...new Set([...freeVariables(ownershipFormula)]
|
|
797
|
+
.map((v) => v.split(/[.Ⱶ]/)[0])
|
|
798
|
+
.filter((v) => v !== "user")),
|
|
799
|
+
]
|
|
800
|
+
: null;
|
|
786
801
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
787
802
|
if (ids.length > 0) {
|
|
788
803
|
const schema = db_1.default.getTenantSchemaPrefix();
|
|
789
804
|
const pkName = this.pk_name || "id";
|
|
790
805
|
if (isNode()) {
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
806
|
+
const pkVals = ids.map((row) => String(row[pkName]));
|
|
807
|
+
await db_1.default.query(`delete from ${schema}"${db_1.default.sqlsanitize(this.name)}_sync_info" where ref = ANY($1::text[])`, [pkVals]);
|
|
808
|
+
const tsParam = timestamp.valueOf() / 1000.0;
|
|
809
|
+
const insertParams = [tsParam];
|
|
810
|
+
const valueClauses = pkVals.map((pkVal, i) => {
|
|
811
|
+
const ownerVal = ownerFieldName
|
|
812
|
+
? (ids[i][ownerFieldName] ?? null)
|
|
813
|
+
: null;
|
|
814
|
+
const ownerFieldsVal = formulaTopKeys && formulaTopKeys.length > 0
|
|
815
|
+
? JSON.stringify(Object.fromEntries(formulaTopKeys.map((k) => [k, ids[i][k] ?? null])))
|
|
816
|
+
: null;
|
|
817
|
+
insertParams.push(pkVal);
|
|
818
|
+
insertParams.push(ownerVal);
|
|
819
|
+
insertParams.push(ownerFieldsVal);
|
|
820
|
+
return `($${insertParams.length - 2}::text, date_trunc('milliseconds', to_timestamp($1)), true, $${insertParams.length - 1}, $${insertParams.length})`;
|
|
821
|
+
});
|
|
822
|
+
await db_1.default.query(`insert into ${schema}"${db_1.default.sqlsanitize(this.name)}_sync_info" (ref, last_modified, deleted, owner_id, owner_fields)
|
|
823
|
+
values ${valueClauses.join(",")}`, insertParams);
|
|
797
824
|
}
|
|
798
825
|
else {
|
|
826
|
+
const pkVals = ids.map((row) => String(row[pkName]));
|
|
827
|
+
const placeholders = pkVals
|
|
828
|
+
.map((_, i) => `$${i + 1}`)
|
|
829
|
+
.join(",");
|
|
799
830
|
await db_1.default.query(`update "${db_1.default.sqlsanitize(this.name)}_sync_info"
|
|
800
831
|
set deleted = true, modified_local = true
|
|
801
|
-
where ref in (${
|
|
832
|
+
where ref in (${placeholders})`, pkVals);
|
|
802
833
|
}
|
|
803
834
|
}
|
|
804
835
|
}, (e) => {
|
|
@@ -842,6 +873,12 @@ class Table {
|
|
|
842
873
|
// get triggers on delete
|
|
843
874
|
const triggers = await trigger_1.default.getTableTriggers("Delete", this);
|
|
844
875
|
const fields = this.fields;
|
|
876
|
+
const ownerFieldName = (() => {
|
|
877
|
+
if (!this.has_sync_info || !this.ownership_field_id)
|
|
878
|
+
return null;
|
|
879
|
+
const f = fields?.find((f) => f.id === this.ownership_field_id);
|
|
880
|
+
return f?.name ?? null;
|
|
881
|
+
})();
|
|
845
882
|
if (this.updateWhereWithOwnership(where, use_user)?.notAuthorized) {
|
|
846
883
|
const state = require("../db/state").getState();
|
|
847
884
|
state.log(4, `Not authorized to deleteRows in table ${this.name}.`);
|
|
@@ -854,6 +891,13 @@ class Table {
|
|
|
854
891
|
expression: "__aggregation",
|
|
855
892
|
attributes: { json: { table: this.name } },
|
|
856
893
|
}, { cached: true });
|
|
894
|
+
// Join fields needed to snapshot ownership_formula values into sync_info.
|
|
895
|
+
// Computed once here; used either via the existing rows fetch (if it runs)
|
|
896
|
+
// or via a separate minimal fetch in the else branch below.
|
|
897
|
+
const ownershipJoinFields = {};
|
|
898
|
+
if (this.has_sync_info && this.ownership_formula) {
|
|
899
|
+
add_free_variables_to_joinfields(freeVariables(this.ownership_formula), ownershipJoinFields, fields);
|
|
900
|
+
}
|
|
857
901
|
let rows;
|
|
858
902
|
if (calc_agg_fields.length ||
|
|
859
903
|
(use_user &&
|
|
@@ -863,6 +907,7 @@ class Table {
|
|
|
863
907
|
where,
|
|
864
908
|
forUser: use_user,
|
|
865
909
|
forPublic: use_user?.role_id === 100,
|
|
910
|
+
joinFields: ownershipJoinFields,
|
|
866
911
|
});
|
|
867
912
|
}
|
|
868
913
|
const deleteFileFields = fields.filter((f) => f.type === "File" && f.attributes?.also_delete_file);
|
|
@@ -900,32 +945,47 @@ class Table {
|
|
|
900
945
|
}
|
|
901
946
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
902
947
|
if (rows) {
|
|
903
|
-
const
|
|
948
|
+
const pkIds = rows.map((r) => r[this.pk_name]);
|
|
904
949
|
if (!db_1.default.isSQLite) {
|
|
905
950
|
await db_1.default.deleteWhere(this.name, {
|
|
906
|
-
[this.pk_name]: { in:
|
|
951
|
+
[this.pk_name]: { in: pkIds },
|
|
907
952
|
});
|
|
908
953
|
}
|
|
909
954
|
else {
|
|
910
|
-
await db_1.default.query(`delete from "${db_1.default.sqlsanitize(this.name)}" where "${db_1.default.sqlsanitize(this.pk_name)}" in (${
|
|
955
|
+
await db_1.default.query(`delete from "${db_1.default.sqlsanitize(this.name)}" where "${db_1.default.sqlsanitize(this.pk_name)}" in (${pkIds.join(",")})`);
|
|
911
956
|
}
|
|
912
957
|
for (const row of rows)
|
|
913
958
|
await this.auto_update_calc_aggregations(row);
|
|
914
959
|
if (this.has_sync_info) {
|
|
915
960
|
const dbTime = await db_1.default.time();
|
|
916
|
-
await this.addDeleteSyncInfo(rows, dbTime);
|
|
961
|
+
await this.addDeleteSyncInfo(rows, dbTime, ownerFieldName, this.ownership_formula);
|
|
917
962
|
}
|
|
918
963
|
}
|
|
919
964
|
else {
|
|
920
|
-
|
|
921
|
-
? await db_1.default.select(this.name, where, {
|
|
922
|
-
fields: [this.pk_name],
|
|
923
|
-
})
|
|
924
|
-
: null;
|
|
925
|
-
await db_1.default.deleteWhere(this.name, where);
|
|
965
|
+
let preDeleteRows = null;
|
|
926
966
|
if (this.has_sync_info) {
|
|
967
|
+
if (this.ownership_formula) {
|
|
968
|
+
// ownership_formula: fetch all fields (including join fields) so
|
|
969
|
+
// addDeleteSyncInfo can snapshot them into owner_fields.
|
|
970
|
+
preDeleteRows = await this.getJoinedRows({
|
|
971
|
+
where,
|
|
972
|
+
joinFields: ownershipJoinFields,
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
else {
|
|
976
|
+
// Simple case: fetch only the pk (and owner field if present).
|
|
977
|
+
const selectFields = ownerFieldName
|
|
978
|
+
? [this.pk_name, ownerFieldName]
|
|
979
|
+
: [this.pk_name];
|
|
980
|
+
preDeleteRows = await db_1.default.select(this.name, where, {
|
|
981
|
+
fields: selectFields,
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
await db_1.default.deleteWhere(this.name, where);
|
|
986
|
+
if (this.has_sync_info && preDeleteRows) {
|
|
927
987
|
const dbTime = await db_1.default.time();
|
|
928
|
-
await this.addDeleteSyncInfo(
|
|
988
|
+
await this.addDeleteSyncInfo(preDeleteRows, dbTime, ownerFieldName, this.ownership_formula);
|
|
929
989
|
}
|
|
930
990
|
}
|
|
931
991
|
//if (fields.find((f) => f.primary_key)) await this.resetSequence();
|
|
@@ -961,7 +1021,7 @@ class Table {
|
|
|
961
1021
|
if (this.fields) {
|
|
962
1022
|
for (const f of this.fields) {
|
|
963
1023
|
if (f.type && (0, common_types_1.instanceOfType)(f.type) && f.type.readFromDB)
|
|
964
|
-
row[f.name] = f.type.readFromDB(row[f.name]);
|
|
1024
|
+
row[f.name] = f.type.readFromDB(row[f.name], f);
|
|
965
1025
|
}
|
|
966
1026
|
}
|
|
967
1027
|
return row;
|
|
@@ -1124,9 +1184,20 @@ class Table {
|
|
|
1124
1184
|
* @param fieldnm
|
|
1125
1185
|
* @returns {Promise<Object[]>}
|
|
1126
1186
|
*/
|
|
1127
|
-
async distinctValues(fieldnm, whereObj) {
|
|
1128
|
-
|
|
1129
|
-
|
|
1187
|
+
async distinctValues(fieldnm, whereObj, user) {
|
|
1188
|
+
const useWhere = { ...(whereObj || {}) };
|
|
1189
|
+
if (user &&
|
|
1190
|
+
user.role_id > this.min_role_read &&
|
|
1191
|
+
!(this.ownership_field_id || this.ownership_formula)) {
|
|
1192
|
+
return [];
|
|
1193
|
+
}
|
|
1194
|
+
if (user &&
|
|
1195
|
+
user.role_id > this.min_role_read &&
|
|
1196
|
+
(this.ownership_field_id || this.ownership_formula)) {
|
|
1197
|
+
this.updateWhereWithOwnership(useWhere, user, true);
|
|
1198
|
+
}
|
|
1199
|
+
if (Object.keys(useWhere).length) {
|
|
1200
|
+
const { where, values } = (0, internal_1.mkWhere)(useWhere, db_1.default.isSQLite);
|
|
1130
1201
|
const res = await db_1.default.query(`select distinct "${db_1.default.sqlsanitize(fieldnm)}" from ${this.sql_name} ${where} order by "${db_1.default.sqlsanitize(fieldnm)}" limit 1000`, values);
|
|
1131
1202
|
return res.rows.map((r) => r[fieldnm]);
|
|
1132
1203
|
}
|
|
@@ -1338,7 +1409,7 @@ class Table {
|
|
|
1338
1409
|
joinFields,
|
|
1339
1410
|
});
|
|
1340
1411
|
}
|
|
1341
|
-
let calced = await apply_calculated_fields_stored(need_to_update ? updated || {} : { ...existing, ...v_in }, this.fields, this);
|
|
1412
|
+
let calced = await apply_calculated_fields_stored(need_to_update ? updated || {} : { ...existing, ...v_in }, this.fields, this, use_user);
|
|
1342
1413
|
for (const f of fields)
|
|
1343
1414
|
if (f.calculated && f.stored) {
|
|
1344
1415
|
if (typeof f.type !== "string" &&
|
|
@@ -1480,7 +1551,8 @@ class Table {
|
|
|
1480
1551
|
SELECT MAX(last_modified) "last_modified", ref
|
|
1481
1552
|
FROM ${schema}"${db_1.default.sqlsanitize(this.name)}_sync_info"
|
|
1482
1553
|
GROUP BY ref HAVING ref = ($1)`;
|
|
1483
|
-
const
|
|
1554
|
+
const strIds = ids.map((id) => String(id));
|
|
1555
|
+
const result = await db_1.default.query(sql, db_1.default.isSQLite ? strIds : [strIds]);
|
|
1484
1556
|
return result.rows;
|
|
1485
1557
|
}, (e) => {
|
|
1486
1558
|
require("../db/state")
|
|
@@ -1502,15 +1574,15 @@ class Table {
|
|
|
1502
1574
|
}
|
|
1503
1575
|
await db_1.default.query(`insert into ${schema}"${db_1.default.sqlsanitize(this.name)}_sync_info" (ref, last_modified, updated_fields)
|
|
1504
1576
|
values(
|
|
1505
|
-
$1,
|
|
1577
|
+
$1::text,
|
|
1506
1578
|
date_trunc('milliseconds', to_timestamp($2)),
|
|
1507
1579
|
$3::jsonb
|
|
1508
1580
|
)`, [id, timestamp.valueOf() / 1000.0, JSON.stringify(fieldTimestamps)]);
|
|
1509
1581
|
}
|
|
1510
1582
|
else {
|
|
1511
1583
|
await db_1.default.query(`insert into "${db_1.default.sqlsanitize(this.name)}_sync_info"
|
|
1512
|
-
(ref, modified_local, deleted)
|
|
1513
|
-
values(
|
|
1584
|
+
(ref, modified_local, deleted)
|
|
1585
|
+
values(CAST($1 AS TEXT), true, false)`, [id]);
|
|
1514
1586
|
}
|
|
1515
1587
|
}, (e) => {
|
|
1516
1588
|
require("../db/state")
|
|
@@ -1531,13 +1603,13 @@ class Table {
|
|
|
1531
1603
|
for (const k of Object.keys(v).filter((key) => key !== this.pk_name)) {
|
|
1532
1604
|
fieldTimestamps[k] = timestamp;
|
|
1533
1605
|
}
|
|
1534
|
-
await db_1.default.query(`update ${schema}"${db_1.default.sqlsanitize(this.name)}_sync_info"
|
|
1535
|
-
set
|
|
1606
|
+
await db_1.default.query(`update ${schema}"${db_1.default.sqlsanitize(this.name)}_sync_info"
|
|
1607
|
+
set
|
|
1536
1608
|
last_modified=date_trunc('milliseconds', to_timestamp($1)),
|
|
1537
1609
|
updated_fields =
|
|
1538
1610
|
coalesce(updated_fields, '{}'::jsonb) || $4::jsonb
|
|
1539
|
-
where
|
|
1540
|
-
ref=$2 and last_modified = to_timestamp($3)`, [
|
|
1611
|
+
where
|
|
1612
|
+
ref=$2::text and last_modified = to_timestamp($3)`, [
|
|
1541
1613
|
timestamp.valueOf() / 1000.0,
|
|
1542
1614
|
id,
|
|
1543
1615
|
oldLastModified.valueOf() / 1000.0,
|
|
@@ -1545,8 +1617,8 @@ class Table {
|
|
|
1545
1617
|
]);
|
|
1546
1618
|
}
|
|
1547
1619
|
else {
|
|
1548
|
-
await db_1.default.query(`update "${db_1.default.sqlsanitize(this.name)}_sync_info" set modified_local = true
|
|
1549
|
-
where ref = $
|
|
1620
|
+
await db_1.default.query(`update "${db_1.default.sqlsanitize(this.name)}_sync_info" set modified_local = true
|
|
1621
|
+
where ref = CAST($1 AS TEXT) and last_modified = $2`, [id, oldLastModified ? oldLastModified.valueOf() : null]);
|
|
1550
1622
|
}
|
|
1551
1623
|
}, (e) => {
|
|
1552
1624
|
require("../db/state")
|
|
@@ -1758,11 +1830,37 @@ class Table {
|
|
|
1758
1830
|
return valResCollector; //???
|
|
1759
1831
|
if ("set_fields" in valResCollector)
|
|
1760
1832
|
Object.assign(v_in, valResCollector.set_fields);
|
|
1833
|
+
// Apply expression defaults for fields not provided or left null by the caller
|
|
1834
|
+
for (const field of fields) {
|
|
1835
|
+
if (!field.primary_key &&
|
|
1836
|
+
field.attributes?.default_expression &&
|
|
1837
|
+
(!(field.name in v_in) || v_in[field.name] == null)) {
|
|
1838
|
+
try {
|
|
1839
|
+
const exprFn = get_async_expression_function(field.attributes.default_expression, fields, {});
|
|
1840
|
+
v_in[field.name] = await exprFn(v_in, use_user);
|
|
1841
|
+
}
|
|
1842
|
+
catch (_e) {
|
|
1843
|
+
state.log(4, `Error applying default_expression for field ${field.name}: ${_e.message}`);
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
// On mobile (SQLite), PKs with a client-side default (e.g. UUID via the
|
|
1848
|
+
// uuid-type plugin's default_js) must be generated before the insert.
|
|
1849
|
+
if (!isNode() && v_in[pk_name] == null) {
|
|
1850
|
+
const pkField = fields?.find((f) => f.primary_key && !f.is_fkey);
|
|
1851
|
+
const defaultJs = pkField?.type?.primaryKey?.default_js;
|
|
1852
|
+
if (typeof defaultJs === "function") {
|
|
1853
|
+
v_in[pk_name] = defaultJs();
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1761
1856
|
if (Object.keys(joinFields).length > 0 ||
|
|
1762
1857
|
fields.some((f) => f.expression === "__aggregation")) {
|
|
1763
1858
|
state.log(6, `Inserting ${this.name} because join fields: ${JSON.stringify(v_in)}`);
|
|
1764
1859
|
this.prepare_row_for_writing(v_in);
|
|
1765
1860
|
id = await db_1.default.insert(this.name, v_in, { pk_name, ...sqliteJsonCols });
|
|
1861
|
+
// db.insert returns SQLite rowid, not the PK for non-integer PK types
|
|
1862
|
+
if (!isNode() && v_in[pk_name] != null)
|
|
1863
|
+
id = v_in[pk_name];
|
|
1766
1864
|
let existing = await this.getJoinedRows({
|
|
1767
1865
|
where: { [pk_name]: id },
|
|
1768
1866
|
joinFields,
|
|
@@ -1775,7 +1873,7 @@ class Table {
|
|
|
1775
1873
|
state.log(4, `Not authorized to insertRow in table ${this.name}. Inserted row not retrieved.`);
|
|
1776
1874
|
return;
|
|
1777
1875
|
}
|
|
1778
|
-
let calced = await apply_calculated_fields_stored(existing[0], fields, this);
|
|
1876
|
+
let calced = await apply_calculated_fields_stored(existing[0], fields, this, use_user);
|
|
1779
1877
|
v = { ...v_in };
|
|
1780
1878
|
for (const f of fields)
|
|
1781
1879
|
if (f.calculated && f.stored)
|
|
@@ -1784,13 +1882,16 @@ class Table {
|
|
|
1784
1882
|
await db_1.default.update(this.name, v, id, { pk_name, ...sqliteJsonCols });
|
|
1785
1883
|
}
|
|
1786
1884
|
else {
|
|
1787
|
-
v = await apply_calculated_fields_stored(v_in, fields, this);
|
|
1885
|
+
v = await apply_calculated_fields_stored(v_in, fields, this, use_user);
|
|
1788
1886
|
this.prepare_row_for_writing(v);
|
|
1789
1887
|
state.log(6, `Inserting ${this.name} row: ${JSON.stringify(v)}`);
|
|
1790
1888
|
id = await db_1.default.insert(this.name, v, {
|
|
1791
1889
|
pk_name,
|
|
1792
1890
|
...sqliteJsonCols,
|
|
1793
1891
|
});
|
|
1892
|
+
// db.insert returns SQLite rowid, not the PK for non-integer PK types
|
|
1893
|
+
if (!isNode() && v[pk_name] != null)
|
|
1894
|
+
id = v[pk_name];
|
|
1794
1895
|
}
|
|
1795
1896
|
if (use_user &&
|
|
1796
1897
|
use_user.role_id > this.min_role_write &&
|
|
@@ -1822,15 +1923,15 @@ class Table {
|
|
|
1822
1923
|
if (isNode()) {
|
|
1823
1924
|
// sync_info for insert
|
|
1824
1925
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
1926
|
+
const tsParam = (syncTimestamp ? syncTimestamp : await db_1.default.time()).valueOf() /
|
|
1927
|
+
1000.0;
|
|
1825
1928
|
await db_1.default.query(`insert into ${schemaPrefix}"${db_1.default.sqlsanitize(this.name)}_sync_info"
|
|
1826
|
-
(ref, last_modified) values(
|
|
1827
|
-
${id}, date_trunc('milliseconds', to_timestamp(${(syncTimestamp ? syncTimestamp : await db_1.default.time()).valueOf() /
|
|
1828
|
-
1000.0})))`);
|
|
1929
|
+
(ref, last_modified) values($1::text, date_trunc('milliseconds', to_timestamp($2)))`, [id, tsParam]);
|
|
1829
1930
|
}
|
|
1830
1931
|
else {
|
|
1831
1932
|
await db_1.default.query(`insert into "${db_1.default.sqlsanitize(this.name)}_sync_info"
|
|
1832
1933
|
(last_modified, ref, modified_local, deleted)
|
|
1833
|
-
values(NULL, $
|
|
1934
|
+
values(NULL, CAST($1 AS TEXT), true, false)`, [id]);
|
|
1834
1935
|
}
|
|
1835
1936
|
}, (e) => {
|
|
1836
1937
|
state.log(2, `Error inserting sync info for table ${this.name}: ${e.message}`);
|
|
@@ -2142,10 +2243,12 @@ class Table {
|
|
|
2142
2243
|
throw new Error("Unable to find a field with a primary key.");
|
|
2143
2244
|
}
|
|
2144
2245
|
await db_1.default.query(`create table ${schemaPrefix}"${(0, internal_1.sqlsanitize)(this.name)}_sync_info" (
|
|
2145
|
-
ref
|
|
2246
|
+
ref text not null,
|
|
2146
2247
|
last_modified timestamp,
|
|
2147
2248
|
deleted boolean default false,
|
|
2148
|
-
updated_fields jsonb
|
|
2249
|
+
updated_fields jsonb,
|
|
2250
|
+
owner_id integer,
|
|
2251
|
+
owner_fields jsonb)`);
|
|
2149
2252
|
await db_1.default.query(`create index "${(0, internal_1.sqlsanitize)(this.name)}_sync_info_ref_index" on ${schemaPrefix}"${(0, internal_1.sqlsanitize)(this.name)}_sync_info"(ref)`);
|
|
2150
2253
|
await db_1.default.query(`create index "${(0, internal_1.sqlsanitize)(this.name)}_sync_info_lm_index" on ${schemaPrefix}"${(0, internal_1.sqlsanitize)(this.name)}_sync_info"(last_modified)`);
|
|
2151
2254
|
await db_1.default.query(`create index "${(0, internal_1.sqlsanitize)(this.name)}_sync_info_deleted_index" on ${schemaPrefix}"${(0, internal_1.sqlsanitize)(this.name)}_sync_info"(deleted)`);
|
|
@@ -2334,6 +2437,7 @@ class Table {
|
|
|
2334
2437
|
throw new Error(`Unable to find table with id: ${this.id}`);
|
|
2335
2438
|
}
|
|
2336
2439
|
const { external, fields, constraints, ...upd_rec } = new_table_rec;
|
|
2440
|
+
upd_rec.updated_at = new Date();
|
|
2337
2441
|
await db_1.default.update("_sc_tables", upd_rec, this.id);
|
|
2338
2442
|
//limited refresh if we do not have a client
|
|
2339
2443
|
if (!db_1.default.getRequestContext()?.client)
|