remult 0.22.3 → 0.22.4
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/esm/angular/interfaces/index.d.ts +1 -0
- package/esm/angular/interfaces/index.js +1 -0
- package/esm/angular/interfaces/src/column-collection.d.ts +70 -0
- package/esm/angular/interfaces/src/column-collection.js +341 -0
- package/esm/angular/interfaces/src/data-area-settings.d.ts +15 -0
- package/esm/angular/interfaces/src/data-area-settings.js +35 -0
- package/esm/angular/interfaces/src/data-control-interfaces.d.ts +36 -0
- package/esm/angular/interfaces/src/data-control-interfaces.js +57 -0
- package/esm/angular/interfaces/src/dataList.d.ts +13 -0
- package/esm/angular/interfaces/src/dataList.js +42 -0
- package/esm/angular/interfaces/src/filter-helper.d.ts +13 -0
- package/esm/angular/interfaces/src/filter-helper.js +59 -0
- package/esm/angular/interfaces/src/grid-settings.d.ts +145 -0
- package/esm/angular/interfaces/src/grid-settings.js +387 -0
- package/esm/angular/interfaces/src/public_api.d.ts +4 -0
- package/esm/angular/interfaces/src/public_api.js +4 -0
- package/esm/angular/src/angular/RemultAngularPluginsService.d.ts +4 -0
- package/esm/angular/src/angular/RemultAngularPluginsService.js +3 -0
- package/esm/angular/src/angular/data-control/data-control2.component.d.ts +49 -0
- package/esm/angular/src/angular/data-control/data-control2.component.js +138 -0
- package/esm/core/SseSubscriptionServer.d.ts +33 -0
- package/esm/core/SseSubscriptionServer.js +107 -0
- package/esm/core/assign.d.ts +1 -0
- package/esm/core/assign.js +5 -0
- package/esm/core/classType.d.ts +3 -0
- package/esm/core/classType.js +1 -0
- package/esm/core/index.d.ts +27 -0
- package/esm/core/index.js +25 -0
- package/esm/core/inputTypes.d.ts +9 -0
- package/esm/core/inputTypes.js +9 -0
- package/esm/core/live-query/data-provider-live-query-storage.d.ts +22 -0
- package/esm/core/live-query/data-provider-live-query-storage.js +71 -0
- package/esm/core/postgres/index.d.ts +2 -0
- package/esm/core/postgres/index.js +2 -0
- package/esm/core/postgres/postgres-data-provider.d.ts +36 -0
- package/esm/core/postgres/postgres-data-provider.js +120 -0
- package/esm/core/postgres/schema-builder.d.ts +17 -0
- package/esm/core/postgres/schema-builder.js +153 -0
- package/esm/core/remult-fresh.d.ts +17 -0
- package/esm/core/remult-fresh.js +29 -0
- package/esm/core/remult-knex/index.d.ts +30 -0
- package/esm/core/remult-knex/index.js +446 -0
- package/esm/core/remult-mongo.d.ts +24 -0
- package/esm/core/remult-mongo.js +254 -0
- package/esm/core/server/JsonEntityFileStorage.d.ts +10 -0
- package/esm/core/server/JsonEntityFileStorage.js +25 -0
- package/esm/core/server/core.d.ts +4 -0
- package/esm/core/server/core.js +4 -0
- package/esm/core/server/expressBridge.d.ts +182 -0
- package/esm/core/server/expressBridge.js +938 -0
- package/esm/core/server/index.d.ts +6 -0
- package/esm/core/server/index.js +9 -0
- package/esm/core/server/initAsyncHooks.d.ts +1 -0
- package/esm/core/server/initAsyncHooks.js +9 -0
- package/esm/core/server/initDataProvider.d.ts +2 -0
- package/esm/core/server/initDataProvider.js +14 -0
- package/esm/core/src/__EntityValueProvider.d.ts +11 -0
- package/esm/core/src/__EntityValueProvider.js +1 -0
- package/esm/core/src/__RowsOfDataForTesting.d.ts +3 -0
- package/esm/core/src/__RowsOfDataForTesting.js +1 -0
- package/esm/core/src/backend-tests/backend-database-test-setup.backend-spec.d.ts +6 -0
- package/esm/core/src/backend-tests/backend-database-test-setup.backend-spec.js +147 -0
- package/{src → esm/core/src}/backend-tests/my-test.backend-spec.d.ts +1 -1
- package/esm/core/src/backend-tests/my-test.backend-spec.js +197 -0
- package/esm/core/src/backend-tests/test-http-server.backend-spec.d.ts +1 -0
- package/esm/core/src/backend-tests/test-http-server.backend-spec.js +167 -0
- package/esm/core/src/buildRestDataProvider.d.ts +31 -0
- package/esm/core/src/buildRestDataProvider.js +113 -0
- package/esm/core/src/column-interfaces.d.ts +154 -0
- package/esm/core/src/column-interfaces.js +7 -0
- package/esm/core/src/column.d.ts +58 -0
- package/esm/core/src/column.js +174 -0
- package/esm/core/src/context.d.ts +134 -0
- package/esm/core/src/context.js +286 -0
- package/esm/core/src/data-api.d.ts +39 -0
- package/esm/core/src/data-api.js +279 -0
- package/esm/core/src/data-interfaces.d.ts +41 -0
- package/esm/core/src/data-interfaces.js +12 -0
- package/esm/core/src/data-providers/array-entity-data-provider.d.ts +22 -0
- package/esm/core/src/data-providers/array-entity-data-provider.js +252 -0
- package/esm/core/src/data-providers/in-memory-database.d.ts +9 -0
- package/esm/core/src/data-providers/in-memory-database.js +14 -0
- package/esm/core/src/data-providers/json-data-provider.d.ts +12 -0
- package/esm/core/src/data-providers/json-data-provider.js +55 -0
- package/esm/core/src/data-providers/rest-data-provider.d.ts +49 -0
- package/esm/core/src/data-providers/rest-data-provider.js +256 -0
- package/esm/core/src/data-providers/sql-database.d.ts +33 -0
- package/esm/core/src/data-providers/sql-database.js +384 -0
- package/esm/core/src/data-providers/web-sql-data-provider.d.ts +24 -0
- package/esm/core/src/data-providers/web-sql-data-provider.js +143 -0
- package/esm/core/src/entity.d.ts +91 -0
- package/esm/core/src/entity.js +1 -0
- package/esm/core/src/filter/filter-consumer-bridge-to-sql-request.d.ts +50 -0
- package/esm/core/src/filter/filter-consumer-bridge-to-sql-request.js +150 -0
- package/esm/core/src/filter/filter-consumer-bridge-to-url-builder.d.ts +1 -0
- package/esm/core/src/filter/filter-consumer-bridge-to-url-builder.js +1 -0
- package/esm/core/src/filter/filter-interfaces.d.ts +85 -0
- package/esm/core/src/filter/filter-interfaces.js +477 -0
- package/esm/core/src/live-query/LiveQueryClient.d.ts +20 -0
- package/esm/core/src/live-query/LiveQueryClient.js +188 -0
- package/esm/core/src/live-query/SseSubscriptionClient.d.ts +5 -0
- package/esm/core/src/live-query/SseSubscriptionClient.js +86 -0
- package/esm/core/src/live-query/SubscriptionChannel.d.ts +72 -0
- package/esm/core/src/live-query/SubscriptionChannel.js +123 -0
- package/esm/core/src/live-query/SubscriptionServer.d.ts +53 -0
- package/esm/core/src/live-query/SubscriptionServer.js +111 -0
- package/esm/core/src/live-query/live-query-tests.spec.d.ts +6 -0
- package/esm/core/src/live-query/live-query-tests.spec.js +952 -0
- package/esm/core/src/remult-proxy.d.ts +35 -0
- package/esm/core/src/remult-proxy.js +113 -0
- package/esm/core/src/remult3/DecoratorReplacer.d.ts +10 -0
- package/esm/core/src/remult3/DecoratorReplacer.js +20 -0
- package/esm/core/src/remult3/RepositoryImplementation.d.ts +355 -0
- package/esm/core/src/remult3/RepositoryImplementation.js +2109 -0
- package/esm/core/src/remult3/index.d.ts +2 -0
- package/esm/core/src/remult3/index.js +2 -0
- package/esm/core/src/remult3/remult3.d.ts +395 -0
- package/esm/core/src/remult3/remult3.js +2 -0
- package/esm/core/src/server-action.d.ts +81 -0
- package/esm/core/src/server-action.js +394 -0
- package/esm/core/src/shared-tests/db-tests-setup.d.ts +32 -0
- package/esm/core/src/shared-tests/db-tests-setup.js +78 -0
- package/esm/core/src/shared-tests/db-tests.db-tests.d.ts +25 -0
- package/esm/core/src/shared-tests/db-tests.db-tests.js +810 -0
- package/esm/core/src/shared-tests/deleteAll.d.ts +2 -0
- package/esm/core/src/shared-tests/deleteAll.js +4 -0
- package/esm/core/src/shared-tests/entityWithValidations.d.ts +14 -0
- package/esm/core/src/shared-tests/entityWithValidations.js +43 -0
- package/esm/core/src/shared-tests/index.d.ts +4 -0
- package/esm/core/src/shared-tests/index.js +4 -0
- package/esm/core/src/shared-tests/test-custom-id-column.spec.d.ts +1 -0
- package/esm/core/src/shared-tests/test-custom-id-column.spec.js +67 -0
- package/esm/core/src/shared-tests/test-special-value.spec.d.ts +10 -0
- package/esm/core/src/shared-tests/test-special-value.spec.js +73 -0
- package/esm/core/src/shared-tests/test-update-with-null.spec.d.ts +1 -0
- package/esm/core/src/shared-tests/test-update-with-null.spec.js +31 -0
- package/esm/core/src/sort.d.ts +16 -0
- package/esm/core/src/sort.js +103 -0
- package/esm/core/src/sql-command.d.ts +19 -0
- package/esm/core/src/sql-command.js +1 -0
- package/esm/core/src/test.d.ts +3 -0
- package/esm/core/src/test.js +12 -0
- package/esm/core/src/tests/Done.d.ts +5 -0
- package/esm/core/src/tests/Done.js +9 -0
- package/esm/core/src/tests/RowProvider.spec.d.ts +16 -0
- package/esm/core/src/tests/RowProvider.spec.js +631 -0
- package/esm/core/src/tests/TestDataApiResponse.d.ts +10 -0
- package/esm/core/src/tests/TestDataApiResponse.js +22 -0
- package/esm/core/src/tests/basicRowFunctionality.spec.d.ts +8 -0
- package/esm/core/src/tests/basicRowFunctionality.spec.js +2103 -0
- package/esm/core/src/tests/c.d.ts +8 -0
- package/esm/core/src/tests/c.js +13 -0
- package/esm/core/src/tests/createData.d.ts +7 -0
- package/esm/core/src/tests/createData.js +22 -0
- package/esm/core/src/tests/d.d.ts +6 -0
- package/esm/core/src/tests/d.js +11 -0
- package/esm/core/src/tests/dWithPrefilter.d.ts +7 -0
- package/esm/core/src/tests/dWithPrefilter.js +18 -0
- package/esm/core/src/tests/entityForCustomFilter.d.ts +16 -0
- package/esm/core/src/tests/entityForCustomFilter.js +33 -0
- package/esm/core/src/tests/entityWithValidationsOnColumn.d.ts +5 -0
- package/esm/core/src/tests/entityWithValidationsOnColumn.js +13 -0
- package/esm/core/src/tests/frontend-database-tests-setup.spec.d.ts +4 -0
- package/esm/core/src/tests/frontend-database-tests-setup.spec.js +37 -0
- package/esm/core/src/tests/h.d.ts +6 -0
- package/esm/core/src/tests/h.js +24 -0
- package/esm/core/src/tests/remult-3-basic-tests.spec.d.ts +1 -0
- package/esm/core/src/tests/remult-3-basic-tests.spec.js +236 -0
- package/esm/core/src/tests/remult-3-entities.d.ts +23 -0
- package/esm/core/src/tests/remult-3-entities.js +39 -0
- package/esm/core/src/tests/tasks.d.ts +6 -0
- package/esm/core/src/tests/tasks.js +10 -0
- package/esm/core/src/tests/test-context-as-storage.spec.d.ts +0 -0
- package/esm/core/src/tests/test-context-as-storage.spec.js +1 -0
- package/esm/core/src/tests/test-data-api/predefined-filter.spec.d.ts +1 -0
- package/esm/core/src/tests/test-data-api/predefined-filter.spec.js +389 -0
- package/esm/core/src/tests/test-data-api/test-put-with-validation.spec.d.ts +5 -0
- package/esm/core/src/tests/test-data-api/test-put-with-validation.spec.js +152 -0
- package/esm/core/src/tests/test-decorator-precedence.spec.d.ts +1 -0
- package/esm/core/src/tests/test-decorator-precedence.spec.js +38 -0
- package/esm/core/src/tests/test-default-value.spec.d.ts +1 -0
- package/esm/core/src/tests/test-default-value.spec.js +33 -0
- package/esm/core/src/tests/test-entity-inheritance.spec.d.ts +14 -0
- package/esm/core/src/tests/test-entity-inheritance.spec.js +82 -0
- package/esm/core/src/tests/test-exceptions.spec.d.ts +1 -0
- package/esm/core/src/tests/test-exceptions.spec.js +375 -0
- package/esm/core/src/tests/test-json.spec.d.ts +1 -0
- package/esm/core/src/tests/test-json.spec.js +115 -0
- package/esm/core/src/tests/test-many-to-one-relation-in-entity.spec.d.ts +9 -0
- package/esm/core/src/tests/test-many-to-one-relation-in-entity.spec.js +809 -0
- package/esm/core/src/tests/test-non-active-record.spec.d.ts +1 -0
- package/esm/core/src/tests/test-non-active-record.spec.js +66 -0
- package/esm/core/src/tests/test-object-column.spec.d.ts +1 -0
- package/esm/core/src/tests/test-object-column.spec.js +189 -0
- package/esm/core/src/tests/test-paged-foreach.spec.d.ts +1 -0
- package/esm/core/src/tests/test-paged-foreach.spec.js +328 -0
- package/esm/core/src/tests/test-remult-angular.spec.d.ts +1 -0
- package/esm/core/src/tests/test-remult-angular.spec.js +23 -0
- package/esm/core/src/tests/test-server-controller.spec.d.ts +7 -0
- package/esm/core/src/tests/test-server-controller.spec.js +489 -0
- package/esm/core/src/tests/test-server-expressions.spec.d.ts +1 -0
- package/esm/core/src/tests/test-server-expressions.spec.js +178 -0
- package/esm/core/src/tests/test-server-method-on-entity.spec.d.ts +1 -0
- package/esm/core/src/tests/test-server-method-on-entity.spec.js +306 -0
- package/esm/core/src/tests/test-sql-expression.spec.d.ts +5 -0
- package/esm/core/src/tests/test-sql-expression.spec.js +205 -0
- package/esm/core/src/tests/test-sql.spec.d.ts +1 -0
- package/esm/core/src/tests/test-sql.spec.js +170 -0
- package/esm/core/src/tests/test-subscribe.spec.d.ts +1 -0
- package/esm/core/src/tests/test-subscribe.spec.js +135 -0
- package/esm/core/src/tests/test-type-recovery.spec.d.ts +1 -0
- package/esm/core/src/tests/test-type-recovery.spec.js +49 -0
- package/esm/core/src/tests/test-where-api.spec.d.ts +1 -0
- package/esm/core/src/tests/test-where-api.spec.js +434 -0
- package/esm/core/src/tests/testHelper.spec.d.ts +36 -0
- package/esm/core/src/tests/testHelper.spec.js +226 -0
- package/esm/core/src/tests/testModel/models.d.ts +16 -0
- package/esm/core/src/tests/testModel/models.js +25 -0
- package/esm/core/src/validators.d.ts +14 -0
- package/esm/core/src/validators.js +33 -0
- package/{valueConverters.d.ts → esm/core/src/valueConverters.d.ts} +13 -12
- package/esm/core/src/valueConverters.js +197 -0
- package/esm/core/urlBuilder.d.ts +6 -0
- package/esm/core/urlBuilder.js +20 -0
- package/esm/graphql.js +27 -27
- package/esm/postgres/schema-builder.js +4 -4
- package/esm/src/remult3/RepositoryImplementation.d.ts +1 -1
- package/esm/src/remult3/RepositoryImplementation.js +27 -4
- package/esm/src/shared-tests/db-tests.db-tests.js +57 -0
- package/esm/test-servers/shared/Task.d.ts +9 -0
- package/esm/test-servers/shared/Task.js +32 -0
- package/esm/test.d.ts +5 -0
- package/esm/test.js +6 -0
- package/package.json +1 -1
- package/src/remult3/RepositoryImplementation.d.ts +1 -1
- package/src/remult3/RepositoryImplementation.js +88 -63
- package/src/shared-tests/db-tests.db-tests.js +97 -0
- package/esm/src/id-entity.d.ts +0 -4
- package/esm/src/id-entity.js +0 -8
- package/postgres/postgresColumnSyntax.d.ts +0 -2
- package/postgres/postgresColumnSyntax.js +0 -34
- package/remult-middleware.d.ts +0 -2
- package/remult-middleware.js +0 -70
- package/server/register-actions-on-server.d.ts +0 -3
- package/server/register-actions-on-server.js +0 -50
- package/server/register-entities-on-server.d.ts +0 -3
- package/server/register-entities-on-server.js +0 -19
- package/src/backend-tests/my-test.backend-spec.js +0 -217
- package/src/backend-tests/test-http-server.backend-spec.d.ts +0 -6
- package/src/backend-tests/test-http-server.backend-spec.js +0 -342
- package/src/id-entity.d.ts +0 -4
- package/src/id-entity.js +0 -17
- package/valueConverters.js +0 -168
|
@@ -0,0 +1,2109 @@
|
|
|
1
|
+
import { CompoundIdField, LookupColumn, makeTitle } from '../column';
|
|
2
|
+
import { allEntities, Remult, isBackend, queryConfig as queryConfig, setControllerSettings } from "../context";
|
|
3
|
+
import { entityFilterToJson, Filter } from "../filter/filter-interfaces";
|
|
4
|
+
import { Sort } from "../sort";
|
|
5
|
+
import { v4 as uuid } from 'uuid';
|
|
6
|
+
import { createId } from '@paralleldrive/cuid2';
|
|
7
|
+
import { ValueConverters } from "../valueConverters";
|
|
8
|
+
import { filterHelper } from "../filter/filter-interfaces";
|
|
9
|
+
import { assign } from "../../assign";
|
|
10
|
+
import { remult as defaultRemult } from "../remult-proxy";
|
|
11
|
+
//import { remult } from "../remult-proxy";
|
|
12
|
+
let classValidatorValidate = undefined;
|
|
13
|
+
// import("class-validator".toString())
|
|
14
|
+
// .then((v) => {
|
|
15
|
+
// classValidatorValidate = (item, ref) => {
|
|
16
|
+
// return v.validate(item).then(errors => {
|
|
17
|
+
// for (const err of errors) {
|
|
18
|
+
// for (const key in err.constraints) {
|
|
19
|
+
// if (Object.prototype.hasOwnProperty.call(err.constraints, key)) {
|
|
20
|
+
// const element = err.constraints[key];
|
|
21
|
+
// ref.fields.find(err.property).error = element;
|
|
22
|
+
// }
|
|
23
|
+
// }
|
|
24
|
+
// }
|
|
25
|
+
// });
|
|
26
|
+
// }
|
|
27
|
+
// })
|
|
28
|
+
// .catch(() => {
|
|
29
|
+
// });
|
|
30
|
+
export class RepositoryImplementation {
|
|
31
|
+
entity;
|
|
32
|
+
remult;
|
|
33
|
+
dataProvider;
|
|
34
|
+
async createAfterFilter(orderBy, lastRow) {
|
|
35
|
+
let values = new Map();
|
|
36
|
+
for (const s of Sort.translateOrderByToSort(this.metadata, orderBy).Segments) {
|
|
37
|
+
let existingVal = lastRow[s.field.key];
|
|
38
|
+
// if (typeof existingVal !== "string" && typeof existingVal !== "number") {
|
|
39
|
+
// }
|
|
40
|
+
// else {
|
|
41
|
+
// let ei = getEntitySettings(s.field.valueType, false);
|
|
42
|
+
// if (ei) {
|
|
43
|
+
// existingVal = await this.remult.repo(s.field.valueType).findId(existingVal);
|
|
44
|
+
// }
|
|
45
|
+
// }
|
|
46
|
+
values.set(s.field.key, existingVal);
|
|
47
|
+
}
|
|
48
|
+
let r = { $or: [] };
|
|
49
|
+
let equalToColumn = [];
|
|
50
|
+
for (const s of Sort.translateOrderByToSort(this.metadata, orderBy).Segments) {
|
|
51
|
+
let ff = new filterHelper(s.field);
|
|
52
|
+
let f = {};
|
|
53
|
+
for (const c of equalToColumn) {
|
|
54
|
+
f[c.key] = values.get(c.key);
|
|
55
|
+
}
|
|
56
|
+
equalToColumn.push(s.field);
|
|
57
|
+
if (s.isDescending) {
|
|
58
|
+
f[s.field.key] = { $lt: values.get(s.field.key) };
|
|
59
|
+
}
|
|
60
|
+
else
|
|
61
|
+
f[s.field.key] = { $gt: values.get(s.field.key) };
|
|
62
|
+
r.$or.push(f);
|
|
63
|
+
}
|
|
64
|
+
return r;
|
|
65
|
+
}
|
|
66
|
+
_info;
|
|
67
|
+
__edp;
|
|
68
|
+
get edp() {
|
|
69
|
+
return this.__edp ? this.__edp : this.__edp = this.dataProvider.getEntityDataProvider(this.metadata);
|
|
70
|
+
}
|
|
71
|
+
constructor(entity, remult, dataProvider) {
|
|
72
|
+
this.entity = entity;
|
|
73
|
+
this.remult = remult;
|
|
74
|
+
this.dataProvider = dataProvider;
|
|
75
|
+
this._info = createOldEntity(entity, remult);
|
|
76
|
+
}
|
|
77
|
+
idCache = new Map();
|
|
78
|
+
getCachedById(id) {
|
|
79
|
+
id = id + '';
|
|
80
|
+
this.getCachedByIdAsync(id);
|
|
81
|
+
let r = this.idCache.get(id);
|
|
82
|
+
if (r instanceof Promise)
|
|
83
|
+
return undefined;
|
|
84
|
+
return r;
|
|
85
|
+
}
|
|
86
|
+
async getCachedByIdAsync(id) {
|
|
87
|
+
id = id + '';
|
|
88
|
+
let r = this.idCache.get(id);
|
|
89
|
+
if (r instanceof Promise)
|
|
90
|
+
return await r;
|
|
91
|
+
if (this.idCache.has(id)) {
|
|
92
|
+
return r;
|
|
93
|
+
}
|
|
94
|
+
this.idCache.set(id, undefined);
|
|
95
|
+
let row = this.findId(id).then(row => {
|
|
96
|
+
if (row === undefined) {
|
|
97
|
+
r = null;
|
|
98
|
+
}
|
|
99
|
+
else
|
|
100
|
+
r = row;
|
|
101
|
+
this.idCache.set(id, r);
|
|
102
|
+
return r;
|
|
103
|
+
});
|
|
104
|
+
this.idCache.set(id, row);
|
|
105
|
+
return await row;
|
|
106
|
+
}
|
|
107
|
+
//TODO2 - Used to prevent unnecessary many to one relations
|
|
108
|
+
addToCache(item) {
|
|
109
|
+
if (item)
|
|
110
|
+
this.idCache.set(this.getEntityRef(item).getId() + '', item);
|
|
111
|
+
}
|
|
112
|
+
get metadata() { return this._info; }
|
|
113
|
+
;
|
|
114
|
+
listeners;
|
|
115
|
+
addEventListener(listener) {
|
|
116
|
+
if (!this.listeners)
|
|
117
|
+
this.listeners = [];
|
|
118
|
+
this.listeners.push(listener);
|
|
119
|
+
return () => {
|
|
120
|
+
this.listeners.splice(this.listeners.indexOf(listener), 1);
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
query(options) {
|
|
124
|
+
return new QueryResultImpl(options, this);
|
|
125
|
+
}
|
|
126
|
+
getEntityRef(entity) {
|
|
127
|
+
let x = entity[entityMember];
|
|
128
|
+
if (!x) {
|
|
129
|
+
this.fixTypes(entity);
|
|
130
|
+
x = new rowHelperImplementation(this._info, entity, this, this.edp, this.remult, true);
|
|
131
|
+
Object.defineProperty(entity, entityMember, {
|
|
132
|
+
get: () => x
|
|
133
|
+
});
|
|
134
|
+
x.saveOriginalData();
|
|
135
|
+
}
|
|
136
|
+
return x;
|
|
137
|
+
}
|
|
138
|
+
async delete(item) {
|
|
139
|
+
if (typeof item === "string" || typeof item === "number")
|
|
140
|
+
return this.edp.delete(item);
|
|
141
|
+
else {
|
|
142
|
+
return this.getRefForExistingRow(item, undefined).delete();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async insert(entity) {
|
|
146
|
+
if (Array.isArray(entity)) {
|
|
147
|
+
let r = [];
|
|
148
|
+
for (const item of entity) {
|
|
149
|
+
r.push(await this.insert(item));
|
|
150
|
+
}
|
|
151
|
+
return r;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
let ref = getEntityRef(entity, false);
|
|
155
|
+
if (ref) {
|
|
156
|
+
if (!ref.isNew())
|
|
157
|
+
throw "Item is not new";
|
|
158
|
+
return await ref.save();
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
return await this.getEntityRef(this.create(entity)).save();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
get fields() {
|
|
166
|
+
return this.metadata.fields;
|
|
167
|
+
}
|
|
168
|
+
async validate(entity, ...fields) {
|
|
169
|
+
{
|
|
170
|
+
let ref = getEntityRef(entity, false);
|
|
171
|
+
if (!ref)
|
|
172
|
+
ref = this.getEntityRef({ ...entity });
|
|
173
|
+
if (!fields || fields.length === 0) {
|
|
174
|
+
return await ref.validate();
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
ref.__clearErrorsAndReportChanged();
|
|
178
|
+
let hasError = false;
|
|
179
|
+
for (const f of fields) {
|
|
180
|
+
if (!await ref.fields.find(f).validate())
|
|
181
|
+
hasError = true;
|
|
182
|
+
}
|
|
183
|
+
if (!hasError)
|
|
184
|
+
return undefined;
|
|
185
|
+
return ref.buildErrorInfoObject();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async update(id, entity) {
|
|
190
|
+
let ref = this.getRefForExistingRow(entity, id);
|
|
191
|
+
return (await ref.save());
|
|
192
|
+
}
|
|
193
|
+
getRefForExistingRow(entity, id) {
|
|
194
|
+
let ref = getEntityRef(entity, false);
|
|
195
|
+
if (!ref) {
|
|
196
|
+
const instance = new this.entity(this.remult);
|
|
197
|
+
for (const field of this.fieldsOf(entity)) {
|
|
198
|
+
instance[field.key] = entity[field.key];
|
|
199
|
+
}
|
|
200
|
+
this.fixTypes(instance);
|
|
201
|
+
let row = new rowHelperImplementation(this._info, instance, this, this.edp, this.remult, false);
|
|
202
|
+
if (typeof (id) === "object")
|
|
203
|
+
id = this.metadata.idMetadata.getId(id);
|
|
204
|
+
if (id) {
|
|
205
|
+
row.id = id;
|
|
206
|
+
row.originalId = id;
|
|
207
|
+
}
|
|
208
|
+
else
|
|
209
|
+
row.id = row.getId();
|
|
210
|
+
ref = row;
|
|
211
|
+
Object.defineProperty(instance, entityMember, {
|
|
212
|
+
get: () => row
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return ref;
|
|
216
|
+
}
|
|
217
|
+
async save(entity) {
|
|
218
|
+
if (Array.isArray(entity)) {
|
|
219
|
+
return Promise.all(entity.map(x => this.save(x)));
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
let ref = getEntityRef(entity, false);
|
|
223
|
+
if (ref)
|
|
224
|
+
return await ref.save();
|
|
225
|
+
else if (entity instanceof EntityBase) {
|
|
226
|
+
return await this.getEntityRef(entity).save();
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
let id = this.metadata.idMetadata.getId(entity);
|
|
230
|
+
if (id === undefined)
|
|
231
|
+
return this.insert(entity);
|
|
232
|
+
return this.update(id, entity);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
liveQuery(options) {
|
|
237
|
+
if (!options)
|
|
238
|
+
options = {};
|
|
239
|
+
return {
|
|
240
|
+
subscribe: (l) => {
|
|
241
|
+
let listener = l;
|
|
242
|
+
if (typeof l === "function") {
|
|
243
|
+
listener = {
|
|
244
|
+
next: l,
|
|
245
|
+
complete: () => { },
|
|
246
|
+
error: () => { }
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
listener.error ??= () => { };
|
|
250
|
+
listener.complete ??= () => { };
|
|
251
|
+
return this.remult.liveQuerySubscriber.subscribe(this, options, listener);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
async find(options, skipOrderByAndLimit = false) {
|
|
256
|
+
Remult.onFind(this._info, options);
|
|
257
|
+
if (!options)
|
|
258
|
+
options = {};
|
|
259
|
+
let opt = await this.buildEntityDataProviderFindOptions(options);
|
|
260
|
+
if (skipOrderByAndLimit) {
|
|
261
|
+
delete opt.orderBy;
|
|
262
|
+
delete opt.limit;
|
|
263
|
+
}
|
|
264
|
+
let rawRows = await this.edp.find(opt);
|
|
265
|
+
let result = await this.loadManyToOneForManyRows(rawRows, options.load);
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
async buildEntityDataProviderFindOptions(options) {
|
|
269
|
+
let opt = {};
|
|
270
|
+
opt = {};
|
|
271
|
+
if (!options.orderBy || Object.keys(options.orderBy).length === 0) {
|
|
272
|
+
options.orderBy = this._info.entityInfo.defaultOrderBy;
|
|
273
|
+
}
|
|
274
|
+
opt.where = await this.translateWhereToFilter(options.where);
|
|
275
|
+
opt.orderBy = Sort.translateOrderByToSort(this.metadata, options.orderBy);
|
|
276
|
+
opt.limit = options.limit;
|
|
277
|
+
opt.page = options.page;
|
|
278
|
+
return opt;
|
|
279
|
+
}
|
|
280
|
+
async fromJsonArray(jsonItems, load) {
|
|
281
|
+
return this.loadManyToOneForManyRows(jsonItems.map(row => {
|
|
282
|
+
let result = {};
|
|
283
|
+
for (const col of this.metadata.fields.toArray()) {
|
|
284
|
+
result[col.key] = col.valueConverter.fromJson(row[col.key]);
|
|
285
|
+
}
|
|
286
|
+
return result;
|
|
287
|
+
}), load);
|
|
288
|
+
}
|
|
289
|
+
async loadManyToOneForManyRows(rawRows, load) {
|
|
290
|
+
let loadFields = undefined;
|
|
291
|
+
if (load)
|
|
292
|
+
loadFields = load(this.metadata.fields);
|
|
293
|
+
for (const col of this.metadata.fields) {
|
|
294
|
+
let ei = getEntitySettings(col.valueType, false);
|
|
295
|
+
if (ei) {
|
|
296
|
+
let load = !col.options.lazy;
|
|
297
|
+
if (loadFields !== undefined)
|
|
298
|
+
load = loadFields.includes(col);
|
|
299
|
+
if (load) {
|
|
300
|
+
let repo = this.remult.repo(col.valueType);
|
|
301
|
+
let toLoad = [];
|
|
302
|
+
for (const r of rawRows) {
|
|
303
|
+
let val = r[col.key];
|
|
304
|
+
if (val !== undefined && val !== null && !toLoad.includes(val) && !repo.idCache.has(val)) {
|
|
305
|
+
toLoad.push(val);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (toLoad.length > 0) {
|
|
309
|
+
await loadManyToOne(repo, toLoad);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
async function loadManyToOne(repo, toLoad) {
|
|
315
|
+
let rows = await repo.find({ where: repo.metadata.idMetadata.getIdFilter(...toLoad) }, true);
|
|
316
|
+
for (const r of rows) {
|
|
317
|
+
repo.addToCache(r);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
let result = await Promise.all(rawRows.map(async (r) => await this.mapRawDataToResult(r, loadFields)));
|
|
321
|
+
return result;
|
|
322
|
+
}
|
|
323
|
+
async mapRawDataToResult(r, loadFields) {
|
|
324
|
+
if (!r)
|
|
325
|
+
return undefined;
|
|
326
|
+
let x = new this.entity(this.remult);
|
|
327
|
+
let helper = new rowHelperImplementation(this._info, x, this, this.edp, this.remult, false);
|
|
328
|
+
Object.defineProperty(x, entityMember, {
|
|
329
|
+
get: () => helper
|
|
330
|
+
});
|
|
331
|
+
await helper.loadDataFrom(r, loadFields);
|
|
332
|
+
helper.saveOriginalData();
|
|
333
|
+
return x;
|
|
334
|
+
}
|
|
335
|
+
toJson(item) {
|
|
336
|
+
if (Array.isArray(item))
|
|
337
|
+
return item.map(x => this.toJson(x));
|
|
338
|
+
return this.getEntityRef(item).toApiJson(true);
|
|
339
|
+
}
|
|
340
|
+
fromJson(json, newRow) {
|
|
341
|
+
if (json === null || json === undefined)
|
|
342
|
+
return json;
|
|
343
|
+
if (Array.isArray(json))
|
|
344
|
+
return json.map(item => this.fromJson(item));
|
|
345
|
+
let result = new this.entity(this.remult);
|
|
346
|
+
for (const col of this.fieldsOf(json)) {
|
|
347
|
+
let ei = getEntitySettings(col.valueType, false);
|
|
348
|
+
if (ei) {
|
|
349
|
+
result[col.key] = this.remult.repo(col.valueType).fromJson(json[col.key]);
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
if (json[col.key] !== undefined) {
|
|
353
|
+
result[col.key] = col.valueConverter.fromJson(json[col.key]);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
this.fixTypes(result);
|
|
358
|
+
if (newRow) {
|
|
359
|
+
return this.create(result);
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
let row = new rowHelperImplementation(this._info, result, this, this.edp, this.remult, false);
|
|
363
|
+
Object.defineProperty(result, entityMember, {
|
|
364
|
+
get: () => row
|
|
365
|
+
});
|
|
366
|
+
row.saveOriginalData();
|
|
367
|
+
return result;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
async count(where) {
|
|
371
|
+
return this.edp.count(await this.translateWhereToFilter(where));
|
|
372
|
+
}
|
|
373
|
+
cache = new Map();
|
|
374
|
+
async findFirst(where, options, skipOrderByAndLimit = false) {
|
|
375
|
+
if (!options)
|
|
376
|
+
options = {};
|
|
377
|
+
if (where) {
|
|
378
|
+
if (options.where) {
|
|
379
|
+
let w = options.where;
|
|
380
|
+
options.where = {
|
|
381
|
+
$and: [
|
|
382
|
+
w,
|
|
383
|
+
where
|
|
384
|
+
]
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
else
|
|
388
|
+
options.where = where;
|
|
389
|
+
}
|
|
390
|
+
let r;
|
|
391
|
+
let cacheInfo;
|
|
392
|
+
if (options.useCache) {
|
|
393
|
+
let f = await entityFilterToJson(this.metadata, options.where);
|
|
394
|
+
let key = JSON.stringify(f);
|
|
395
|
+
cacheInfo = this.cache.get(key);
|
|
396
|
+
if (cacheInfo !== undefined) {
|
|
397
|
+
if (cacheInfo.value && this.getEntityRef(cacheInfo.value).wasDeleted()) {
|
|
398
|
+
cacheInfo = undefined;
|
|
399
|
+
this.cache.delete(key);
|
|
400
|
+
}
|
|
401
|
+
else
|
|
402
|
+
return this.cache.get(key).promise;
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
cacheInfo = {
|
|
406
|
+
value: undefined,
|
|
407
|
+
promise: undefined
|
|
408
|
+
};
|
|
409
|
+
this.cache.set(key, cacheInfo);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
r = this.find({ ...options, limit: 1 }, skipOrderByAndLimit).then(async (items) => {
|
|
413
|
+
let r = undefined;
|
|
414
|
+
if (items.length > 0)
|
|
415
|
+
r = items[0];
|
|
416
|
+
if (!r && options.createIfNotFound) {
|
|
417
|
+
r = this.create();
|
|
418
|
+
if (options.where) {
|
|
419
|
+
await __updateEntityBasedOnWhere(this.metadata, options.where, r);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return r;
|
|
423
|
+
});
|
|
424
|
+
if (cacheInfo) {
|
|
425
|
+
cacheInfo.promise = r = r.then(r => {
|
|
426
|
+
cacheInfo.value = r;
|
|
427
|
+
return r;
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
return r;
|
|
431
|
+
}
|
|
432
|
+
fieldsOf(item) {
|
|
433
|
+
let keys = Object.keys(item);
|
|
434
|
+
return this.metadata.fields.toArray().filter(x => keys.includes(x.key));
|
|
435
|
+
}
|
|
436
|
+
create(item) {
|
|
437
|
+
let r = new this.entity(this.remult);
|
|
438
|
+
if (item) {
|
|
439
|
+
for (const field of this.fieldsOf(item)) {
|
|
440
|
+
r[field.key] = item[field.key];
|
|
441
|
+
}
|
|
442
|
+
this.fixTypes(r);
|
|
443
|
+
}
|
|
444
|
+
let z = this.getEntityRef(r);
|
|
445
|
+
return r;
|
|
446
|
+
}
|
|
447
|
+
async fixTypes(item) {
|
|
448
|
+
for (const field of this.fieldsOf(item)) {
|
|
449
|
+
const val = item[field.key];
|
|
450
|
+
if (val !== null && val !== undefined) {
|
|
451
|
+
if (field.valueType === Date && !(val instanceof Date))
|
|
452
|
+
item[field.key] = field.valueConverter.fromJson(field.valueConverter.toJson(val));
|
|
453
|
+
else
|
|
454
|
+
for (const [type, typeName] of [[String, "string"], [Number, "number"], [Boolean, "boolean"]]) {
|
|
455
|
+
if (field.valueType === type && typeof val !== typeName)
|
|
456
|
+
item[field.key] = field.valueConverter.fromJson(field.valueConverter.toJson(val));
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return item;
|
|
461
|
+
}
|
|
462
|
+
findId(id, options) {
|
|
463
|
+
if (id === null || id === undefined)
|
|
464
|
+
return null;
|
|
465
|
+
if (typeof id !== "string" && typeof id !== "number")
|
|
466
|
+
throw new Error("id can be either number or string, but got: " + typeof (id));
|
|
467
|
+
return this.findFirst({}, {
|
|
468
|
+
useCache: true,
|
|
469
|
+
...options,
|
|
470
|
+
where: this.metadata.idMetadata.getIdFilter(id),
|
|
471
|
+
}, true);
|
|
472
|
+
}
|
|
473
|
+
/* @internal*/
|
|
474
|
+
async translateWhereToFilter(where) {
|
|
475
|
+
if (this.metadata.options.backendPrefilter && isBackend()) {
|
|
476
|
+
let z = where;
|
|
477
|
+
where = {
|
|
478
|
+
$and: [
|
|
479
|
+
z, await Filter.resolve(this.metadata.options.backendPrefilter)
|
|
480
|
+
]
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
let r = await Filter.fromEntityFilter(this.metadata, where);
|
|
484
|
+
if (r && !this.dataProvider.isProxy) {
|
|
485
|
+
r = await Filter.translateCustomWhere(r, this.metadata, this.remult);
|
|
486
|
+
}
|
|
487
|
+
return r;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
export function __updateEntityBasedOnWhere(entityDefs, where, r) {
|
|
491
|
+
let w = Filter.fromEntityFilter(entityDefs, where);
|
|
492
|
+
const emptyFunction = () => { };
|
|
493
|
+
if (w) {
|
|
494
|
+
w.__applyToConsumer({
|
|
495
|
+
custom: emptyFunction,
|
|
496
|
+
databaseCustom: emptyFunction,
|
|
497
|
+
containsCaseInsensitive: emptyFunction,
|
|
498
|
+
isDifferentFrom: emptyFunction,
|
|
499
|
+
isEqualTo: (col, val) => {
|
|
500
|
+
r[col.key] = val;
|
|
501
|
+
},
|
|
502
|
+
isGreaterOrEqualTo: emptyFunction,
|
|
503
|
+
isGreaterThan: emptyFunction,
|
|
504
|
+
isIn: emptyFunction,
|
|
505
|
+
isLessOrEqualTo: emptyFunction,
|
|
506
|
+
isLessThan: emptyFunction,
|
|
507
|
+
isNotNull: emptyFunction,
|
|
508
|
+
isNull: emptyFunction,
|
|
509
|
+
or: emptyFunction
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
export const entityInfo = Symbol("entityInfo");
|
|
514
|
+
export const entityInfo_key = Symbol("entityInfo_key");
|
|
515
|
+
const entityMember = Symbol("entityMember");
|
|
516
|
+
export function getEntitySettings(entity, throwError = true) {
|
|
517
|
+
if (entity === undefined)
|
|
518
|
+
if (throwError) {
|
|
519
|
+
throw new Error("Undefined is not an entity :)");
|
|
520
|
+
}
|
|
521
|
+
else
|
|
522
|
+
return undefined;
|
|
523
|
+
let info = Reflect.getMetadata(entityInfo, entity);
|
|
524
|
+
if (!info && throwError)
|
|
525
|
+
throw new Error(entity.prototype.constructor.name + " is not a known entity, did you forget to set @Entity() or did you forget to add the '@' before the call to Entity?");
|
|
526
|
+
return info;
|
|
527
|
+
}
|
|
528
|
+
export function getEntityKey(entity) {
|
|
529
|
+
return Reflect.getMetadata(entityInfo_key, entity);
|
|
530
|
+
}
|
|
531
|
+
export const columnsOfType = new Map();
|
|
532
|
+
export function createOldEntity(entity, remult) {
|
|
533
|
+
let r = columnsOfType.get(entity);
|
|
534
|
+
if (!r)
|
|
535
|
+
columnsOfType.set(entity, r = []);
|
|
536
|
+
let info = getEntitySettings(entity)(remult);
|
|
537
|
+
let key = getEntityKey(entity);
|
|
538
|
+
let base = Object.getPrototypeOf(entity);
|
|
539
|
+
while (base != null) {
|
|
540
|
+
let baseCols = columnsOfType.get(base);
|
|
541
|
+
if (baseCols) {
|
|
542
|
+
r.unshift(...baseCols.filter(x => !r.find(y => y.key == x.key)));
|
|
543
|
+
}
|
|
544
|
+
let baseSettingsFactory = getEntitySettings(base, false);
|
|
545
|
+
if (baseSettingsFactory) {
|
|
546
|
+
let baseSettings = baseSettingsFactory(remult);
|
|
547
|
+
info = { ...baseSettings, ...info };
|
|
548
|
+
let functions = ["saving", "saved", "deleting", "deleted", "validation"];
|
|
549
|
+
for (const key of functions) {
|
|
550
|
+
if (baseSettings[key] && baseSettings[key] !== info[key]) {
|
|
551
|
+
let x = info[key];
|
|
552
|
+
info[key] = async (a, b) => {
|
|
553
|
+
await x(a, b);
|
|
554
|
+
await baseSettings[key](a, b);
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
base = Object.getPrototypeOf(base);
|
|
560
|
+
}
|
|
561
|
+
return new EntityFullInfo(prepareColumnInfo(r, remult), info, remult, entity, key);
|
|
562
|
+
}
|
|
563
|
+
class rowHelperBase {
|
|
564
|
+
columnsInfo;
|
|
565
|
+
instance;
|
|
566
|
+
remult;
|
|
567
|
+
_error;
|
|
568
|
+
get error() {
|
|
569
|
+
this._subscribers?.reportObserved();
|
|
570
|
+
return this._error;
|
|
571
|
+
}
|
|
572
|
+
set error(val) {
|
|
573
|
+
this._error = val;
|
|
574
|
+
this._subscribers?.reportChanged();
|
|
575
|
+
}
|
|
576
|
+
constructor(columnsInfo, instance, remult) {
|
|
577
|
+
this.columnsInfo = columnsInfo;
|
|
578
|
+
this.instance = instance;
|
|
579
|
+
this.remult = remult;
|
|
580
|
+
{
|
|
581
|
+
let fac = remult;
|
|
582
|
+
if (fac != null && fac.remultFactory) {
|
|
583
|
+
remult = fac.remultFactory();
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
for (const col of columnsInfo) {
|
|
587
|
+
let ei = getEntitySettings(col.valueType, false);
|
|
588
|
+
if (ei && remult) {
|
|
589
|
+
let lookup = new LookupColumn(remult.repo(col.valueType), undefined);
|
|
590
|
+
this.lookups.set(col.key, lookup);
|
|
591
|
+
let val = instance[col.key];
|
|
592
|
+
let refImpl;
|
|
593
|
+
Object.defineProperty(instance, col.key, {
|
|
594
|
+
get: () => {
|
|
595
|
+
if (this._subscribers) {
|
|
596
|
+
this._subscribers.reportObserved();
|
|
597
|
+
if (!refImpl) {
|
|
598
|
+
refImpl = this.fields.find(col.key);
|
|
599
|
+
if (!refImpl._subscribers) {
|
|
600
|
+
refImpl._subscribers = new SubscribableImp();
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
refImpl._subscribers.reportObserved();
|
|
604
|
+
}
|
|
605
|
+
return lookup.item;
|
|
606
|
+
},
|
|
607
|
+
set: (val) => {
|
|
608
|
+
lookup.set(val);
|
|
609
|
+
this._subscribers?.reportChanged();
|
|
610
|
+
if (!refImpl) {
|
|
611
|
+
refImpl = this.fields.find(col.key);
|
|
612
|
+
if (!refImpl._subscribers) {
|
|
613
|
+
refImpl._subscribers = new SubscribableImp();
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
refImpl._subscribers.reportChanged();
|
|
617
|
+
},
|
|
618
|
+
enumerable: true
|
|
619
|
+
});
|
|
620
|
+
lookup.set(val);
|
|
621
|
+
}
|
|
622
|
+
else {
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
_subscribers;
|
|
627
|
+
subscribe(listener) {
|
|
628
|
+
this.initSubscribers();
|
|
629
|
+
return this._subscribers.subscribe(listener);
|
|
630
|
+
}
|
|
631
|
+
_isLoading = false;
|
|
632
|
+
initSubscribers() {
|
|
633
|
+
if (!this._subscribers) {
|
|
634
|
+
this._subscribers = new SubscribableImp();
|
|
635
|
+
;
|
|
636
|
+
for (const col of this.columnsInfo) {
|
|
637
|
+
let ei = getEntitySettings(col.valueType, false);
|
|
638
|
+
let refImpl = this.fields.find(col.key);
|
|
639
|
+
refImpl._subscribers = new SubscribableImp();
|
|
640
|
+
if (ei && this.remult) {
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
let val = this.instance[col.key];
|
|
644
|
+
Object.defineProperty(this.instance, col.key, {
|
|
645
|
+
get: () => {
|
|
646
|
+
this._subscribers.reportObserved();
|
|
647
|
+
refImpl._subscribers.reportObserved();
|
|
648
|
+
return val;
|
|
649
|
+
},
|
|
650
|
+
set: (value) => {
|
|
651
|
+
val = value;
|
|
652
|
+
this._subscribers.reportChanged();
|
|
653
|
+
refImpl._subscribers.reportChanged();
|
|
654
|
+
},
|
|
655
|
+
enumerable: true
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
get isLoading() {
|
|
662
|
+
this._subscribers?.reportObserved();
|
|
663
|
+
return this._isLoading;
|
|
664
|
+
}
|
|
665
|
+
set isLoading(val) {
|
|
666
|
+
this._isLoading = val;
|
|
667
|
+
this._subscribers?.reportChanged();
|
|
668
|
+
}
|
|
669
|
+
lookups = new Map();
|
|
670
|
+
async waitLoad() {
|
|
671
|
+
await Promise.all([...this.lookups.values()].map(x => x.waitLoad()));
|
|
672
|
+
}
|
|
673
|
+
errors;
|
|
674
|
+
__assertValidity() {
|
|
675
|
+
if (!this.hasErrors())
|
|
676
|
+
throw this.buildErrorInfoObject();
|
|
677
|
+
}
|
|
678
|
+
buildErrorInfoObject() {
|
|
679
|
+
let error = {
|
|
680
|
+
modelState: Object.assign({}, this.errors),
|
|
681
|
+
message: this.error
|
|
682
|
+
};
|
|
683
|
+
if (!error.message) {
|
|
684
|
+
for (const col of this.columnsInfo) {
|
|
685
|
+
if (this.errors[col.key]) {
|
|
686
|
+
error.message = this.fields[col.key].metadata.caption + ": " + this.errors[col.key];
|
|
687
|
+
this.error = error.message;
|
|
688
|
+
break;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
return error;
|
|
693
|
+
}
|
|
694
|
+
catchSaveErrors(err) {
|
|
695
|
+
let e = err;
|
|
696
|
+
if (e instanceof Promise) {
|
|
697
|
+
return e.then(x => this.catchSaveErrors(x));
|
|
698
|
+
}
|
|
699
|
+
if (e.error) {
|
|
700
|
+
e = e.error;
|
|
701
|
+
}
|
|
702
|
+
if (e.message)
|
|
703
|
+
this.error = e.message;
|
|
704
|
+
else if (e.Message)
|
|
705
|
+
this.error = e.Message;
|
|
706
|
+
else
|
|
707
|
+
this.error = e;
|
|
708
|
+
let s = e.modelState;
|
|
709
|
+
if (!s)
|
|
710
|
+
s = e.ModelState;
|
|
711
|
+
if (s) {
|
|
712
|
+
this.errors = s;
|
|
713
|
+
}
|
|
714
|
+
throw err;
|
|
715
|
+
}
|
|
716
|
+
__clearErrorsAndReportChanged() {
|
|
717
|
+
this.errors = undefined;
|
|
718
|
+
this.error = undefined;
|
|
719
|
+
this._reportChangedToEntityAndFields();
|
|
720
|
+
}
|
|
721
|
+
_reportChangedToEntityAndFields() {
|
|
722
|
+
if (this._subscribers) {
|
|
723
|
+
this._subscribers.reportChanged();
|
|
724
|
+
for (const field of this.fields) {
|
|
725
|
+
let ref = field;
|
|
726
|
+
ref._subscribers.reportChanged();
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
hasErrors() {
|
|
731
|
+
this._subscribers?.reportObserved();
|
|
732
|
+
return !!!this.error && this.errors == undefined;
|
|
733
|
+
}
|
|
734
|
+
copyDataToObject() {
|
|
735
|
+
let d = {};
|
|
736
|
+
for (const col of this.columnsInfo) {
|
|
737
|
+
let lu = this.lookups.get(col.key);
|
|
738
|
+
let val = undefined;
|
|
739
|
+
if (lu)
|
|
740
|
+
val = lu.id;
|
|
741
|
+
else
|
|
742
|
+
val = this.instance[col.key];
|
|
743
|
+
if (val !== undefined) {
|
|
744
|
+
val = col.valueConverter.toJson(val);
|
|
745
|
+
if (val !== undefined && val !== null)
|
|
746
|
+
val = col.valueConverter.fromJson(JSON.parse(JSON.stringify(val)));
|
|
747
|
+
}
|
|
748
|
+
d[col.key] = val;
|
|
749
|
+
}
|
|
750
|
+
return d;
|
|
751
|
+
}
|
|
752
|
+
originalValues = {};
|
|
753
|
+
saveOriginalData() {
|
|
754
|
+
this.originalValues = this.copyDataToObject();
|
|
755
|
+
this.saveMoreOriginalData();
|
|
756
|
+
}
|
|
757
|
+
saveMoreOriginalData() {
|
|
758
|
+
}
|
|
759
|
+
async validate() {
|
|
760
|
+
this.__clearErrorsAndReportChanged();
|
|
761
|
+
if (classValidatorValidate)
|
|
762
|
+
await classValidatorValidate(this.instance, this);
|
|
763
|
+
await this.__performColumnAndEntityValidations();
|
|
764
|
+
let r = this.hasErrors();
|
|
765
|
+
if (!this.hasErrors())
|
|
766
|
+
return this.buildErrorInfoObject();
|
|
767
|
+
else
|
|
768
|
+
return undefined;
|
|
769
|
+
}
|
|
770
|
+
async __validateEntity() {
|
|
771
|
+
this.__clearErrorsAndReportChanged();
|
|
772
|
+
if (classValidatorValidate)
|
|
773
|
+
await classValidatorValidate(this.instance, this);
|
|
774
|
+
await this.__performColumnAndEntityValidations();
|
|
775
|
+
this.__assertValidity();
|
|
776
|
+
}
|
|
777
|
+
async __performColumnAndEntityValidations() {
|
|
778
|
+
}
|
|
779
|
+
toApiJson(includeRelatedEntities = false) {
|
|
780
|
+
let result = {};
|
|
781
|
+
for (const col of this.columnsInfo) {
|
|
782
|
+
if (!this.remult || col.includeInApi === undefined || this.remult.isAllowed(col.includeInApi)) {
|
|
783
|
+
let val;
|
|
784
|
+
let lu = this.lookups.get(col.key);
|
|
785
|
+
if (lu)
|
|
786
|
+
if (includeRelatedEntities)
|
|
787
|
+
val = lu.toJson();
|
|
788
|
+
else
|
|
789
|
+
val = lu.id;
|
|
790
|
+
else {
|
|
791
|
+
val = this.instance[col.key];
|
|
792
|
+
if (!this.remult) {
|
|
793
|
+
if (val) {
|
|
794
|
+
let eo = getEntitySettings(val.constructor, false);
|
|
795
|
+
if (eo) {
|
|
796
|
+
val = getEntityRef(val).getId();
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
result[col.key] = col.valueConverter.toJson(val);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
return result;
|
|
805
|
+
}
|
|
806
|
+
async _updateEntityBasedOnApi(body) {
|
|
807
|
+
let keys = Object.keys(body);
|
|
808
|
+
for (const col of this.columnsInfo) {
|
|
809
|
+
if (keys.includes(col.key))
|
|
810
|
+
if (col.includeInApi === undefined || this.remult.isAllowed(col.includeInApi)) {
|
|
811
|
+
if (!this.remult || col.allowApiUpdate === undefined || this.remult.isAllowedForInstance(this.instance, col.allowApiUpdate)) {
|
|
812
|
+
let lu = this.lookups.get(col.key);
|
|
813
|
+
if (lu)
|
|
814
|
+
lu.id = body[col.key];
|
|
815
|
+
else
|
|
816
|
+
this.instance[col.key] = col.valueConverter.fromJson(body[col.key]);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
await Promise.all([...this.fields].map(x => x.load()));
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
export class rowHelperImplementation extends rowHelperBase {
|
|
824
|
+
info;
|
|
825
|
+
repository;
|
|
826
|
+
edp;
|
|
827
|
+
_isNew;
|
|
828
|
+
constructor(info, instance, repository, edp, remult, _isNew) {
|
|
829
|
+
super(info.columnsInfo, instance, remult);
|
|
830
|
+
this.info = info;
|
|
831
|
+
this.repository = repository;
|
|
832
|
+
this.edp = edp;
|
|
833
|
+
this._isNew = _isNew;
|
|
834
|
+
this.metadata = info;
|
|
835
|
+
if (_isNew) {
|
|
836
|
+
for (const col of info.columnsInfo) {
|
|
837
|
+
if (col.defaultValue && instance[col.key] === undefined) {
|
|
838
|
+
if (typeof col.defaultValue === "function") {
|
|
839
|
+
instance[col.key] = col.defaultValue(instance);
|
|
840
|
+
}
|
|
841
|
+
else if (!instance[col.key])
|
|
842
|
+
instance[col.key] = col.defaultValue;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
if (this.info.options.entityRefInit)
|
|
847
|
+
this.info.options.entityRefInit(this, instance);
|
|
848
|
+
if (Remult.entityRefInit)
|
|
849
|
+
Remult.entityRefInit(this, instance);
|
|
850
|
+
}
|
|
851
|
+
get apiUpdateAllowed() { return this.remult.isAllowedForInstance(this.instance, this.metadata.options.allowApiUpdate); }
|
|
852
|
+
get apiDeleteAllowed() { return this.remult.isAllowedForInstance(this.instance, this.metadata.options.allowApiDelete); }
|
|
853
|
+
get apiInsertAllowed() { return this.remult.isAllowedForInstance(this.instance, this.metadata.options.allowApiInsert); }
|
|
854
|
+
metadata;
|
|
855
|
+
getId() {
|
|
856
|
+
return this.info.idMetadata.getId(this.instance);
|
|
857
|
+
}
|
|
858
|
+
saveMoreOriginalData() {
|
|
859
|
+
this.originalId = this.getId();
|
|
860
|
+
}
|
|
861
|
+
_wasDeleted = false;
|
|
862
|
+
wasDeleted() {
|
|
863
|
+
this._subscribers?.reportObserved();
|
|
864
|
+
return this._wasDeleted;
|
|
865
|
+
}
|
|
866
|
+
undoChanges() {
|
|
867
|
+
this.loadDataFrom(this.originalValues);
|
|
868
|
+
this.__clearErrorsAndReportChanged();
|
|
869
|
+
}
|
|
870
|
+
async reload() {
|
|
871
|
+
await this.edp.find({ where: this.getIdFilter() }).then(async (newData) => {
|
|
872
|
+
await this.loadDataFrom(newData[0]);
|
|
873
|
+
this.saveOriginalData();
|
|
874
|
+
});
|
|
875
|
+
this._reportChangedToEntityAndFields();
|
|
876
|
+
return this.instance;
|
|
877
|
+
}
|
|
878
|
+
_columns;
|
|
879
|
+
get fields() {
|
|
880
|
+
if (!this._columns) {
|
|
881
|
+
let _items = [];
|
|
882
|
+
let r = {
|
|
883
|
+
find: (c) => r[typeof c === "string" ? c : c.key],
|
|
884
|
+
[Symbol.iterator]: () => _items[Symbol.iterator](),
|
|
885
|
+
toArray: () => _items
|
|
886
|
+
};
|
|
887
|
+
for (const c of this.info.columnsInfo) {
|
|
888
|
+
_items.push(r[c.key] = new FieldRefImplementation(c, this.info.fields[c.key], this.instance, this, this));
|
|
889
|
+
}
|
|
890
|
+
this._columns = r;
|
|
891
|
+
}
|
|
892
|
+
return this._columns;
|
|
893
|
+
}
|
|
894
|
+
_saving = false;
|
|
895
|
+
async save() {
|
|
896
|
+
try {
|
|
897
|
+
if (this._saving)
|
|
898
|
+
throw new Error("cannot save while entity is already saving");
|
|
899
|
+
this._saving = true;
|
|
900
|
+
if (this.wasDeleted())
|
|
901
|
+
throw new Error("cannot save a deleted row");
|
|
902
|
+
this.isLoading = true;
|
|
903
|
+
await this.__validateEntity();
|
|
904
|
+
let doNotSave = false;
|
|
905
|
+
for (const col of this.fields) {
|
|
906
|
+
if (col.metadata.options.saving)
|
|
907
|
+
await col.metadata.options.saving(this.instance, col);
|
|
908
|
+
}
|
|
909
|
+
if (this.info.entityInfo.saving) {
|
|
910
|
+
await this.info.entityInfo.saving(this.instance, () => doNotSave = true);
|
|
911
|
+
}
|
|
912
|
+
this.__assertValidity();
|
|
913
|
+
let d = this.copyDataToObject();
|
|
914
|
+
let ignoreKeys = [];
|
|
915
|
+
for (const field of this.metadata.fields) {
|
|
916
|
+
if (field.dbReadOnly) {
|
|
917
|
+
d[field.key] = undefined;
|
|
918
|
+
ignoreKeys.push(field.key);
|
|
919
|
+
let f = this.fields.find(field);
|
|
920
|
+
f.value = f.originalValue;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
if (this.info.idMetadata.field instanceof CompoundIdField)
|
|
924
|
+
delete (d.id);
|
|
925
|
+
let updatedRow;
|
|
926
|
+
let isNew = this.isNew();
|
|
927
|
+
try {
|
|
928
|
+
this._subscribers?.reportChanged();
|
|
929
|
+
if (this.isNew()) {
|
|
930
|
+
updatedRow = await this.edp.insert(d);
|
|
931
|
+
}
|
|
932
|
+
else {
|
|
933
|
+
let changesOnly = {};
|
|
934
|
+
let wasChanged = false;
|
|
935
|
+
for (const key in d) {
|
|
936
|
+
if (Object.prototype.hasOwnProperty.call(d, key)) {
|
|
937
|
+
const element = d[key];
|
|
938
|
+
if (this.fields.find(key).valueChanged() && !ignoreKeys.includes(key)) {
|
|
939
|
+
changesOnly[key] = element;
|
|
940
|
+
wasChanged = true;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
if (!wasChanged)
|
|
945
|
+
return this.instance;
|
|
946
|
+
if (doNotSave) {
|
|
947
|
+
updatedRow = (await this.edp.find({ where: this.getIdFilter() }))[0];
|
|
948
|
+
}
|
|
949
|
+
else {
|
|
950
|
+
updatedRow = await this.edp.update(this.id, changesOnly);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
await this.loadDataFrom(updatedRow);
|
|
954
|
+
if (this.info.entityInfo.saved)
|
|
955
|
+
await this.info.entityInfo.saved(this.instance);
|
|
956
|
+
if (this.repository.listeners)
|
|
957
|
+
for (const listener of this.repository.listeners.filter(x => x.saved)) {
|
|
958
|
+
await listener.saved(this.instance, isNew);
|
|
959
|
+
}
|
|
960
|
+
await this.repository.remult.liveQueryPublisher.itemChanged(this.repository.metadata.key, [{ id: this.getId(), oldId: this.getOriginalId(), deleted: false }]);
|
|
961
|
+
this.saveOriginalData();
|
|
962
|
+
this._isNew = false;
|
|
963
|
+
return this.instance;
|
|
964
|
+
}
|
|
965
|
+
catch (err) {
|
|
966
|
+
await this.catchSaveErrors(err);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
finally {
|
|
970
|
+
this.isLoading = false;
|
|
971
|
+
this._reportChangedToEntityAndFields();
|
|
972
|
+
this._saving = false;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
getIdFilter() {
|
|
976
|
+
return Filter.fromEntityFilter(this.metadata, this.repository.metadata.idMetadata.getIdFilter(this.id));
|
|
977
|
+
}
|
|
978
|
+
async delete() {
|
|
979
|
+
this.__clearErrorsAndReportChanged();
|
|
980
|
+
if (this.info.entityInfo.deleting)
|
|
981
|
+
await this.info.entityInfo.deleting(this.instance);
|
|
982
|
+
this.__assertValidity();
|
|
983
|
+
try {
|
|
984
|
+
await this.edp.delete(this.id);
|
|
985
|
+
if (this.info.entityInfo.deleted)
|
|
986
|
+
await this.info.entityInfo.deleted(this.instance);
|
|
987
|
+
if (this.repository.listeners)
|
|
988
|
+
for (const listener of this.repository.listeners.filter(x => x.deleted)) {
|
|
989
|
+
await listener.deleted(this.instance);
|
|
990
|
+
}
|
|
991
|
+
await this.repository.remult.liveQueryPublisher.itemChanged(this.repository.metadata.key, [{ id: this.getId(), oldId: this.getOriginalId(), deleted: true }]);
|
|
992
|
+
this._wasDeleted = true;
|
|
993
|
+
}
|
|
994
|
+
catch (err) {
|
|
995
|
+
await this.catchSaveErrors(err);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
async loadDataFrom(data, loadItems) {
|
|
999
|
+
for (const col of this.info.fields) {
|
|
1000
|
+
let lu = this.lookups.get(col.key);
|
|
1001
|
+
if (lu) {
|
|
1002
|
+
lu.id = data[col.key];
|
|
1003
|
+
if (loadItems === undefined) {
|
|
1004
|
+
if (!col.options.lazy)
|
|
1005
|
+
await lu.waitLoad();
|
|
1006
|
+
}
|
|
1007
|
+
else {
|
|
1008
|
+
if (loadItems.includes(col))
|
|
1009
|
+
await lu.waitLoad();
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
else
|
|
1013
|
+
this.instance[col.key] = data[col.key];
|
|
1014
|
+
}
|
|
1015
|
+
await this.calcServerExpression();
|
|
1016
|
+
this.id = this.getId();
|
|
1017
|
+
}
|
|
1018
|
+
id;
|
|
1019
|
+
originalId;
|
|
1020
|
+
getOriginalId() {
|
|
1021
|
+
return this.originalId;
|
|
1022
|
+
}
|
|
1023
|
+
async calcServerExpression() {
|
|
1024
|
+
if (isBackend())
|
|
1025
|
+
for (const col of this.info.columnsInfo) {
|
|
1026
|
+
if (col.serverExpression) {
|
|
1027
|
+
this.instance[col.key] = await col.serverExpression(this.instance);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
isNew() {
|
|
1032
|
+
this._subscribers?.reportObserved();
|
|
1033
|
+
return this._isNew;
|
|
1034
|
+
}
|
|
1035
|
+
wasChanged() {
|
|
1036
|
+
this._subscribers?.reportObserved();
|
|
1037
|
+
for (const col of this.fields) {
|
|
1038
|
+
if (col.valueChanged())
|
|
1039
|
+
return true;
|
|
1040
|
+
}
|
|
1041
|
+
return false;
|
|
1042
|
+
}
|
|
1043
|
+
async __performColumnAndEntityValidations() {
|
|
1044
|
+
for (const c of this.columnsInfo) {
|
|
1045
|
+
if (c.validate) {
|
|
1046
|
+
let col = new FieldRefImplementation(c, this.info.fields[c.key], this.instance, this, this);
|
|
1047
|
+
await col.__performValidation();
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
if (this.info.entityInfo.validation)
|
|
1051
|
+
await this.info.entityInfo.validation(this.instance, this);
|
|
1052
|
+
if (this.repository.listeners)
|
|
1053
|
+
for (const listener of this.repository.listeners.filter(x => x.validating)) {
|
|
1054
|
+
await listener.validating(this.instance);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
const controllerColumns = Symbol("controllerColumns");
|
|
1059
|
+
function prepareColumnInfo(r, remult) {
|
|
1060
|
+
return r.map(x => decorateColumnSettings(x.settings(remult), remult));
|
|
1061
|
+
}
|
|
1062
|
+
export function getFields(container, remult) {
|
|
1063
|
+
return getControllerRef(container, remult).fields;
|
|
1064
|
+
}
|
|
1065
|
+
export function getControllerRef(container, remultArg) {
|
|
1066
|
+
const remultVar = remultArg || defaultRemult;
|
|
1067
|
+
let result = container[controllerColumns];
|
|
1068
|
+
if (!result)
|
|
1069
|
+
result = container[entityMember];
|
|
1070
|
+
if (!result) {
|
|
1071
|
+
let columnSettings = columnsOfType.get(container.constructor);
|
|
1072
|
+
if (!columnSettings)
|
|
1073
|
+
columnsOfType.set(container.constructor, columnSettings = []);
|
|
1074
|
+
let base = Object.getPrototypeOf(container.constructor);
|
|
1075
|
+
while (base != null) {
|
|
1076
|
+
let baseCols = columnsOfType.get(base);
|
|
1077
|
+
if (baseCols) {
|
|
1078
|
+
columnSettings.unshift(...baseCols.filter(x => !columnSettings.find(y => y.key == x.key)));
|
|
1079
|
+
}
|
|
1080
|
+
base = Object.getPrototypeOf(base);
|
|
1081
|
+
}
|
|
1082
|
+
container[controllerColumns] = result = new controllerRefImpl(prepareColumnInfo(columnSettings, remultVar), container, remultVar);
|
|
1083
|
+
}
|
|
1084
|
+
return result;
|
|
1085
|
+
}
|
|
1086
|
+
export class controllerRefImpl extends rowHelperBase {
|
|
1087
|
+
constructor(columnsInfo, instance, remult) {
|
|
1088
|
+
super(columnsInfo, instance, remult);
|
|
1089
|
+
let _items = [];
|
|
1090
|
+
let r = {
|
|
1091
|
+
find: (c) => r[typeof c === "string" ? c : c.key],
|
|
1092
|
+
[Symbol.iterator]: () => _items[Symbol.iterator](),
|
|
1093
|
+
toArray: () => _items
|
|
1094
|
+
};
|
|
1095
|
+
for (const col of columnsInfo) {
|
|
1096
|
+
_items.push(r[col.key] = new FieldRefImplementation(col, new columnDefsImpl(col, undefined, remult), instance, undefined, this));
|
|
1097
|
+
}
|
|
1098
|
+
this.fields = r;
|
|
1099
|
+
}
|
|
1100
|
+
async __performColumnAndEntityValidations() {
|
|
1101
|
+
for (const col of this.fields) {
|
|
1102
|
+
if (col instanceof FieldRefImplementation) {
|
|
1103
|
+
await col.__performValidation();
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
fields;
|
|
1108
|
+
}
|
|
1109
|
+
export class FieldRefImplementation {
|
|
1110
|
+
settings;
|
|
1111
|
+
metadata;
|
|
1112
|
+
container;
|
|
1113
|
+
helper;
|
|
1114
|
+
rowBase;
|
|
1115
|
+
constructor(settings, metadata, container, helper, rowBase) {
|
|
1116
|
+
this.settings = settings;
|
|
1117
|
+
this.metadata = metadata;
|
|
1118
|
+
this.container = container;
|
|
1119
|
+
this.helper = helper;
|
|
1120
|
+
this.rowBase = rowBase;
|
|
1121
|
+
this.target = this.settings.target;
|
|
1122
|
+
this.entityRef = this.helper;
|
|
1123
|
+
}
|
|
1124
|
+
_subscribers;
|
|
1125
|
+
subscribe(listener) {
|
|
1126
|
+
if (!this._subscribers) {
|
|
1127
|
+
this.rowBase.initSubscribers();
|
|
1128
|
+
}
|
|
1129
|
+
return this._subscribers.subscribe(listener);
|
|
1130
|
+
}
|
|
1131
|
+
valueIsNull() {
|
|
1132
|
+
this.reportObserved();
|
|
1133
|
+
let lu = this.rowBase.lookups.get(this.metadata.key);
|
|
1134
|
+
if (lu) {
|
|
1135
|
+
return lu.id === undefined || lu.id === null;
|
|
1136
|
+
}
|
|
1137
|
+
return this.value === null;
|
|
1138
|
+
}
|
|
1139
|
+
originalValueIsNull() {
|
|
1140
|
+
this.reportObserved();
|
|
1141
|
+
let lu = this.rowBase.lookups.get(this.metadata.key);
|
|
1142
|
+
return this.rawOriginalValue() === null;
|
|
1143
|
+
}
|
|
1144
|
+
async load() {
|
|
1145
|
+
let lu = this.rowBase.lookups.get(this.metadata.key);
|
|
1146
|
+
if (lu) {
|
|
1147
|
+
if (this.valueChanged()) {
|
|
1148
|
+
await lu.waitLoadOf(this.rawOriginalValue());
|
|
1149
|
+
}
|
|
1150
|
+
return await lu.waitLoad();
|
|
1151
|
+
}
|
|
1152
|
+
return this.value;
|
|
1153
|
+
}
|
|
1154
|
+
target;
|
|
1155
|
+
reportObserved() {
|
|
1156
|
+
this._subscribers?.reportObserved();
|
|
1157
|
+
this.rowBase._subscribers?.reportObserved();
|
|
1158
|
+
}
|
|
1159
|
+
reportChanged() {
|
|
1160
|
+
this._subscribers?.reportChanged();
|
|
1161
|
+
this.rowBase._subscribers?.reportChanged();
|
|
1162
|
+
}
|
|
1163
|
+
get error() {
|
|
1164
|
+
this.reportObserved();
|
|
1165
|
+
if (!this.rowBase.errors)
|
|
1166
|
+
return undefined;
|
|
1167
|
+
return this.rowBase.errors[this.metadata.key];
|
|
1168
|
+
}
|
|
1169
|
+
set error(error) {
|
|
1170
|
+
if (!this.rowBase.errors)
|
|
1171
|
+
this.rowBase.errors = {};
|
|
1172
|
+
this.rowBase.errors[this.metadata.key] = error;
|
|
1173
|
+
this.reportChanged();
|
|
1174
|
+
}
|
|
1175
|
+
get displayValue() {
|
|
1176
|
+
this.reportObserved();
|
|
1177
|
+
if (this.value != undefined) {
|
|
1178
|
+
if (this.settings.displayValue)
|
|
1179
|
+
return this.settings.displayValue(this.container, this.value);
|
|
1180
|
+
else if (this.metadata.valueConverter.displayValue)
|
|
1181
|
+
return this.metadata.valueConverter.displayValue(this.value);
|
|
1182
|
+
else
|
|
1183
|
+
return this.value.toString();
|
|
1184
|
+
}
|
|
1185
|
+
return "";
|
|
1186
|
+
}
|
|
1187
|
+
;
|
|
1188
|
+
get value() { return this.container[this.metadata.key]; }
|
|
1189
|
+
;
|
|
1190
|
+
set value(value) { this.container[this.metadata.key] = value; }
|
|
1191
|
+
;
|
|
1192
|
+
get originalValue() {
|
|
1193
|
+
this.reportObserved();
|
|
1194
|
+
let lu = this.rowBase.lookups.get(this.metadata.key);
|
|
1195
|
+
if (lu)
|
|
1196
|
+
return lu.get(this.rawOriginalValue());
|
|
1197
|
+
return this.rowBase.originalValues[this.metadata.key];
|
|
1198
|
+
}
|
|
1199
|
+
;
|
|
1200
|
+
rawOriginalValue() {
|
|
1201
|
+
return this.rowBase.originalValues[this.metadata.key];
|
|
1202
|
+
}
|
|
1203
|
+
setId(id) {
|
|
1204
|
+
this.value = id;
|
|
1205
|
+
}
|
|
1206
|
+
getId() {
|
|
1207
|
+
let lu = this.rowBase.lookups.get(this.metadata.key);
|
|
1208
|
+
if (lu)
|
|
1209
|
+
return lu.id != undefined ? lu.id : null;
|
|
1210
|
+
return this.value;
|
|
1211
|
+
}
|
|
1212
|
+
get inputValue() {
|
|
1213
|
+
this.reportObserved();
|
|
1214
|
+
let lu = this.rowBase.lookups.get(this.metadata.key);
|
|
1215
|
+
if (lu)
|
|
1216
|
+
return lu.id != undefined ? lu.id.toString() : null;
|
|
1217
|
+
return this.metadata.valueConverter.toInput(this.value, this.settings.inputType);
|
|
1218
|
+
}
|
|
1219
|
+
set inputValue(val) {
|
|
1220
|
+
let lu = this.rowBase.lookups.get(this.metadata.key);
|
|
1221
|
+
if (lu) {
|
|
1222
|
+
lu.setId(val);
|
|
1223
|
+
}
|
|
1224
|
+
else
|
|
1225
|
+
this.value = this.metadata.valueConverter.fromInput(val, this.settings.inputType);
|
|
1226
|
+
}
|
|
1227
|
+
;
|
|
1228
|
+
valueChanged() {
|
|
1229
|
+
this.reportObserved();
|
|
1230
|
+
let val = this.value;
|
|
1231
|
+
let lu = this.rowBase.lookups.get(this.metadata.key);
|
|
1232
|
+
if (lu) {
|
|
1233
|
+
val = lu.id;
|
|
1234
|
+
}
|
|
1235
|
+
return JSON.stringify(this.metadata.valueConverter.toJson(this.rowBase.originalValues[this.metadata.key])) != JSON.stringify(this.metadata.valueConverter.toJson(val));
|
|
1236
|
+
}
|
|
1237
|
+
entityRef;
|
|
1238
|
+
async __performValidation() {
|
|
1239
|
+
try {
|
|
1240
|
+
let x = typeof (this.settings.validate);
|
|
1241
|
+
if (Array.isArray(this.settings.validate)) {
|
|
1242
|
+
for (const v of this.settings.validate) {
|
|
1243
|
+
await v(this.container, this);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
else if (typeof this.settings.validate === 'function')
|
|
1247
|
+
await this.settings.validate(this.container, this);
|
|
1248
|
+
}
|
|
1249
|
+
catch (error) {
|
|
1250
|
+
if (typeof error === "string")
|
|
1251
|
+
this.error = error;
|
|
1252
|
+
else
|
|
1253
|
+
this.error = error.message;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
async validate() {
|
|
1257
|
+
await this.__performValidation();
|
|
1258
|
+
return !!!this.error;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
export function getEntityRef(entity, throwException = true) {
|
|
1262
|
+
let x = entity[entityMember];
|
|
1263
|
+
if (!x && throwException)
|
|
1264
|
+
throw new Error("item " + entity.constructor.name + " was not initialized using a context");
|
|
1265
|
+
return x;
|
|
1266
|
+
}
|
|
1267
|
+
export const CaptionTransformer = {
|
|
1268
|
+
transformCaption: (remult, key, caption) => caption
|
|
1269
|
+
};
|
|
1270
|
+
export function buildCaption(caption, key, remult) {
|
|
1271
|
+
let result;
|
|
1272
|
+
if (typeof (caption) === "function") {
|
|
1273
|
+
if (remult)
|
|
1274
|
+
result = caption(remult);
|
|
1275
|
+
}
|
|
1276
|
+
else if (caption)
|
|
1277
|
+
result = caption;
|
|
1278
|
+
result = CaptionTransformer.transformCaption(remult, key, result);
|
|
1279
|
+
if (result)
|
|
1280
|
+
return result;
|
|
1281
|
+
if (key)
|
|
1282
|
+
return makeTitle(key);
|
|
1283
|
+
return '';
|
|
1284
|
+
}
|
|
1285
|
+
export class columnDefsImpl {
|
|
1286
|
+
settings;
|
|
1287
|
+
entityDefs;
|
|
1288
|
+
remult;
|
|
1289
|
+
constructor(settings, entityDefs, remult) {
|
|
1290
|
+
this.settings = settings;
|
|
1291
|
+
this.entityDefs = entityDefs;
|
|
1292
|
+
this.remult = remult;
|
|
1293
|
+
this.options = this.settings;
|
|
1294
|
+
this.target = this.settings.target;
|
|
1295
|
+
this.valueConverter = this.settings.valueConverter;
|
|
1296
|
+
this.allowNull = !!this.settings.allowNull;
|
|
1297
|
+
this.valueType = this.settings.valueType;
|
|
1298
|
+
this.key = this.settings.key;
|
|
1299
|
+
this.inputType = this.settings.inputType;
|
|
1300
|
+
if (settings.serverExpression)
|
|
1301
|
+
this.isServerExpression = true;
|
|
1302
|
+
if (typeof (this.settings.allowApiUpdate) === "boolean")
|
|
1303
|
+
this.readonly = this.settings.allowApiUpdate;
|
|
1304
|
+
if (!this.inputType)
|
|
1305
|
+
this.inputType = this.valueConverter.inputType;
|
|
1306
|
+
this.caption = buildCaption(settings.caption, settings.key, remult);
|
|
1307
|
+
}
|
|
1308
|
+
apiUpdateAllowed(item) {
|
|
1309
|
+
if (this.options.allowApiUpdate === undefined)
|
|
1310
|
+
return true;
|
|
1311
|
+
return this.remult.isAllowedForInstance(item, this.options.allowApiUpdate);
|
|
1312
|
+
}
|
|
1313
|
+
displayValue(item) {
|
|
1314
|
+
return this.entityDefs.getEntityMetadataWithoutBreakingTheEntity(item).fields.find(this.key).displayValue;
|
|
1315
|
+
}
|
|
1316
|
+
get includedInApi() {
|
|
1317
|
+
if (this.options.includeInApi === undefined)
|
|
1318
|
+
return true;
|
|
1319
|
+
return this.remult.isAllowed(this.options.includeInApi); //TODO 2- consolidate other code paths to go through here
|
|
1320
|
+
}
|
|
1321
|
+
toInput(value, inputType) {
|
|
1322
|
+
return this.valueConverter.toInput(value, inputType);
|
|
1323
|
+
}
|
|
1324
|
+
fromInput(inputValue, inputType) {
|
|
1325
|
+
return this.valueConverter.fromInput(inputValue, inputType);
|
|
1326
|
+
}
|
|
1327
|
+
async getDbName() {
|
|
1328
|
+
try {
|
|
1329
|
+
if (this.settings.sqlExpression) {
|
|
1330
|
+
let result;
|
|
1331
|
+
if (typeof this.settings.sqlExpression === "function") {
|
|
1332
|
+
result = await this.settings.sqlExpression(this.entityDefs);
|
|
1333
|
+
}
|
|
1334
|
+
else
|
|
1335
|
+
result = this.settings.sqlExpression;
|
|
1336
|
+
if (!result)
|
|
1337
|
+
return this.settings.dbName;
|
|
1338
|
+
return result;
|
|
1339
|
+
}
|
|
1340
|
+
return this.settings.dbName;
|
|
1341
|
+
}
|
|
1342
|
+
finally {
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
options;
|
|
1346
|
+
target;
|
|
1347
|
+
readonly;
|
|
1348
|
+
valueConverter;
|
|
1349
|
+
allowNull;
|
|
1350
|
+
caption;
|
|
1351
|
+
get dbName() {
|
|
1352
|
+
let result;
|
|
1353
|
+
if (this.settings.sqlExpression) {
|
|
1354
|
+
if (typeof this.settings.sqlExpression === "function") {
|
|
1355
|
+
result = this.settings.sqlExpression(this.entityDefs);
|
|
1356
|
+
}
|
|
1357
|
+
else
|
|
1358
|
+
result = this.settings.sqlExpression;
|
|
1359
|
+
}
|
|
1360
|
+
if (result)
|
|
1361
|
+
return result;
|
|
1362
|
+
return this.settings.dbName;
|
|
1363
|
+
}
|
|
1364
|
+
inputType;
|
|
1365
|
+
key;
|
|
1366
|
+
get dbReadOnly() {
|
|
1367
|
+
return this.settings.dbReadOnly;
|
|
1368
|
+
}
|
|
1369
|
+
;
|
|
1370
|
+
isServerExpression;
|
|
1371
|
+
valueType;
|
|
1372
|
+
}
|
|
1373
|
+
class EntityFullInfo {
|
|
1374
|
+
columnsInfo;
|
|
1375
|
+
entityInfo;
|
|
1376
|
+
remult;
|
|
1377
|
+
entityType;
|
|
1378
|
+
key;
|
|
1379
|
+
options;
|
|
1380
|
+
constructor(columnsInfo, entityInfo, remult, entityType, key) {
|
|
1381
|
+
this.columnsInfo = columnsInfo;
|
|
1382
|
+
this.entityInfo = entityInfo;
|
|
1383
|
+
this.remult = remult;
|
|
1384
|
+
this.entityType = entityType;
|
|
1385
|
+
this.key = key;
|
|
1386
|
+
this.options = entityInfo;
|
|
1387
|
+
if (this.options.allowApiCrud !== undefined) {
|
|
1388
|
+
let crud;
|
|
1389
|
+
if ((typeof this.options.allowApiCrud) === "function")
|
|
1390
|
+
crud = (_, remult) => this.options.allowApiCrud(remult);
|
|
1391
|
+
else
|
|
1392
|
+
crud = this.options.allowApiCrud;
|
|
1393
|
+
if (this.options.allowApiDelete === undefined)
|
|
1394
|
+
this.options.allowApiDelete = crud;
|
|
1395
|
+
if (this.options.allowApiInsert === undefined)
|
|
1396
|
+
this.options.allowApiInsert = crud;
|
|
1397
|
+
if (this.options.allowApiUpdate === undefined)
|
|
1398
|
+
this.options.allowApiUpdate = crud;
|
|
1399
|
+
if (this.options.allowApiRead === undefined)
|
|
1400
|
+
this.options.allowApiRead = this.options.allowApiCrud;
|
|
1401
|
+
}
|
|
1402
|
+
if (this.options.allowApiRead === undefined)
|
|
1403
|
+
this.options.allowApiRead = true;
|
|
1404
|
+
if (!this.key)
|
|
1405
|
+
this.key = entityType.name;
|
|
1406
|
+
if (!entityInfo.dbName)
|
|
1407
|
+
entityInfo.dbName = this.key;
|
|
1408
|
+
let _items = [];
|
|
1409
|
+
let r = {
|
|
1410
|
+
find: (c) => r[typeof c === "string" ? c : c.key],
|
|
1411
|
+
[Symbol.iterator]: () => _items[Symbol.iterator](),
|
|
1412
|
+
toArray: () => _items
|
|
1413
|
+
};
|
|
1414
|
+
for (const x of columnsInfo) {
|
|
1415
|
+
_items.push(r[x.key] = new columnDefsImpl(x, this, remult));
|
|
1416
|
+
}
|
|
1417
|
+
this.fields = r;
|
|
1418
|
+
this.caption = buildCaption(entityInfo.caption, this.key, remult);
|
|
1419
|
+
if (entityInfo.id) {
|
|
1420
|
+
let r = entityInfo.id(this.fields);
|
|
1421
|
+
if (Array.isArray(r)) {
|
|
1422
|
+
if (r.length > 1)
|
|
1423
|
+
this.idMetadata.field = new CompoundIdField(...r);
|
|
1424
|
+
else if (r.length == 1)
|
|
1425
|
+
this.idMetadata.field = r[0];
|
|
1426
|
+
}
|
|
1427
|
+
else
|
|
1428
|
+
this.idMetadata.field = r;
|
|
1429
|
+
}
|
|
1430
|
+
else {
|
|
1431
|
+
if (this.fields["id"])
|
|
1432
|
+
this.idMetadata.field = this.fields["id"];
|
|
1433
|
+
else
|
|
1434
|
+
this.idMetadata.field = [...this.fields][0];
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
apiUpdateAllowed(item) {
|
|
1438
|
+
if (this.options.allowApiUpdate === undefined)
|
|
1439
|
+
return false;
|
|
1440
|
+
return !item ? this.remult.isAllowedForInstance(undefined, this.options.allowApiUpdate) : this.getEntityMetadataWithoutBreakingTheEntity(item).apiUpdateAllowed;
|
|
1441
|
+
}
|
|
1442
|
+
get apiReadAllowed() {
|
|
1443
|
+
if (this.options.allowApiRead === undefined)
|
|
1444
|
+
return true; //TODO2 - consider that this default may be confusing, since it's different from all others.
|
|
1445
|
+
return this.remult.isAllowed(this.options.allowApiRead);
|
|
1446
|
+
}
|
|
1447
|
+
apiDeleteAllowed(item) {
|
|
1448
|
+
if (this.options.allowApiDelete === undefined)
|
|
1449
|
+
return false;
|
|
1450
|
+
return !item ? this.remult.isAllowedForInstance(undefined, this.options.allowApiDelete) : this.getEntityMetadataWithoutBreakingTheEntity(item).apiDeleteAllowed;
|
|
1451
|
+
}
|
|
1452
|
+
apiInsertAllowed(item) {
|
|
1453
|
+
if (this.options.allowApiUpdate === undefined)
|
|
1454
|
+
return false;
|
|
1455
|
+
return !item ? this.remult.isAllowedForInstance(undefined, this.options.allowApiInsert) : this.getEntityMetadataWithoutBreakingTheEntity(item).apiInsertAllowed;
|
|
1456
|
+
}
|
|
1457
|
+
getEntityMetadataWithoutBreakingTheEntity(item) {
|
|
1458
|
+
let result = getEntityRef(item, false);
|
|
1459
|
+
if (result)
|
|
1460
|
+
return result;
|
|
1461
|
+
return this.remult.repo(this.entityType).getEntityRef({ ...item });
|
|
1462
|
+
}
|
|
1463
|
+
dbNamePromise;
|
|
1464
|
+
getDbName() {
|
|
1465
|
+
if (this.dbNamePromise)
|
|
1466
|
+
return this.dbNamePromise;
|
|
1467
|
+
if (!this.options.sqlExpression) {
|
|
1468
|
+
this.dbNamePromise = Promise.resolve(this.options.dbName);
|
|
1469
|
+
}
|
|
1470
|
+
if (typeof this.options.sqlExpression === "string")
|
|
1471
|
+
this.dbNamePromise = Promise.resolve(this.options.sqlExpression);
|
|
1472
|
+
else if (typeof this.options.sqlExpression === "function") {
|
|
1473
|
+
let r = this.options.sqlExpression(this);
|
|
1474
|
+
if (r instanceof Promise)
|
|
1475
|
+
this.dbNamePromise = r;
|
|
1476
|
+
else if (r)
|
|
1477
|
+
this.dbNamePromise = Promise.resolve(r);
|
|
1478
|
+
}
|
|
1479
|
+
this.dbNamePromise = this.dbNamePromise.then(x => {
|
|
1480
|
+
if (!x)
|
|
1481
|
+
return this.options.dbName;
|
|
1482
|
+
return x;
|
|
1483
|
+
});
|
|
1484
|
+
return this.dbNamePromise;
|
|
1485
|
+
}
|
|
1486
|
+
idMetadata = {
|
|
1487
|
+
getId: item => {
|
|
1488
|
+
if (this.idMetadata.field instanceof CompoundIdField)
|
|
1489
|
+
return this.idMetadata.field.getId(item);
|
|
1490
|
+
else
|
|
1491
|
+
return item[this.idMetadata.field.key];
|
|
1492
|
+
},
|
|
1493
|
+
field: undefined,
|
|
1494
|
+
createIdInFilter: (items) => {
|
|
1495
|
+
if (items.length > 0)
|
|
1496
|
+
return {
|
|
1497
|
+
$or: items.map(x => this.idMetadata.getIdFilter(getEntityRef(x).getId()))
|
|
1498
|
+
};
|
|
1499
|
+
},
|
|
1500
|
+
isIdField: (col) => {
|
|
1501
|
+
return col.key == this.idMetadata.field.key;
|
|
1502
|
+
},
|
|
1503
|
+
getIdFilter: (...ids) => {
|
|
1504
|
+
if (this.idMetadata.field instanceof CompoundIdField) {
|
|
1505
|
+
let field = this.idMetadata.field;
|
|
1506
|
+
return {
|
|
1507
|
+
$or: ids.map(x => field.isEqualTo(x))
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
if (ids.length == 1)
|
|
1511
|
+
return {
|
|
1512
|
+
[this.idMetadata.field.key]: ids[0]
|
|
1513
|
+
};
|
|
1514
|
+
else
|
|
1515
|
+
return {
|
|
1516
|
+
[this.idMetadata.field.key]: ids
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
};
|
|
1520
|
+
fields;
|
|
1521
|
+
dbName;
|
|
1522
|
+
caption;
|
|
1523
|
+
}
|
|
1524
|
+
export function FieldType(...options) {
|
|
1525
|
+
return (target, context) => {
|
|
1526
|
+
if (!options) {
|
|
1527
|
+
options = [];
|
|
1528
|
+
}
|
|
1529
|
+
options.splice(0, 0, { valueType: target });
|
|
1530
|
+
Reflect.defineMetadata(storableMember, options, target);
|
|
1531
|
+
return target;
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
export class Fields {
|
|
1535
|
+
/**
|
|
1536
|
+
* Stored as a JSON.stringify - to store as json use Fields.json
|
|
1537
|
+
*/
|
|
1538
|
+
static object(...options) {
|
|
1539
|
+
return Field(undefined, ...options);
|
|
1540
|
+
}
|
|
1541
|
+
static json(...options) {
|
|
1542
|
+
return Field(undefined, {
|
|
1543
|
+
valueConverter: {
|
|
1544
|
+
fieldTypeInDb: "json"
|
|
1545
|
+
}
|
|
1546
|
+
}, ...options);
|
|
1547
|
+
}
|
|
1548
|
+
static dateOnly(...options) {
|
|
1549
|
+
return Field(() => Date, {
|
|
1550
|
+
valueConverter: ValueConverters.DateOnly
|
|
1551
|
+
}, ...options);
|
|
1552
|
+
}
|
|
1553
|
+
static date(...options) {
|
|
1554
|
+
return Field(() => Date, ...options);
|
|
1555
|
+
}
|
|
1556
|
+
static integer(...options) {
|
|
1557
|
+
return Field(() => Number, {
|
|
1558
|
+
valueConverter: ValueConverters.Integer
|
|
1559
|
+
}, ...options);
|
|
1560
|
+
}
|
|
1561
|
+
static autoIncrement(...options) {
|
|
1562
|
+
return Field(() => Number, {
|
|
1563
|
+
allowApiUpdate: false,
|
|
1564
|
+
dbReadOnly: true,
|
|
1565
|
+
valueConverter: { ...ValueConverters.Integer, fieldTypeInDb: 'autoincrement' }
|
|
1566
|
+
}, ...options);
|
|
1567
|
+
}
|
|
1568
|
+
static number(...options) {
|
|
1569
|
+
return Field(() => Number, ...options);
|
|
1570
|
+
}
|
|
1571
|
+
static createdAt(...options) {
|
|
1572
|
+
return Field(() => Date, {
|
|
1573
|
+
allowApiUpdate: false,
|
|
1574
|
+
saving: (_, ref) => {
|
|
1575
|
+
if (getEntityRef(_).isNew())
|
|
1576
|
+
ref.value = new Date();
|
|
1577
|
+
}
|
|
1578
|
+
}, ...options);
|
|
1579
|
+
}
|
|
1580
|
+
static updatedAt(...options) {
|
|
1581
|
+
return Field(() => Date, {
|
|
1582
|
+
allowApiUpdate: false,
|
|
1583
|
+
saving: (_, ref) => {
|
|
1584
|
+
ref.value = new Date();
|
|
1585
|
+
}
|
|
1586
|
+
}, ...options);
|
|
1587
|
+
}
|
|
1588
|
+
static uuid(...options) {
|
|
1589
|
+
return Field(() => String, {
|
|
1590
|
+
allowApiUpdate: false,
|
|
1591
|
+
defaultValue: () => uuid(),
|
|
1592
|
+
saving: (_, r) => {
|
|
1593
|
+
if (!r.value)
|
|
1594
|
+
r.value = uuid();
|
|
1595
|
+
}
|
|
1596
|
+
}, ...options);
|
|
1597
|
+
}
|
|
1598
|
+
static cuid(...options) {
|
|
1599
|
+
return Field(() => String, {
|
|
1600
|
+
allowApiUpdate: false,
|
|
1601
|
+
defaultValue: () => createId(),
|
|
1602
|
+
saving: (_, r) => {
|
|
1603
|
+
if (!r.value)
|
|
1604
|
+
r.value = createId();
|
|
1605
|
+
}
|
|
1606
|
+
}, ...options);
|
|
1607
|
+
}
|
|
1608
|
+
static string(...options) {
|
|
1609
|
+
return Field(() => String, ...options);
|
|
1610
|
+
}
|
|
1611
|
+
static boolean(...options) {
|
|
1612
|
+
return Field(() => Boolean, ...options);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
export function isAutoIncrement(f) {
|
|
1616
|
+
return f.options?.valueConverter?.fieldTypeInDb === 'autoincrement';
|
|
1617
|
+
}
|
|
1618
|
+
export function ValueListFieldType(...options) {
|
|
1619
|
+
return (type, context) => {
|
|
1620
|
+
FieldType(o => {
|
|
1621
|
+
o.valueConverter = ValueListInfo.get(type),
|
|
1622
|
+
o.displayValue = (item, val) => val?.caption;
|
|
1623
|
+
}, ...options)(type, context);
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
export class ValueListInfo {
|
|
1627
|
+
valueListType;
|
|
1628
|
+
static get(type) {
|
|
1629
|
+
let r = typeCache.get(type);
|
|
1630
|
+
if (!r) {
|
|
1631
|
+
r = new ValueListInfo(type);
|
|
1632
|
+
typeCache.set(type, r);
|
|
1633
|
+
}
|
|
1634
|
+
return r;
|
|
1635
|
+
}
|
|
1636
|
+
byIdMap = new Map();
|
|
1637
|
+
values = [];
|
|
1638
|
+
isNumeric = false;
|
|
1639
|
+
constructor(valueListType) {
|
|
1640
|
+
this.valueListType = valueListType;
|
|
1641
|
+
for (let member in this.valueListType) {
|
|
1642
|
+
let s = this.valueListType[member];
|
|
1643
|
+
if (s instanceof this.valueListType) {
|
|
1644
|
+
if (s.id === undefined)
|
|
1645
|
+
s.id = member;
|
|
1646
|
+
if (typeof s.id === 'number')
|
|
1647
|
+
this.isNumeric = true;
|
|
1648
|
+
if (s.caption === undefined)
|
|
1649
|
+
s.caption = makeTitle(s.id !== undefined ? s.id.toString() : member);
|
|
1650
|
+
this.byIdMap.set(s.id, s);
|
|
1651
|
+
this.values.push(s);
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
if (this.isNumeric) {
|
|
1655
|
+
this.fieldTypeInDb = 'integer';
|
|
1656
|
+
}
|
|
1657
|
+
var options = Reflect.getMetadata(storableMember, this.valueListType);
|
|
1658
|
+
if (options) {
|
|
1659
|
+
for (const op of options) {
|
|
1660
|
+
if (op?.getValues) {
|
|
1661
|
+
this.values.splice(0, this.values.length, ...op.getValues());
|
|
1662
|
+
this.byIdMap.clear();
|
|
1663
|
+
this.values.forEach(s => {
|
|
1664
|
+
if (s.caption === undefined && s.id !== undefined)
|
|
1665
|
+
s.caption = makeTitle(s.id);
|
|
1666
|
+
this.byIdMap.set(s.id, s);
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
if (this.values.find(s => s.id === undefined))
|
|
1671
|
+
throw new Error(`ValueType ${this.valueListType} has values without an id`);
|
|
1672
|
+
}
|
|
1673
|
+
else
|
|
1674
|
+
throw new Error(`ValueType not yet initialized, did you forget to call @ValueListFieldType on ` + valueListType);
|
|
1675
|
+
}
|
|
1676
|
+
getValues() {
|
|
1677
|
+
return this.values;
|
|
1678
|
+
}
|
|
1679
|
+
byId(key) {
|
|
1680
|
+
if (this.isNumeric)
|
|
1681
|
+
key = +key;
|
|
1682
|
+
return this.byIdMap.get(key);
|
|
1683
|
+
}
|
|
1684
|
+
fromJson(val) {
|
|
1685
|
+
return this.byId(val);
|
|
1686
|
+
}
|
|
1687
|
+
toJson(val) {
|
|
1688
|
+
if (!val)
|
|
1689
|
+
return undefined;
|
|
1690
|
+
return val.id;
|
|
1691
|
+
}
|
|
1692
|
+
fromDb(val) {
|
|
1693
|
+
return this.fromJson(val);
|
|
1694
|
+
}
|
|
1695
|
+
toDb(val) {
|
|
1696
|
+
return this.toJson(val);
|
|
1697
|
+
}
|
|
1698
|
+
toInput(val, inputType) {
|
|
1699
|
+
return this.toJson(val);
|
|
1700
|
+
}
|
|
1701
|
+
fromInput(val, inputType) {
|
|
1702
|
+
return this.fromJson(val);
|
|
1703
|
+
}
|
|
1704
|
+
displayValue(val) {
|
|
1705
|
+
if (!val)
|
|
1706
|
+
return '';
|
|
1707
|
+
return val.caption;
|
|
1708
|
+
}
|
|
1709
|
+
fieldTypeInDb;
|
|
1710
|
+
inputType;
|
|
1711
|
+
}
|
|
1712
|
+
const typeCache = new Map();
|
|
1713
|
+
export function getValueList(type) {
|
|
1714
|
+
{
|
|
1715
|
+
const fr = type?.metadata?.valueType;
|
|
1716
|
+
if (fr)
|
|
1717
|
+
return ValueListInfo.get(fr).getValues();
|
|
1718
|
+
}
|
|
1719
|
+
{
|
|
1720
|
+
const fr = type?.valueType;
|
|
1721
|
+
if (fr)
|
|
1722
|
+
return ValueListInfo.get(fr).getValues();
|
|
1723
|
+
}
|
|
1724
|
+
return ValueListInfo.get(type).getValues();
|
|
1725
|
+
}
|
|
1726
|
+
/**Decorates fields that should be used as fields.
|
|
1727
|
+
* for more info see: [Field Types](https://remult.dev/docs/field-types.html)
|
|
1728
|
+
*
|
|
1729
|
+
* FieldOptions can be set in two ways:
|
|
1730
|
+
* @example
|
|
1731
|
+
* // as an object
|
|
1732
|
+
* @Fields.string({ includeInApi:false })
|
|
1733
|
+
* title='';
|
|
1734
|
+
* @example
|
|
1735
|
+
* // as an arrow function that receives `remult` as a parameter
|
|
1736
|
+
* @Fields.string((options,remult) => options.includeInApi = true)
|
|
1737
|
+
* title='';
|
|
1738
|
+
*/
|
|
1739
|
+
export function Field(valueType, ...options) {
|
|
1740
|
+
return (target,
|
|
1741
|
+
//@ts-ignore
|
|
1742
|
+
context, c) => {
|
|
1743
|
+
const key = typeof context === "string" ? context : context.name.toString();
|
|
1744
|
+
let factory = (remult) => {
|
|
1745
|
+
let r = buildOptions(options, remult);
|
|
1746
|
+
if (!r.valueType && valueType) {
|
|
1747
|
+
r.valueType = valueType();
|
|
1748
|
+
}
|
|
1749
|
+
if (!r.key) {
|
|
1750
|
+
r.key = key;
|
|
1751
|
+
}
|
|
1752
|
+
if (!r.dbName)
|
|
1753
|
+
r.dbName = r.key;
|
|
1754
|
+
let type = r.valueType;
|
|
1755
|
+
if (!type) {
|
|
1756
|
+
type = Reflect.getMetadata("design:type", target, key);
|
|
1757
|
+
r.valueType = type;
|
|
1758
|
+
}
|
|
1759
|
+
if (!r.target)
|
|
1760
|
+
r.target = target;
|
|
1761
|
+
return r;
|
|
1762
|
+
};
|
|
1763
|
+
let names = columnsOfType.get(target.constructor);
|
|
1764
|
+
if (!names) {
|
|
1765
|
+
names = [];
|
|
1766
|
+
columnsOfType.set(target.constructor, names);
|
|
1767
|
+
}
|
|
1768
|
+
let set = names.find(x => x.key == key);
|
|
1769
|
+
if (!set)
|
|
1770
|
+
names.push({
|
|
1771
|
+
key,
|
|
1772
|
+
settings: factory,
|
|
1773
|
+
});
|
|
1774
|
+
else {
|
|
1775
|
+
let prev = set.settings;
|
|
1776
|
+
set.settings = (c) => {
|
|
1777
|
+
let prevO = prev(c);
|
|
1778
|
+
let curr = factory(c);
|
|
1779
|
+
return Object.assign(prevO, curr);
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
};
|
|
1783
|
+
}
|
|
1784
|
+
export const storableMember = Symbol("storableMember");
|
|
1785
|
+
function buildOptions(options, remult) {
|
|
1786
|
+
let r = {};
|
|
1787
|
+
for (const o of options) {
|
|
1788
|
+
if (o) {
|
|
1789
|
+
if (typeof o === "function")
|
|
1790
|
+
o(r, remult);
|
|
1791
|
+
else
|
|
1792
|
+
Object.assign(r, o);
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
return r;
|
|
1796
|
+
}
|
|
1797
|
+
export function decorateColumnSettings(settings, remult) {
|
|
1798
|
+
if (settings.valueType) {
|
|
1799
|
+
let settingsOnTypeLevel = Reflect.getMetadata(storableMember, settings.valueType);
|
|
1800
|
+
if (settingsOnTypeLevel) {
|
|
1801
|
+
settings = {
|
|
1802
|
+
...buildOptions(settingsOnTypeLevel, remult),
|
|
1803
|
+
...settings
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
if (settings.valueType == String) {
|
|
1808
|
+
let x = settings;
|
|
1809
|
+
if (!settings.valueConverter)
|
|
1810
|
+
x.valueConverter = ValueConverters.String;
|
|
1811
|
+
}
|
|
1812
|
+
if (settings.valueType == Number) {
|
|
1813
|
+
let x = settings;
|
|
1814
|
+
if (!settings.valueConverter)
|
|
1815
|
+
x.valueConverter = ValueConverters.Number;
|
|
1816
|
+
}
|
|
1817
|
+
if (settings.valueType == Date) {
|
|
1818
|
+
let x = settings;
|
|
1819
|
+
if (!settings.valueConverter) {
|
|
1820
|
+
x.valueConverter = ValueConverters.Date;
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
if (settings.valueType == Boolean) {
|
|
1824
|
+
let x = settings;
|
|
1825
|
+
if (!x.valueConverter)
|
|
1826
|
+
x.valueConverter = ValueConverters.Boolean;
|
|
1827
|
+
}
|
|
1828
|
+
if (!settings.valueConverter) {
|
|
1829
|
+
let ei = getEntitySettings(settings.valueType, false);
|
|
1830
|
+
if (ei) {
|
|
1831
|
+
settings.valueConverter = {
|
|
1832
|
+
toDb: x => x,
|
|
1833
|
+
fromDb: x => x
|
|
1834
|
+
};
|
|
1835
|
+
}
|
|
1836
|
+
else
|
|
1837
|
+
settings.valueConverter = ValueConverters.Default;
|
|
1838
|
+
}
|
|
1839
|
+
if (!settings.valueConverter.toJson) {
|
|
1840
|
+
settings.valueConverter.toJson = x => x;
|
|
1841
|
+
}
|
|
1842
|
+
if (!settings.valueConverter.fromJson) {
|
|
1843
|
+
settings.valueConverter.fromJson = x => x;
|
|
1844
|
+
}
|
|
1845
|
+
if (!settings.valueConverter.toDb) {
|
|
1846
|
+
settings.valueConverter.toDb = x => settings.valueConverter.toJson(x);
|
|
1847
|
+
}
|
|
1848
|
+
if (!settings.valueConverter.fromDb) {
|
|
1849
|
+
settings.valueConverter.fromDb = x => settings.valueConverter.fromJson(x);
|
|
1850
|
+
}
|
|
1851
|
+
if (!settings.valueConverter.toInput) {
|
|
1852
|
+
settings.valueConverter.toInput = x => settings.valueConverter.toJson(x);
|
|
1853
|
+
}
|
|
1854
|
+
if (!settings.valueConverter.fromInput) {
|
|
1855
|
+
settings.valueConverter.fromInput = x => settings.valueConverter.fromJson(x);
|
|
1856
|
+
}
|
|
1857
|
+
return settings;
|
|
1858
|
+
}
|
|
1859
|
+
//[ ] YONI - in Typescript 5 I do get the name of the class, what would we like to change because of that?
|
|
1860
|
+
/**Decorates classes that should be used as entities.
|
|
1861
|
+
* Receives a key and an array of EntityOptions.
|
|
1862
|
+
* @example
|
|
1863
|
+
* import { Entity, Fields } from "remult";
|
|
1864
|
+
* @Entity("tasks", {
|
|
1865
|
+
* allowApiCrud: true
|
|
1866
|
+
* })
|
|
1867
|
+
* export class Task {
|
|
1868
|
+
* @Fields.uuid()
|
|
1869
|
+
* id!: string;
|
|
1870
|
+
* @Fields.string()
|
|
1871
|
+
* title = '';
|
|
1872
|
+
* @Fields.boolean()
|
|
1873
|
+
* completed = false;
|
|
1874
|
+
* }
|
|
1875
|
+
* @note
|
|
1876
|
+
* EntityOptions can be set in two ways:
|
|
1877
|
+
* @example
|
|
1878
|
+
* // as an object
|
|
1879
|
+
* @Entity("tasks",{ allowApiCrud:true })
|
|
1880
|
+
* @example
|
|
1881
|
+
* // as an arrow function that receives `remult` as a parameter
|
|
1882
|
+
* @Entity("tasks", (options,remult) => options.allowApiCrud = true)
|
|
1883
|
+
*/
|
|
1884
|
+
export function Entity(key, ...options) {
|
|
1885
|
+
return (target,
|
|
1886
|
+
//@ts-ignore
|
|
1887
|
+
info) => {
|
|
1888
|
+
for (const rawFilterMember in target) {
|
|
1889
|
+
if (Object.prototype.hasOwnProperty.call(target, rawFilterMember)) {
|
|
1890
|
+
const element = target[rawFilterMember];
|
|
1891
|
+
if (element?.rawFilterInfo?.rawFilterTranslator) {
|
|
1892
|
+
if (!element.rawFilterInfo.key)
|
|
1893
|
+
element.rawFilterInfo.key = rawFilterMember;
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
let factory = remult => {
|
|
1898
|
+
let r = {};
|
|
1899
|
+
for (const o of options) {
|
|
1900
|
+
if (o) {
|
|
1901
|
+
if (typeof o === "function")
|
|
1902
|
+
o(r, remult);
|
|
1903
|
+
else
|
|
1904
|
+
Object.assign(r, o);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
let base = Object.getPrototypeOf(target);
|
|
1908
|
+
if (base) {
|
|
1909
|
+
let baseFactory = getEntitySettings(base, false);
|
|
1910
|
+
if (baseFactory) {
|
|
1911
|
+
let opt = baseFactory(remult);
|
|
1912
|
+
if (opt) {
|
|
1913
|
+
r = {
|
|
1914
|
+
...opt,
|
|
1915
|
+
...r
|
|
1916
|
+
};
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
return r;
|
|
1921
|
+
};
|
|
1922
|
+
allEntities.push(target);
|
|
1923
|
+
setControllerSettings(target, { key });
|
|
1924
|
+
Reflect.defineMetadata(entityInfo, factory, target);
|
|
1925
|
+
Reflect.defineMetadata(entityInfo_key, key, target);
|
|
1926
|
+
return target;
|
|
1927
|
+
};
|
|
1928
|
+
}
|
|
1929
|
+
export class EntityBase {
|
|
1930
|
+
get _() { return getEntityRef(this); }
|
|
1931
|
+
save() { return this._.save(); }
|
|
1932
|
+
assign(values) {
|
|
1933
|
+
assign(this, values);
|
|
1934
|
+
return this;
|
|
1935
|
+
}
|
|
1936
|
+
delete() { return this._.delete(); }
|
|
1937
|
+
isNew() { return this._.isNew(); }
|
|
1938
|
+
get $() { return this._.fields; }
|
|
1939
|
+
}
|
|
1940
|
+
export class IdEntity extends EntityBase {
|
|
1941
|
+
@Fields.uuid()
|
|
1942
|
+
id;
|
|
1943
|
+
}
|
|
1944
|
+
export class ControllerBase {
|
|
1945
|
+
remult;
|
|
1946
|
+
constructor(remult) {
|
|
1947
|
+
this.remult = remult || defaultRemult;
|
|
1948
|
+
}
|
|
1949
|
+
assign(values) {
|
|
1950
|
+
assign(this, values);
|
|
1951
|
+
return this;
|
|
1952
|
+
}
|
|
1953
|
+
get $() { return getFields(this, this.remult); }
|
|
1954
|
+
get _() { return getControllerRef(this, this.remult); }
|
|
1955
|
+
}
|
|
1956
|
+
class QueryResultImpl {
|
|
1957
|
+
options;
|
|
1958
|
+
repo;
|
|
1959
|
+
constructor(options, repo) {
|
|
1960
|
+
this.options = options;
|
|
1961
|
+
this.repo = repo;
|
|
1962
|
+
if (!this.options)
|
|
1963
|
+
this.options = {};
|
|
1964
|
+
if (!this.options.pageSize) {
|
|
1965
|
+
this.options.pageSize = queryConfig.defaultPageSize;
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
_count = undefined;
|
|
1969
|
+
async getPage(page) {
|
|
1970
|
+
if (page < 1)
|
|
1971
|
+
page = 1;
|
|
1972
|
+
return this.repo.find({
|
|
1973
|
+
where: this.options.where,
|
|
1974
|
+
orderBy: this.options.orderBy,
|
|
1975
|
+
limit: this.options.pageSize,
|
|
1976
|
+
page: page,
|
|
1977
|
+
load: this.options.load
|
|
1978
|
+
});
|
|
1979
|
+
}
|
|
1980
|
+
async count() {
|
|
1981
|
+
if (this._count === undefined)
|
|
1982
|
+
this._count = await this.repo.count(this.options.where);
|
|
1983
|
+
return this._count;
|
|
1984
|
+
}
|
|
1985
|
+
async forEach(what) {
|
|
1986
|
+
let i = 0;
|
|
1987
|
+
for await (const x of this) {
|
|
1988
|
+
await what(x);
|
|
1989
|
+
i++;
|
|
1990
|
+
}
|
|
1991
|
+
return i;
|
|
1992
|
+
}
|
|
1993
|
+
async paginator(pNextPageFilter) {
|
|
1994
|
+
this.options.orderBy = Sort.createUniqueEntityOrderBy(this.repo.metadata, this.options.orderBy);
|
|
1995
|
+
let items = await this.repo.find({
|
|
1996
|
+
where: { $and: [this.options.where, pNextPageFilter] },
|
|
1997
|
+
orderBy: this.options.orderBy,
|
|
1998
|
+
limit: this.options.pageSize,
|
|
1999
|
+
load: this.options.load
|
|
2000
|
+
});
|
|
2001
|
+
let nextPage = undefined;
|
|
2002
|
+
let hasNextPage = items.length == this.options.pageSize;
|
|
2003
|
+
if (hasNextPage) {
|
|
2004
|
+
let nextPageFilter = await this.repo.createAfterFilter(this.options.orderBy, items[items.length - 1]);
|
|
2005
|
+
nextPage = () => this.paginator(nextPageFilter);
|
|
2006
|
+
}
|
|
2007
|
+
return {
|
|
2008
|
+
count: () => this.count(),
|
|
2009
|
+
hasNextPage,
|
|
2010
|
+
items,
|
|
2011
|
+
nextPage
|
|
2012
|
+
};
|
|
2013
|
+
}
|
|
2014
|
+
[Symbol.asyncIterator]() {
|
|
2015
|
+
if (!this.options.where) {
|
|
2016
|
+
this.options.where = {};
|
|
2017
|
+
}
|
|
2018
|
+
let ob = this.options.orderBy;
|
|
2019
|
+
this.options.orderBy = Sort.createUniqueEntityOrderBy(this.repo.metadata, ob);
|
|
2020
|
+
let itemIndex = -1;
|
|
2021
|
+
let currentPage = undefined;
|
|
2022
|
+
let itStrategy;
|
|
2023
|
+
let j = 0;
|
|
2024
|
+
itStrategy = async () => {
|
|
2025
|
+
if (this.options.progress) {
|
|
2026
|
+
this.options.progress.progress(j++ / await this.count());
|
|
2027
|
+
}
|
|
2028
|
+
if (currentPage === undefined || itemIndex == currentPage.items.length) {
|
|
2029
|
+
if (currentPage && !currentPage.hasNextPage)
|
|
2030
|
+
return { value: undefined, done: true };
|
|
2031
|
+
let prev = currentPage;
|
|
2032
|
+
if (currentPage)
|
|
2033
|
+
currentPage = await currentPage.nextPage();
|
|
2034
|
+
else
|
|
2035
|
+
currentPage = await this.paginator();
|
|
2036
|
+
itemIndex = 0;
|
|
2037
|
+
if (currentPage.items.length == 0) {
|
|
2038
|
+
return { value: undefined, done: true };
|
|
2039
|
+
}
|
|
2040
|
+
else {
|
|
2041
|
+
if (prev?.items.length > 0) {
|
|
2042
|
+
if (this.repo.getEntityRef(prev.items[0]).getId() == this.repo.getEntityRef(currentPage.items[0]).getId())
|
|
2043
|
+
throw new Error("pagination failure, returned same first row");
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
if (itemIndex < currentPage.items.length)
|
|
2048
|
+
return { value: currentPage.items[itemIndex++], done: false };
|
|
2049
|
+
};
|
|
2050
|
+
return {
|
|
2051
|
+
next: async () => {
|
|
2052
|
+
let r = itStrategy();
|
|
2053
|
+
return r;
|
|
2054
|
+
}
|
|
2055
|
+
};
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
class cacheEntityInfo {
|
|
2059
|
+
value = {};
|
|
2060
|
+
promise;
|
|
2061
|
+
}
|
|
2062
|
+
class SubscribableImp {
|
|
2063
|
+
reportChanged() {
|
|
2064
|
+
if (this._subscribers)
|
|
2065
|
+
this._subscribers.forEach(x => x.reportChanged());
|
|
2066
|
+
}
|
|
2067
|
+
reportObserved() {
|
|
2068
|
+
if (this._subscribers)
|
|
2069
|
+
this._subscribers.forEach(x => x.reportObserved());
|
|
2070
|
+
}
|
|
2071
|
+
_subscribers;
|
|
2072
|
+
subscribe(listener) {
|
|
2073
|
+
let list;
|
|
2074
|
+
if (typeof listener === "function")
|
|
2075
|
+
list = {
|
|
2076
|
+
reportChanged: () => listener(),
|
|
2077
|
+
reportObserved: () => { }
|
|
2078
|
+
};
|
|
2079
|
+
else
|
|
2080
|
+
list = listener;
|
|
2081
|
+
if (!this._subscribers) {
|
|
2082
|
+
this._subscribers = [];
|
|
2083
|
+
}
|
|
2084
|
+
this._subscribers.push(list);
|
|
2085
|
+
return () => this._subscribers = this._subscribers.filter(x => x != list);
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
export function getEntityMetadata(entity) {
|
|
2089
|
+
if (entity.metadata)
|
|
2090
|
+
return entity.metadata;
|
|
2091
|
+
const settings = getEntitySettings(entity, false);
|
|
2092
|
+
if (settings) {
|
|
2093
|
+
return defaultRemult.repo(entity).metadata;
|
|
2094
|
+
}
|
|
2095
|
+
return entity;
|
|
2096
|
+
}
|
|
2097
|
+
export function getRepository(entity) {
|
|
2098
|
+
const settings = getEntitySettings(entity, false);
|
|
2099
|
+
if (settings) {
|
|
2100
|
+
return defaultRemult.repo(entity);
|
|
2101
|
+
}
|
|
2102
|
+
return entity;
|
|
2103
|
+
}
|
|
2104
|
+
export function Field1(valueType) {
|
|
2105
|
+
return (target, context) => { };
|
|
2106
|
+
}
|
|
2107
|
+
export function createdAt() {
|
|
2108
|
+
return Field1(() => Date);
|
|
2109
|
+
}
|