remult 0.17.3 → 0.18.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/SseSubscriptionServer.d.ts +23 -0
- package/SseSubscriptionServer.js +143 -0
- package/ably.d.ts +13 -0
- package/ably.js +59 -0
- package/esm/SseSubscriptionServer.d.ts +23 -0
- package/esm/SseSubscriptionServer.js +102 -0
- package/esm/ably.d.ts +13 -0
- package/esm/ably.js +28 -0
- package/esm/index.d.ts +7 -4
- package/esm/index.js +4 -2
- package/esm/live-query/data-provider-live-query-storage.d.ts +23 -0
- package/esm/live-query/data-provider-live-query-storage.js +78 -0
- package/esm/myLog.d.ts +1 -0
- package/esm/myLog.js +5 -0
- package/esm/postgres/postgres-data-provider.d.ts +4 -2
- package/esm/postgres/postgres-data-provider.js +18 -6
- package/esm/postgres/schema-builder.d.ts +2 -1
- package/esm/postgres/schema-builder.js +49 -29
- package/esm/remult-knex/index.d.ts +10 -32
- package/esm/remult-knex/index.js +75 -51
- package/esm/remult-mongo.d.ts +9 -2
- package/esm/remult-mongo.js +24 -8
- package/esm/remult-next.d.ts +11 -0
- package/esm/remult-next.js +27 -0
- package/esm/server/core.d.ts +2 -0
- package/esm/server/core.js +2 -0
- package/esm/server/expressBridge.d.ts +19 -7
- package/esm/server/expressBridge.js +135 -31
- package/esm/server/index.d.ts +2 -1
- package/esm/server/index.js +2 -1
- package/esm/server/initDataProvider.d.ts +2 -0
- package/esm/server/initDataProvider.js +14 -0
- package/esm/src/buildRestDataProvider.d.ts +31 -0
- package/esm/src/buildRestDataProvider.js +110 -0
- package/esm/src/column-interfaces.d.ts +44 -8
- package/esm/src/column.d.ts +3 -3
- package/esm/src/context.d.ts +39 -36
- package/esm/src/context.js +87 -117
- package/esm/src/data-api.d.ts +9 -3
- package/esm/src/data-api.js +130 -63
- package/esm/src/data-interfaces.d.ts +5 -1
- package/esm/src/data-providers/array-entity-data-provider.d.ts +1 -1
- package/esm/src/data-providers/array-entity-data-provider.js +2 -17
- package/esm/src/data-providers/rest-data-provider.d.ts +4 -3
- package/esm/src/data-providers/rest-data-provider.js +64 -23
- package/esm/src/data-providers/sql-database.d.ts +7 -3
- package/esm/src/data-providers/sql-database.js +60 -17
- package/esm/src/data-providers/web-sql-data-provider.d.ts +3 -0
- package/esm/src/data-providers/web-sql-data-provider.js +23 -9
- package/esm/src/entity.d.ts +2 -2
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.d.ts +16 -16
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +29 -21
- package/esm/src/filter/filter-interfaces.d.ts +5 -5
- package/esm/src/filter/filter-interfaces.js +31 -26
- package/esm/src/live-query/LiveQueryClient.d.ts +1 -0
- package/esm/src/live-query/LiveQueryClient.js +184 -0
- package/esm/src/live-query/SseSubscriptionClient.d.ts +5 -0
- package/esm/src/live-query/SseSubscriptionClient.js +86 -0
- package/esm/src/live-query/SubscriptionChannel.d.ts +41 -0
- package/esm/src/live-query/SubscriptionChannel.js +121 -0
- package/esm/src/live-query/SubscriptionServer.d.ts +38 -0
- package/esm/src/live-query/SubscriptionServer.js +108 -0
- package/esm/src/remult-proxy.d.ts +1 -1
- package/esm/src/remult-proxy.js +52 -7
- package/esm/src/remult3/RepositoryImplementation.d.ts +14 -5
- package/esm/src/remult3/RepositoryImplementation.js +124 -44
- package/esm/src/remult3/getId.d.ts +2 -0
- package/esm/src/remult3/getId.js +7 -0
- package/esm/src/remult3/remult3.d.ts +67 -10
- package/esm/src/remult3/remult3.js +1 -0
- package/esm/src/server-action.d.ts +5 -1
- package/esm/src/server-action.js +11 -12
- package/esm/src/shared-tests/db-tests-setup.d.ts +12 -5
- package/esm/src/shared-tests/db-tests-setup.js +14 -6
- package/esm/src/shared-tests/db-tests.db-tests.js +71 -10
- package/esm/src/sort.d.ts +1 -0
- package/esm/src/sort.js +18 -0
- package/esm/src/sql-command.d.ts +5 -2
- package/esm/src/tests/entityForCustomFilter.d.ts +6 -6
- package/esm/src/tests/entityForCustomFilter.js +20 -21
- package/esm/src/tests/frontend-database-tests-setup.spec.js +3 -4
- package/esm/src/tests/testHelper.spec.d.ts +3 -1
- package/esm/src/tests/testHelper.spec.js +63 -60
- package/esm/src/valueConverters.js +15 -7
- package/index.d.ts +7 -4
- package/index.js +7 -3
- package/live-query/data-provider-live-query-storage.d.ts +23 -0
- package/live-query/data-provider-live-query-storage.js +236 -0
- package/myLog.d.ts +1 -0
- package/myLog.js +20 -0
- package/package.json +18 -1
- package/postgres/postgres-data-provider.d.ts +4 -2
- package/postgres/postgres-data-provider.js +54 -39
- package/postgres/schema-builder.d.ts +2 -1
- package/postgres/schema-builder.js +118 -70
- package/remult-express.js +1 -1
- package/remult-knex/index.d.ts +10 -32
- package/remult-knex/index.js +134 -87
- package/remult-mongo.d.ts +9 -2
- package/remult-mongo.js +39 -7
- package/remult-next.d.ts +11 -0
- package/remult-next.js +73 -0
- package/server/JsonEntityFileStorage.js +2 -2
- package/server/core.d.ts +2 -0
- package/server/core.js +5 -1
- package/server/expressBridge.d.ts +19 -7
- package/server/expressBridge.js +283 -69
- package/server/index.d.ts +2 -1
- package/server/index.js +3 -1
- package/server/initDataProvider.d.ts +2 -0
- package/server/initDataProvider.js +26 -0
- package/src/buildRestDataProvider.d.ts +31 -0
- package/src/buildRestDataProvider.js +162 -0
- package/src/column-interfaces.d.ts +44 -8
- package/src/column.d.ts +3 -3
- package/src/context.d.ts +39 -36
- package/src/context.js +171 -166
- package/src/data-api.d.ts +9 -3
- package/src/data-api.js +233 -88
- package/src/data-interfaces.d.ts +5 -1
- package/src/data-providers/array-entity-data-provider.d.ts +1 -1
- package/src/data-providers/array-entity-data-provider.js +2 -17
- package/src/data-providers/rest-data-provider.d.ts +4 -3
- package/src/data-providers/rest-data-provider.js +105 -39
- package/src/data-providers/sql-database.d.ts +7 -3
- package/src/data-providers/sql-database.js +89 -16
- package/src/data-providers/web-sql-data-provider.d.ts +3 -0
- package/src/data-providers/web-sql-data-provider.js +56 -11
- package/src/entity.d.ts +2 -2
- package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +16 -16
- package/src/filter/filter-consumer-bridge-to-sql-request.js +59 -50
- package/src/filter/filter-interfaces.d.ts +5 -5
- package/src/filter/filter-interfaces.js +31 -26
- package/src/live-query/LiveQueryClient.d.ts +1 -0
- package/src/live-query/LiveQueryClient.js +283 -0
- package/src/live-query/SseSubscriptionClient.d.ts +5 -0
- package/src/live-query/SseSubscriptionClient.js +150 -0
- package/src/live-query/SubscriptionChannel.d.ts +41 -0
- package/src/live-query/SubscriptionChannel.js +209 -0
- package/src/live-query/SubscriptionServer.d.ts +38 -0
- package/src/live-query/SubscriptionServer.js +258 -0
- package/src/remult-proxy.d.ts +1 -1
- package/src/remult-proxy.js +153 -8
- package/src/remult3/RepositoryImplementation.d.ts +14 -5
- package/src/remult3/RepositoryImplementation.js +200 -85
- package/src/remult3/getId.d.ts +2 -0
- package/src/remult3/getId.js +11 -0
- package/src/remult3/remult3.d.ts +67 -10
- package/src/remult3/remult3.js +1 -0
- package/src/server-action.d.ts +5 -1
- package/src/server-action.js +11 -11
- package/src/shared-tests/db-tests-setup.d.ts +12 -5
- package/src/shared-tests/db-tests-setup.js +17 -8
- package/src/shared-tests/db-tests.db-tests.js +140 -8
- package/src/sort.d.ts +1 -0
- package/src/sort.js +18 -0
- package/src/sql-command.d.ts +5 -2
- package/src/tests/entityForCustomFilter.d.ts +6 -6
- package/src/tests/entityForCustomFilter.js +30 -31
- package/src/tests/frontend-database-tests-setup.spec.js +5 -6
- package/src/tests/testHelper.spec.d.ts +3 -1
- package/src/tests/testHelper.spec.js +104 -100
- package/src/valueConverters.js +15 -7
|
@@ -3,9 +3,12 @@ import { allEntities, Remult, isBackend, queryConfig as queryConfig, setControll
|
|
|
3
3
|
import { entityFilterToJson, Filter } from "../filter/filter-interfaces";
|
|
4
4
|
import { Sort } from "../sort";
|
|
5
5
|
import { v4 as uuid } from 'uuid';
|
|
6
|
+
import cuid from 'cuid';
|
|
6
7
|
import { ValueConverters } from "../valueConverters";
|
|
7
8
|
import { filterHelper } from "../filter/filter-interfaces";
|
|
8
9
|
import { assign } from "../../assign";
|
|
10
|
+
import { remult as defaultRemult, RemultProxy } from "../remult-proxy";
|
|
11
|
+
import { getId } from "./getId";
|
|
9
12
|
//import { remult } from "../remult-proxy";
|
|
10
13
|
let classValidatorValidate = undefined;
|
|
11
14
|
// import("class-validator".toString())
|
|
@@ -165,8 +168,10 @@ export class RepositoryImplementation {
|
|
|
165
168
|
instance[field.key] = entity[field.key];
|
|
166
169
|
}
|
|
167
170
|
let row = new rowHelperImplementation(this._info, instance, this, this.edp, this.remult, false);
|
|
168
|
-
if (id)
|
|
171
|
+
if (id) {
|
|
169
172
|
row.id = id;
|
|
173
|
+
row.originalId = id;
|
|
174
|
+
}
|
|
170
175
|
else
|
|
171
176
|
row.id = row.getId();
|
|
172
177
|
ref = row;
|
|
@@ -195,19 +200,30 @@ export class RepositoryImplementation {
|
|
|
195
200
|
}
|
|
196
201
|
}
|
|
197
202
|
}
|
|
203
|
+
liveQuery(options) {
|
|
204
|
+
if (!options)
|
|
205
|
+
options = {};
|
|
206
|
+
return {
|
|
207
|
+
subscribe: (l) => {
|
|
208
|
+
let listener = l;
|
|
209
|
+
if (typeof l === "function") {
|
|
210
|
+
listener = {
|
|
211
|
+
next: l,
|
|
212
|
+
complete: () => { },
|
|
213
|
+
error: () => { }
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
listener.error ??= () => { };
|
|
217
|
+
listener.complete ??= () => { };
|
|
218
|
+
return this.remult.liveQuerySubscriber.subscribe(this, options, listener);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
198
222
|
async find(options) {
|
|
199
223
|
Remult.onFind(this._info, options);
|
|
200
|
-
let opt = {};
|
|
201
224
|
if (!options)
|
|
202
225
|
options = {};
|
|
203
|
-
opt =
|
|
204
|
-
if (!options.orderBy || Object.keys(options.orderBy).length === 0) {
|
|
205
|
-
options.orderBy = this._info.entityInfo.defaultOrderBy;
|
|
206
|
-
}
|
|
207
|
-
opt.where = await this.translateWhereToFilter(options.where);
|
|
208
|
-
opt.orderBy = Sort.translateOrderByToSort(this.metadata, options.orderBy);
|
|
209
|
-
opt.limit = options.limit;
|
|
210
|
-
opt.page = options.page;
|
|
226
|
+
let opt = await this.buildEntityDataProviderFindOptions(options);
|
|
211
227
|
let rawRows = await this.edp.find(opt);
|
|
212
228
|
let loadFields = undefined;
|
|
213
229
|
if (options.load)
|
|
@@ -242,6 +258,18 @@ export class RepositoryImplementation {
|
|
|
242
258
|
}
|
|
243
259
|
}
|
|
244
260
|
}
|
|
261
|
+
async buildEntityDataProviderFindOptions(options) {
|
|
262
|
+
let opt = {};
|
|
263
|
+
opt = {};
|
|
264
|
+
if (!options.orderBy || Object.keys(options.orderBy).length === 0) {
|
|
265
|
+
options.orderBy = this._info.entityInfo.defaultOrderBy;
|
|
266
|
+
}
|
|
267
|
+
opt.where = await this.translateWhereToFilter(options.where);
|
|
268
|
+
opt.orderBy = Sort.translateOrderByToSort(this.metadata, options.orderBy);
|
|
269
|
+
opt.limit = options.limit;
|
|
270
|
+
opt.page = options.page;
|
|
271
|
+
return opt;
|
|
272
|
+
}
|
|
245
273
|
async mapRawDataToResult(r, loadFields) {
|
|
246
274
|
if (!r)
|
|
247
275
|
return undefined;
|
|
@@ -355,6 +383,7 @@ export class RepositoryImplementation {
|
|
|
355
383
|
where: this.metadata.idMetadata.getIdFilter(id),
|
|
356
384
|
});
|
|
357
385
|
}
|
|
386
|
+
/* @internal*/
|
|
358
387
|
async translateWhereToFilter(where) {
|
|
359
388
|
if (this.metadata.options.backendPrefilter && isBackend()) {
|
|
360
389
|
let z = where;
|
|
@@ -365,7 +394,7 @@ export class RepositoryImplementation {
|
|
|
365
394
|
};
|
|
366
395
|
}
|
|
367
396
|
let r = await Filter.fromEntityFilter(this.metadata, where);
|
|
368
|
-
if (r && !this.dataProvider.
|
|
397
|
+
if (r && !this.dataProvider.supportsRawFilter) {
|
|
369
398
|
r = await Filter.translateCustomWhere(r, this.metadata, this.remult);
|
|
370
399
|
}
|
|
371
400
|
return r;
|
|
@@ -452,6 +481,12 @@ class rowHelperBase {
|
|
|
452
481
|
this._isLoading = false;
|
|
453
482
|
this.lookups = new Map();
|
|
454
483
|
this.originalValues = {};
|
|
484
|
+
{
|
|
485
|
+
let fac = remult;
|
|
486
|
+
if (fac != null && fac.remultFactory) {
|
|
487
|
+
remult = fac.remultFactory();
|
|
488
|
+
}
|
|
489
|
+
}
|
|
455
490
|
for (const col of columnsInfo) {
|
|
456
491
|
let ei = getEntitySettings(col.valueType, false);
|
|
457
492
|
if (ei && remult) {
|
|
@@ -622,6 +657,9 @@ class rowHelperBase {
|
|
|
622
657
|
}
|
|
623
658
|
saveOriginalData() {
|
|
624
659
|
this.originalValues = this.copyDataToObject();
|
|
660
|
+
this.saveMoreOriginalData();
|
|
661
|
+
}
|
|
662
|
+
saveMoreOriginalData() {
|
|
625
663
|
}
|
|
626
664
|
async validate() {
|
|
627
665
|
this.__clearErrorsAndReportChanged();
|
|
@@ -711,10 +749,10 @@ export class rowHelperImplementation extends rowHelperBase {
|
|
|
711
749
|
get apiDeleteAllowed() { return this.remult.isAllowedForInstance(this.instance, this.metadata.options.allowApiDelete); }
|
|
712
750
|
get apiInsertAllowed() { return this.remult.isAllowedForInstance(this.instance, this.metadata.options.allowApiInsert); }
|
|
713
751
|
getId() {
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
752
|
+
return getId(this.info, this.instance);
|
|
753
|
+
}
|
|
754
|
+
saveMoreOriginalData() {
|
|
755
|
+
this.originalId = this.getId();
|
|
718
756
|
}
|
|
719
757
|
wasDeleted() {
|
|
720
758
|
this._subscribers?.reportObserved();
|
|
@@ -807,6 +845,7 @@ export class rowHelperImplementation extends rowHelperBase {
|
|
|
807
845
|
await this.loadDataFrom(updatedRow);
|
|
808
846
|
if (this.info.entityInfo.saved)
|
|
809
847
|
await this.info.entityInfo.saved(this.instance);
|
|
848
|
+
await this.repository.remult.liveQueryPublisher.itemChanged(this.repository.metadata.key, [{ id: this.getId(), oldId: this.getOriginalId(), deleted: false }]);
|
|
810
849
|
this.saveOriginalData();
|
|
811
850
|
this._isNew = false;
|
|
812
851
|
return this.instance;
|
|
@@ -837,6 +876,7 @@ export class rowHelperImplementation extends rowHelperBase {
|
|
|
837
876
|
for (const listener of this.repository.listeners.filter(x => x.deleted)) {
|
|
838
877
|
await listener.deleted(this.instance);
|
|
839
878
|
}
|
|
879
|
+
await this.repository.remult.liveQueryPublisher.itemChanged(this.repository.metadata.key, [{ id: this.getId(), oldId: this.getOriginalId(), deleted: true }]);
|
|
840
880
|
this._wasDeleted = true;
|
|
841
881
|
}
|
|
842
882
|
catch (err) {
|
|
@@ -861,14 +901,10 @@ export class rowHelperImplementation extends rowHelperBase {
|
|
|
861
901
|
this.instance[col.key] = data[col.key];
|
|
862
902
|
}
|
|
863
903
|
await this.calcServerExpression();
|
|
864
|
-
|
|
865
|
-
this.id = this.repository.metadata.idMetadata.field.getId(this.instance);
|
|
866
|
-
}
|
|
867
|
-
else
|
|
868
|
-
this.id = data[this.repository.metadata.idMetadata.field.key];
|
|
904
|
+
this.id = getId(this.info, this.instance);
|
|
869
905
|
}
|
|
870
906
|
getOriginalId() {
|
|
871
|
-
return this.
|
|
907
|
+
return this.originalId;
|
|
872
908
|
}
|
|
873
909
|
async calcServerExpression() {
|
|
874
910
|
if (isBackend())
|
|
@@ -913,7 +949,7 @@ export function getFields(container, remult) {
|
|
|
913
949
|
return getControllerRef(container, remult).fields;
|
|
914
950
|
}
|
|
915
951
|
export function getControllerRef(container, remultArg) {
|
|
916
|
-
const remultVar = remultArg ||
|
|
952
|
+
const remultVar = remultArg || defaultRemult;
|
|
917
953
|
let result = container[controllerColumns];
|
|
918
954
|
if (!result)
|
|
919
955
|
result = container[entityMember];
|
|
@@ -1142,24 +1178,23 @@ export class columnDefsImpl {
|
|
|
1142
1178
|
this.inputType = this.valueConverter.inputType;
|
|
1143
1179
|
this.caption = buildCaption(settings.caption, settings.key, remult);
|
|
1144
1180
|
}
|
|
1145
|
-
getDbName() {
|
|
1146
|
-
|
|
1147
|
-
return this.dbNamePromise;
|
|
1148
|
-
this.dbNamePromise = (async () => {
|
|
1181
|
+
async getDbName() {
|
|
1182
|
+
try {
|
|
1149
1183
|
if (this.settings.sqlExpression) {
|
|
1184
|
+
let result;
|
|
1150
1185
|
if (typeof this.settings.sqlExpression === "function") {
|
|
1151
|
-
|
|
1186
|
+
result = await this.settings.sqlExpression(this.entityDefs);
|
|
1152
1187
|
}
|
|
1153
1188
|
else
|
|
1154
|
-
|
|
1189
|
+
result = this.settings.sqlExpression;
|
|
1190
|
+
if (!result)
|
|
1191
|
+
return this.settings.dbName;
|
|
1192
|
+
return result;
|
|
1155
1193
|
}
|
|
1156
1194
|
return this.settings.dbName;
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
return this.settings.dbName;
|
|
1161
|
-
});
|
|
1162
|
-
return this.dbNamePromise;
|
|
1195
|
+
}
|
|
1196
|
+
finally {
|
|
1197
|
+
}
|
|
1163
1198
|
}
|
|
1164
1199
|
get dbName() {
|
|
1165
1200
|
let result;
|
|
@@ -1273,7 +1308,7 @@ class EntityFullInfo {
|
|
|
1273
1308
|
if (typeof this.options.sqlExpression === "string")
|
|
1274
1309
|
this.dbNamePromise = Promise.resolve(this.options.sqlExpression);
|
|
1275
1310
|
else if (typeof this.options.sqlExpression === "function") {
|
|
1276
|
-
let r = this.options.sqlExpression(this
|
|
1311
|
+
let r = this.options.sqlExpression(this);
|
|
1277
1312
|
if (r instanceof Promise)
|
|
1278
1313
|
this.dbNamePromise = r;
|
|
1279
1314
|
else if (r)
|
|
@@ -1324,10 +1359,39 @@ export class Fields {
|
|
|
1324
1359
|
static number(...options) {
|
|
1325
1360
|
return Field(() => Number, ...options);
|
|
1326
1361
|
}
|
|
1362
|
+
static createdAt(...options) {
|
|
1363
|
+
return Field(() => Date, {
|
|
1364
|
+
saving: (_, ref) => {
|
|
1365
|
+
if (getEntityRef(_).isNew())
|
|
1366
|
+
ref.value = new Date();
|
|
1367
|
+
}
|
|
1368
|
+
}, ...options);
|
|
1369
|
+
}
|
|
1370
|
+
static updatedAt(...options) {
|
|
1371
|
+
return Field(() => Date, {
|
|
1372
|
+
saving: (_, ref) => {
|
|
1373
|
+
ref.value = new Date();
|
|
1374
|
+
}
|
|
1375
|
+
}, ...options);
|
|
1376
|
+
}
|
|
1327
1377
|
static uuid(...options) {
|
|
1328
1378
|
return Field(() => String, {
|
|
1329
1379
|
allowApiUpdate: false,
|
|
1330
|
-
defaultValue: () => uuid()
|
|
1380
|
+
defaultValue: () => uuid(),
|
|
1381
|
+
saving: (_, r) => {
|
|
1382
|
+
if (!r.value)
|
|
1383
|
+
r.value = uuid();
|
|
1384
|
+
}
|
|
1385
|
+
}, ...options);
|
|
1386
|
+
}
|
|
1387
|
+
static cuid(...options) {
|
|
1388
|
+
return Field(() => String, {
|
|
1389
|
+
allowApiUpdate: false,
|
|
1390
|
+
defaultValue: () => cuid(),
|
|
1391
|
+
saving: (_, r) => {
|
|
1392
|
+
if (!r.value)
|
|
1393
|
+
r.value = cuid();
|
|
1394
|
+
}
|
|
1331
1395
|
}, ...options);
|
|
1332
1396
|
}
|
|
1333
1397
|
static string(...options) {
|
|
@@ -1344,7 +1408,7 @@ export function ValueListFieldType(...options) {
|
|
|
1344
1408
|
return (type) => {
|
|
1345
1409
|
FieldType(o => {
|
|
1346
1410
|
o.valueConverter = ValueListInfo.get(type),
|
|
1347
|
-
o.displayValue = (item, val) => val
|
|
1411
|
+
o.displayValue = (item, val) => val?.caption;
|
|
1348
1412
|
}, ...options)(type);
|
|
1349
1413
|
};
|
|
1350
1414
|
}
|
|
@@ -1604,12 +1668,12 @@ export function decorateColumnSettings(settings, remult) {
|
|
|
1604
1668
|
*/
|
|
1605
1669
|
export function Entity(key, ...options) {
|
|
1606
1670
|
return target => {
|
|
1607
|
-
for (const
|
|
1608
|
-
if (Object.prototype.hasOwnProperty.call(target,
|
|
1609
|
-
const element = target[
|
|
1610
|
-
if (element?.
|
|
1611
|
-
if (!element.
|
|
1612
|
-
element.
|
|
1671
|
+
for (const rawFilterMember in target) {
|
|
1672
|
+
if (Object.prototype.hasOwnProperty.call(target, rawFilterMember)) {
|
|
1673
|
+
const element = target[rawFilterMember];
|
|
1674
|
+
if (element?.rawFilterInfo?.rawFilterTranslator) {
|
|
1675
|
+
if (!element.rawFilterInfo.key)
|
|
1676
|
+
element.rawFilterInfo.key = rawFilterMember;
|
|
1613
1677
|
}
|
|
1614
1678
|
}
|
|
1615
1679
|
}
|
|
@@ -1658,7 +1722,7 @@ export class EntityBase {
|
|
|
1658
1722
|
}
|
|
1659
1723
|
export class ControllerBase {
|
|
1660
1724
|
constructor(remult) {
|
|
1661
|
-
this.remult = remult ||
|
|
1725
|
+
this.remult = remult || defaultRemult;
|
|
1662
1726
|
}
|
|
1663
1727
|
assign(values) {
|
|
1664
1728
|
assign(this, values);
|
|
@@ -1797,3 +1861,19 @@ class SubscribableImp {
|
|
|
1797
1861
|
return () => this._subscribers = this._subscribers.filter(x => x != list);
|
|
1798
1862
|
}
|
|
1799
1863
|
}
|
|
1864
|
+
export function getEntityMetadata(entity) {
|
|
1865
|
+
if (entity.metadata)
|
|
1866
|
+
return entity.metadata;
|
|
1867
|
+
const settings = getEntitySettings(entity, false);
|
|
1868
|
+
if (settings) {
|
|
1869
|
+
return RemultProxy.defaultRemult.repo(entity).metadata;
|
|
1870
|
+
}
|
|
1871
|
+
return entity;
|
|
1872
|
+
}
|
|
1873
|
+
export function getRepository(entity) {
|
|
1874
|
+
const settings = getEntitySettings(entity, false);
|
|
1875
|
+
if (settings) {
|
|
1876
|
+
return RemultProxy.defaultRemult.repo(entity);
|
|
1877
|
+
}
|
|
1878
|
+
return entity;
|
|
1879
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { ClassType } from "../../classType";
|
|
2
2
|
import { FieldMetadata } from "../column-interfaces";
|
|
3
3
|
import { Unobserve } from "../context";
|
|
4
|
-
import {
|
|
4
|
+
import { LiveQueryChange, SubscriptionListener, Unsubscribe } from "../live-query/SubscriptionChannel";
|
|
5
|
+
import { EntityOptions } from "../entity";
|
|
5
6
|
import { SortSegment } from "../sort";
|
|
6
7
|
import { entityEventListener } from "../__EntityValueProvider";
|
|
7
8
|
export interface EntityRef<entityType> extends Subscribable {
|
|
@@ -15,7 +16,8 @@ export interface EntityRef<entityType> extends Subscribable {
|
|
|
15
16
|
wasDeleted(): boolean;
|
|
16
17
|
fields: FieldsRef<entityType>;
|
|
17
18
|
error: string;
|
|
18
|
-
getId():
|
|
19
|
+
getId(): idType<entityType>;
|
|
20
|
+
getOriginalId(): idType<entityType>;
|
|
19
21
|
repository: Repository<entityType>;
|
|
20
22
|
metadata: EntityMetadata<entityType>;
|
|
21
23
|
toApiJson(): any;
|
|
@@ -94,24 +96,70 @@ export interface IdMetadata<entityType = any> {
|
|
|
94
96
|
isIdField(col: FieldMetadata): boolean;
|
|
95
97
|
createIdInFilter(items: entityType[]): EntityFilter<entityType>;
|
|
96
98
|
}
|
|
99
|
+
/** Metadata for an `Entity`, this metadata can be used in the user interface to provide a richer UI experience */
|
|
97
100
|
export interface EntityMetadata<entityType = any> {
|
|
98
|
-
|
|
101
|
+
/** The Entity's key also used as it's url */
|
|
99
102
|
readonly key: string;
|
|
103
|
+
/** Metadata for the Entity's fields */
|
|
100
104
|
readonly fields: FieldsMetadata<entityType>;
|
|
105
|
+
/** A human readable caption for the entity. Can be used to achieve a consistent caption for a field throughout the app
|
|
106
|
+
* @example
|
|
107
|
+
* <h1>Create a new item in {taskRepo.metadata.caption}</h1>
|
|
108
|
+
*/
|
|
101
109
|
readonly caption: string;
|
|
110
|
+
/** The options send to the `Entity`'s decorator */
|
|
102
111
|
readonly options: EntityOptions;
|
|
112
|
+
/** The class type of the entity */
|
|
103
113
|
readonly entityType: ClassType<entityType>;
|
|
114
|
+
/** true if the current user is allowed to update an entity instance
|
|
115
|
+
* @example
|
|
116
|
+
* const taskRepo = remult.repo(Task);
|
|
117
|
+
* if (taskRepo.metadata.apiUpdateAllowed){
|
|
118
|
+
* //Allow user to edit the entity
|
|
119
|
+
* }
|
|
120
|
+
*/
|
|
104
121
|
readonly apiUpdateAllowed: boolean;
|
|
122
|
+
/** true if the current user is allowed to read from entity
|
|
123
|
+
* @example
|
|
124
|
+
* const taskRepo = remult.repo(Task);
|
|
125
|
+
* if (taskRepo.metadata.apiReadAllowed){
|
|
126
|
+
* taskRepo.find()
|
|
127
|
+
* }
|
|
128
|
+
*/
|
|
105
129
|
readonly apiReadAllowed: boolean;
|
|
130
|
+
/** true if the current user is allowed to delete an entity instance
|
|
131
|
+
* @example
|
|
132
|
+
* const taskRepo = remult.repo(Task);
|
|
133
|
+
* if (taskRepo.metadata.apiDeleteAllowed){
|
|
134
|
+
* //display delete button
|
|
135
|
+
* }
|
|
136
|
+
*/
|
|
106
137
|
readonly apiDeleteAllowed: boolean;
|
|
138
|
+
/** true if the current user is allowed to create an entity instance
|
|
139
|
+
* @example
|
|
140
|
+
* const taskRepo = remult.repo(Task);
|
|
141
|
+
* if (taskRepo.metadata.apiInsertAllowed){
|
|
142
|
+
* //display insert button
|
|
143
|
+
* }
|
|
144
|
+
*/
|
|
107
145
|
readonly apiInsertAllowed: boolean;
|
|
146
|
+
/** Returns the dbName - based on it's `dbName` option and it's `sqlExpression` option */
|
|
108
147
|
getDbName(): Promise<string>;
|
|
148
|
+
/** Metadata for the Entity's id */
|
|
149
|
+
readonly idMetadata: IdMetadata<entityType>;
|
|
109
150
|
}
|
|
110
151
|
export declare type OmitEB<T> = Omit<T, keyof import('./RepositoryImplementation').EntityBase>;
|
|
152
|
+
export declare type idType<entityType> = entityType extends {
|
|
153
|
+
id?: number;
|
|
154
|
+
} ? number : entityType extends {
|
|
155
|
+
id?: string;
|
|
156
|
+
} ? string : (string | number);
|
|
111
157
|
/**used to perform CRUD operations on an `entityType` */
|
|
112
158
|
export interface Repository<entityType> {
|
|
113
159
|
/** returns a result array based on the provided options */
|
|
114
160
|
find(options?: FindOptions<entityType>): Promise<entityType[]>;
|
|
161
|
+
/** returns a result array based on the provided options */
|
|
162
|
+
liveQuery(options?: FindOptions<entityType>): LiveQuery<entityType>;
|
|
115
163
|
/** returns the first item that matchers the `where` condition
|
|
116
164
|
* @example
|
|
117
165
|
* await taskRepo.findFirst({ completed:false })
|
|
@@ -120,16 +168,12 @@ export interface Repository<entityType> {
|
|
|
120
168
|
* */
|
|
121
169
|
findFirst(where?: EntityFilter<entityType>, options?: FindFirstOptions<entityType>): Promise<entityType>;
|
|
122
170
|
/** returns the items that matches the idm the result is cached unless specified differently in the `options` parameter */
|
|
123
|
-
findId(id: entityType
|
|
124
|
-
id?: number;
|
|
125
|
-
} ? number : entityType extends {
|
|
126
|
-
id?: string;
|
|
127
|
-
} ? string : (string | number), options?: FindFirstOptionsBase<entityType>): Promise<entityType>;
|
|
171
|
+
findId(id: idType<entityType>, options?: FindFirstOptionsBase<entityType>): Promise<entityType>;
|
|
128
172
|
/** An alternative form of fetching data from the API server, which is intended for operating on large numbers of entity objects.
|
|
129
173
|
*
|
|
130
174
|
* It also has it's own paging mechanism that can be used n paging scenarios.
|
|
131
175
|
*
|
|
132
|
-
* The `query` method doesn't return an array (as the `find` method) and instead returns an `
|
|
176
|
+
* The `query` method doesn't return an array (as the `find` method) and instead returns an `iterable` `QueryResult` object
|
|
133
177
|
* which supports iterations using the JavaScript `for await` statement.
|
|
134
178
|
* @example
|
|
135
179
|
* for await (const task of taskRepo.query()) {
|
|
@@ -179,10 +223,21 @@ export interface Repository<entityType> {
|
|
|
179
223
|
fromJson(x: any, isNew?: boolean): Promise<entityType>;
|
|
180
224
|
/** returns an `entityRef` for an item returned by `create`, `find` etc... */
|
|
181
225
|
getEntityRef(item: entityType): EntityRef<entityType>;
|
|
182
|
-
/**The metadata for the `entity`
|
|
226
|
+
/**The metadata for the `entity`
|
|
227
|
+
* @See [EntityMetadata](https://remult.dev/docs/ref_entitymetadata.html)
|
|
228
|
+
*/
|
|
183
229
|
metadata: EntityMetadata<entityType>;
|
|
184
230
|
addEventListener(listener: entityEventListener<entityType>): Unobserve;
|
|
185
231
|
}
|
|
232
|
+
export interface LiveQuery<entityType> {
|
|
233
|
+
subscribe(next: (info: LiveQueryChangeInfo<entityType>) => void): Unsubscribe;
|
|
234
|
+
subscribe(listener: Partial<SubscriptionListener<LiveQueryChangeInfo<entityType>>>): Unsubscribe;
|
|
235
|
+
}
|
|
236
|
+
export interface LiveQueryChangeInfo<entityType> {
|
|
237
|
+
items: entityType[];
|
|
238
|
+
changes: LiveQueryChange[];
|
|
239
|
+
applyChanges(prevState: entityType[]): entityType[];
|
|
240
|
+
}
|
|
186
241
|
export interface FindOptions<entityType> extends FindOptionsBase<entityType> {
|
|
187
242
|
/** Determines the number of rows returned by the request, on the browser the default is 100 rows
|
|
188
243
|
* @example
|
|
@@ -224,6 +279,8 @@ export declare type EntityFilter<entityType> = {
|
|
|
224
279
|
export declare type ValueFilter<valueType> = valueType | valueType[] | {
|
|
225
280
|
$ne?: valueType | valueType[];
|
|
226
281
|
"!="?: valueType | valueType[];
|
|
282
|
+
$in?: valueType[];
|
|
283
|
+
$nin?: valueType[];
|
|
227
284
|
};
|
|
228
285
|
export declare type ComparisonValueFilter<valueType> = ValueFilter<valueType> & {
|
|
229
286
|
$gt?: valueType;
|
|
@@ -19,6 +19,10 @@ export declare abstract class Action<inParam, outParam> implements ActionInterfa
|
|
|
19
19
|
protected abstract execute(info: inParam, req: Remult, res: DataApiResponse): Promise<outParam>;
|
|
20
20
|
__register(reg: (url: string, queue: boolean, allowed: AllowedForInstance<any>, what: ((data: any, req: Remult, res: DataApiResponse) => void)) => void): void;
|
|
21
21
|
}
|
|
22
|
+
export declare class ForbiddenError extends Error {
|
|
23
|
+
constructor();
|
|
24
|
+
isForbiddenError: true;
|
|
25
|
+
}
|
|
22
26
|
export declare class myServerAction extends Action<inArgs, result> {
|
|
23
27
|
private types;
|
|
24
28
|
private options;
|
|
@@ -38,7 +42,7 @@ export interface BackendMethodOptions<type> {
|
|
|
38
42
|
export declare const actionInfo: {
|
|
39
43
|
allActions: any[];
|
|
40
44
|
runningOnServer: boolean;
|
|
41
|
-
runActionWithoutBlockingUI: (what: () => Promise<
|
|
45
|
+
runActionWithoutBlockingUI: <T>(what: () => Promise<T>) => Promise<T>;
|
|
42
46
|
startBusyWithProgress: () => {
|
|
43
47
|
progress: (percent: number) => void;
|
|
44
48
|
close: () => void;
|
package/esm/src/server-action.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
|
-
import { Remult, allEntities, classHelpers, ClassHelper, setControllerSettings,
|
|
2
|
+
import { Remult, allEntities, classHelpers, ClassHelper, setControllerSettings, doTransaction } from './context';
|
|
3
|
+
import { buildRestDataProvider } from "./buildRestDataProvider";
|
|
3
4
|
import { SqlDatabase } from './data-providers/sql-database';
|
|
4
|
-
import { getEntityRef, decorateColumnSettings, getEntitySettings, getControllerRef
|
|
5
|
+
import { getEntityRef, decorateColumnSettings, getEntitySettings, getControllerRef } from './remult3';
|
|
6
|
+
import { remult } from './remult-proxy';
|
|
5
7
|
export class Action {
|
|
6
8
|
constructor(actionUrl, queue, allowed) {
|
|
7
9
|
this.actionUrl = actionUrl;
|
|
@@ -10,9 +12,9 @@ export class Action {
|
|
|
10
12
|
}
|
|
11
13
|
async run(pIn, baseUrl, http) {
|
|
12
14
|
if (baseUrl === undefined)
|
|
13
|
-
baseUrl =
|
|
15
|
+
baseUrl = remult.apiClient.url;
|
|
14
16
|
if (!http)
|
|
15
|
-
http = buildRestDataProvider(
|
|
17
|
+
http = buildRestDataProvider(remult.apiClient.httpClient);
|
|
16
18
|
let r = await http.post(baseUrl + '/' + this.actionUrl, pIn);
|
|
17
19
|
let p = r;
|
|
18
20
|
if (p && p.queuedJobId) {
|
|
@@ -59,7 +61,7 @@ export class Action {
|
|
|
59
61
|
}
|
|
60
62
|
}
|
|
61
63
|
Action.apiUrlForJobStatus = 'jobStatusInQueue';
|
|
62
|
-
class ForbiddenError extends Error {
|
|
64
|
+
export class ForbiddenError extends Error {
|
|
63
65
|
constructor() {
|
|
64
66
|
super("Forbidden");
|
|
65
67
|
this.isForbiddenError = true;
|
|
@@ -75,8 +77,7 @@ export class myServerAction extends Action {
|
|
|
75
77
|
async execute(info, remult, res) {
|
|
76
78
|
let result = { data: {} };
|
|
77
79
|
let ds = remult.dataProvider;
|
|
78
|
-
await
|
|
79
|
-
remult.dataProvider = (ds);
|
|
80
|
+
await doTransaction(remult, async () => {
|
|
80
81
|
if (!remult.isAllowedForInstance(undefined, this.options.allowed))
|
|
81
82
|
throw new ForbiddenError();
|
|
82
83
|
info.args = await prepareReceivedArgs(this.types, info.args, remult, ds, res);
|
|
@@ -160,11 +161,9 @@ export function BackendMethod(options) {
|
|
|
160
161
|
let allowed = options.allowed;
|
|
161
162
|
try {
|
|
162
163
|
let remult = req;
|
|
163
|
-
let ds = remult.dataProvider;
|
|
164
164
|
let r;
|
|
165
|
-
await
|
|
166
|
-
|
|
167
|
-
d.args = await prepareReceivedArgs(types, d.args, remult, ds, res);
|
|
165
|
+
await doTransaction(remult, async () => {
|
|
166
|
+
d.args = await prepareReceivedArgs(types, d.args, remult, remult.dataProvider, res);
|
|
168
167
|
if (allEntities.includes(constructor)) {
|
|
169
168
|
let repo = remult.repo(constructor);
|
|
170
169
|
let y;
|
|
@@ -205,7 +204,7 @@ export function BackendMethod(options) {
|
|
|
205
204
|
}
|
|
206
205
|
}
|
|
207
206
|
else {
|
|
208
|
-
let y = new constructor(remult,
|
|
207
|
+
let y = new constructor(remult, remult.dataProvider);
|
|
209
208
|
let controllerRef = getControllerRef(y, remult);
|
|
210
209
|
await controllerRef._updateEntityBasedOnApi(d.fields);
|
|
211
210
|
if (!remult.isAllowedForInstance(y, allowed))
|
|
@@ -5,15 +5,22 @@ import { Repository } from "../remult3";
|
|
|
5
5
|
import { CategoriesForTesting } from "../tests/remult-3-entities";
|
|
6
6
|
import { Status } from "../tests/testModel/models";
|
|
7
7
|
export declare function itWithFocus(key: string, what: () => Promise<void>, focus?: boolean): void;
|
|
8
|
-
export declare function testAll(key: string, what: dbTestWhatSignature, focus?: boolean
|
|
9
|
-
|
|
8
|
+
export declare function testAll(key: string, what: dbTestWhatSignature, focus?: boolean, options?: {
|
|
9
|
+
exclude?: string[];
|
|
10
|
+
}): void;
|
|
11
|
+
export declare function addDatabaseToTest(tester: dbTestMethodSignature, key?: string): dbTestMethodSignature;
|
|
10
12
|
export declare function testInMemory(key: string, what: dbTestWhatSignature, focus?: boolean): void;
|
|
11
|
-
export declare
|
|
13
|
+
export declare const TestDbs: {
|
|
14
|
+
restDataProvider: string;
|
|
15
|
+
};
|
|
16
|
+
export declare type dbTestWhatSignature = ((db: {
|
|
12
17
|
db: DataProvider;
|
|
13
18
|
remult: Remult;
|
|
14
19
|
createEntity<entityType>(entity: ClassType<entityType>): Promise<Repository<entityType>>;
|
|
15
|
-
}) => Promise<void
|
|
16
|
-
export declare type dbTestMethodSignature = (key: string, what: dbTestWhatSignature, focus: boolean) => void
|
|
20
|
+
}) => Promise<void>);
|
|
21
|
+
export declare type dbTestMethodSignature = ((key: string, what: dbTestWhatSignature, focus: boolean) => void) & {
|
|
22
|
+
key?: string;
|
|
23
|
+
};
|
|
17
24
|
export declare function testAllDbs<T extends CategoriesForTesting>(key: string, doTest: (helper: {
|
|
18
25
|
remult: Remult;
|
|
19
26
|
createData: (doInsert?: (insert: (id: number, name: string, description?: string, status?: Status) => Promise<void>) => Promise<void>, entity?: {
|
|
@@ -8,17 +8,23 @@ export function itWithFocus(key, what, focus = false) {
|
|
|
8
8
|
else
|
|
9
9
|
it(key, what);
|
|
10
10
|
}
|
|
11
|
-
export function testAll(key, what, focus = false) {
|
|
11
|
+
export function testAll(key, what, focus = false, options) {
|
|
12
|
+
function runTest(m) {
|
|
13
|
+
return !options?.exclude?.includes(m.key);
|
|
14
|
+
}
|
|
12
15
|
for (const test of databasesTesters) {
|
|
13
|
-
test
|
|
16
|
+
if (runTest(test))
|
|
17
|
+
test(key, what, focus);
|
|
14
18
|
}
|
|
15
19
|
loadedTests.push(x => {
|
|
16
|
-
x
|
|
20
|
+
if (runTest(x))
|
|
21
|
+
x(key, what, focus);
|
|
17
22
|
});
|
|
18
23
|
}
|
|
19
24
|
const databasesTesters = [];
|
|
20
25
|
const loadedTests = [];
|
|
21
|
-
export function addDatabaseToTest(tester) {
|
|
26
|
+
export function addDatabaseToTest(tester, key) {
|
|
27
|
+
tester.key = key;
|
|
22
28
|
for (const test of loadedTests) {
|
|
23
29
|
test(tester);
|
|
24
30
|
}
|
|
@@ -27,12 +33,14 @@ export function addDatabaseToTest(tester) {
|
|
|
27
33
|
}
|
|
28
34
|
export function testInMemory(key, what, focus = false) {
|
|
29
35
|
itWithFocus(key + " - in memory", async () => {
|
|
30
|
-
let remult = new Remult();
|
|
31
36
|
let db = new InMemoryDataProvider();
|
|
32
|
-
remult
|
|
37
|
+
let remult = new Remult(db);
|
|
33
38
|
await what({ db, remult, createEntity: async (x) => remult.repo(x) });
|
|
34
39
|
}, focus);
|
|
35
40
|
}
|
|
41
|
+
export const TestDbs = {
|
|
42
|
+
restDataProvider: "restDataProvider"
|
|
43
|
+
};
|
|
36
44
|
databasesTesters.push(testInMemory);
|
|
37
45
|
export async function testAllDbs(key, doTest, focus = false) {
|
|
38
46
|
testAll(key, async ({ remult, createEntity }) => {
|