remult 0.17.3 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SseSubscriptionServer.d.ts +23 -0
- package/SseSubscriptionServer.js +143 -0
- package/ably.d.ts +13 -0
- package/ably.js +59 -0
- package/esm/SseSubscriptionServer.d.ts +23 -0
- package/esm/SseSubscriptionServer.js +102 -0
- package/esm/ably.d.ts +13 -0
- package/esm/ably.js +28 -0
- package/esm/index.d.ts +7 -4
- package/esm/index.js +4 -2
- package/esm/live-query/data-provider-live-query-storage.d.ts +23 -0
- package/esm/live-query/data-provider-live-query-storage.js +78 -0
- package/esm/myLog.d.ts +1 -0
- package/esm/myLog.js +5 -0
- package/esm/postgres/postgres-data-provider.d.ts +4 -2
- package/esm/postgres/postgres-data-provider.js +18 -6
- package/esm/postgres/schema-builder.d.ts +2 -1
- package/esm/postgres/schema-builder.js +49 -29
- package/esm/remult-knex/index.d.ts +10 -32
- package/esm/remult-knex/index.js +75 -51
- package/esm/remult-mongo.d.ts +9 -2
- package/esm/remult-mongo.js +24 -8
- package/esm/remult-next.d.ts +11 -0
- package/esm/remult-next.js +27 -0
- package/esm/server/core.d.ts +2 -0
- package/esm/server/core.js +2 -0
- package/esm/server/expressBridge.d.ts +19 -7
- package/esm/server/expressBridge.js +135 -31
- package/esm/server/index.d.ts +2 -1
- package/esm/server/index.js +2 -1
- package/esm/server/initDataProvider.d.ts +2 -0
- package/esm/server/initDataProvider.js +14 -0
- package/esm/src/buildRestDataProvider.d.ts +31 -0
- package/esm/src/buildRestDataProvider.js +110 -0
- package/esm/src/column-interfaces.d.ts +44 -8
- package/esm/src/column.d.ts +3 -3
- package/esm/src/context.d.ts +39 -36
- package/esm/src/context.js +87 -117
- package/esm/src/data-api.d.ts +9 -3
- package/esm/src/data-api.js +130 -63
- package/esm/src/data-interfaces.d.ts +5 -1
- package/esm/src/data-providers/array-entity-data-provider.d.ts +1 -1
- package/esm/src/data-providers/array-entity-data-provider.js +2 -17
- package/esm/src/data-providers/rest-data-provider.d.ts +4 -3
- package/esm/src/data-providers/rest-data-provider.js +64 -23
- package/esm/src/data-providers/sql-database.d.ts +7 -3
- package/esm/src/data-providers/sql-database.js +60 -17
- package/esm/src/data-providers/web-sql-data-provider.d.ts +3 -0
- package/esm/src/data-providers/web-sql-data-provider.js +23 -9
- package/esm/src/entity.d.ts +2 -2
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.d.ts +16 -16
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +29 -21
- package/esm/src/filter/filter-interfaces.d.ts +5 -5
- package/esm/src/filter/filter-interfaces.js +31 -26
- package/esm/src/live-query/LiveQueryClient.d.ts +1 -0
- package/esm/src/live-query/LiveQueryClient.js +184 -0
- package/esm/src/live-query/SseSubscriptionClient.d.ts +5 -0
- package/esm/src/live-query/SseSubscriptionClient.js +86 -0
- package/esm/src/live-query/SubscriptionChannel.d.ts +41 -0
- package/esm/src/live-query/SubscriptionChannel.js +121 -0
- package/esm/src/live-query/SubscriptionServer.d.ts +38 -0
- package/esm/src/live-query/SubscriptionServer.js +108 -0
- package/esm/src/remult-proxy.d.ts +1 -1
- package/esm/src/remult-proxy.js +52 -7
- package/esm/src/remult3/RepositoryImplementation.d.ts +14 -5
- package/esm/src/remult3/RepositoryImplementation.js +124 -44
- package/esm/src/remult3/getId.d.ts +2 -0
- package/esm/src/remult3/getId.js +7 -0
- package/esm/src/remult3/remult3.d.ts +67 -10
- package/esm/src/remult3/remult3.js +1 -0
- package/esm/src/server-action.d.ts +5 -1
- package/esm/src/server-action.js +11 -12
- package/esm/src/shared-tests/db-tests-setup.d.ts +12 -5
- package/esm/src/shared-tests/db-tests-setup.js +14 -6
- package/esm/src/shared-tests/db-tests.db-tests.js +71 -10
- package/esm/src/sort.d.ts +1 -0
- package/esm/src/sort.js +18 -0
- package/esm/src/sql-command.d.ts +5 -2
- package/esm/src/tests/entityForCustomFilter.d.ts +6 -6
- package/esm/src/tests/entityForCustomFilter.js +20 -21
- package/esm/src/tests/frontend-database-tests-setup.spec.js +3 -4
- package/esm/src/tests/testHelper.spec.d.ts +3 -1
- package/esm/src/tests/testHelper.spec.js +63 -60
- package/esm/src/valueConverters.js +15 -7
- package/index.d.ts +7 -4
- package/index.js +7 -3
- package/live-query/data-provider-live-query-storage.d.ts +23 -0
- package/live-query/data-provider-live-query-storage.js +236 -0
- package/myLog.d.ts +1 -0
- package/myLog.js +20 -0
- package/package.json +18 -1
- package/postgres/postgres-data-provider.d.ts +4 -2
- package/postgres/postgres-data-provider.js +54 -39
- package/postgres/schema-builder.d.ts +2 -1
- package/postgres/schema-builder.js +118 -70
- package/remult-express.js +1 -1
- package/remult-knex/index.d.ts +10 -32
- package/remult-knex/index.js +134 -87
- package/remult-mongo.d.ts +9 -2
- package/remult-mongo.js +39 -7
- package/remult-next.d.ts +11 -0
- package/remult-next.js +73 -0
- package/server/JsonEntityFileStorage.js +2 -2
- package/server/core.d.ts +2 -0
- package/server/core.js +5 -1
- package/server/expressBridge.d.ts +19 -7
- package/server/expressBridge.js +283 -69
- package/server/index.d.ts +2 -1
- package/server/index.js +3 -1
- package/server/initDataProvider.d.ts +2 -0
- package/server/initDataProvider.js +26 -0
- package/src/buildRestDataProvider.d.ts +31 -0
- package/src/buildRestDataProvider.js +162 -0
- package/src/column-interfaces.d.ts +44 -8
- package/src/column.d.ts +3 -3
- package/src/context.d.ts +39 -36
- package/src/context.js +171 -166
- package/src/data-api.d.ts +9 -3
- package/src/data-api.js +233 -88
- package/src/data-interfaces.d.ts +5 -1
- package/src/data-providers/array-entity-data-provider.d.ts +1 -1
- package/src/data-providers/array-entity-data-provider.js +2 -17
- package/src/data-providers/rest-data-provider.d.ts +4 -3
- package/src/data-providers/rest-data-provider.js +105 -39
- package/src/data-providers/sql-database.d.ts +7 -3
- package/src/data-providers/sql-database.js +89 -16
- package/src/data-providers/web-sql-data-provider.d.ts +3 -0
- package/src/data-providers/web-sql-data-provider.js +56 -11
- package/src/entity.d.ts +2 -2
- package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +16 -16
- package/src/filter/filter-consumer-bridge-to-sql-request.js +59 -50
- package/src/filter/filter-interfaces.d.ts +5 -5
- package/src/filter/filter-interfaces.js +31 -26
- package/src/live-query/LiveQueryClient.d.ts +1 -0
- package/src/live-query/LiveQueryClient.js +283 -0
- package/src/live-query/SseSubscriptionClient.d.ts +5 -0
- package/src/live-query/SseSubscriptionClient.js +150 -0
- package/src/live-query/SubscriptionChannel.d.ts +41 -0
- package/src/live-query/SubscriptionChannel.js +209 -0
- package/src/live-query/SubscriptionServer.d.ts +38 -0
- package/src/live-query/SubscriptionServer.js +258 -0
- package/src/remult-proxy.d.ts +1 -1
- package/src/remult-proxy.js +153 -8
- package/src/remult3/RepositoryImplementation.d.ts +14 -5
- package/src/remult3/RepositoryImplementation.js +200 -85
- package/src/remult3/getId.d.ts +2 -0
- package/src/remult3/getId.js +11 -0
- package/src/remult3/remult3.d.ts +67 -10
- package/src/remult3/remult3.js +1 -0
- package/src/server-action.d.ts +5 -1
- package/src/server-action.js +11 -11
- package/src/shared-tests/db-tests-setup.d.ts +12 -5
- package/src/shared-tests/db-tests-setup.js +17 -8
- package/src/shared-tests/db-tests.db-tests.js +140 -8
- package/src/sort.d.ts +1 -0
- package/src/sort.js +18 -0
- package/src/sql-command.d.ts +5 -2
- package/src/tests/entityForCustomFilter.d.ts +6 -6
- package/src/tests/entityForCustomFilter.js +30 -31
- package/src/tests/frontend-database-tests-setup.spec.js +5 -6
- package/src/tests/testHelper.spec.d.ts +3 -1
- package/src/tests/testHelper.spec.js +104 -100
- package/src/valueConverters.js +15 -7
package/esm/src/data-api.js
CHANGED
|
@@ -1,23 +1,38 @@
|
|
|
1
1
|
import { customUrlToken, buildFilterFromRequestParameters } from './filter/filter-interfaces';
|
|
2
|
+
import { doTransaction } from './context';
|
|
2
3
|
import { Filter } from './filter/filter-interfaces';
|
|
4
|
+
import { ForbiddenError } from './server-action';
|
|
5
|
+
import { getId } from './remult3/getId';
|
|
6
|
+
import { findOptionsToJson, liveQueryAction } from './data-providers/rest-data-provider';
|
|
3
7
|
export class DataApi {
|
|
4
8
|
constructor(repository, remult) {
|
|
5
9
|
this.repository = repository;
|
|
6
10
|
this.remult = remult;
|
|
7
11
|
}
|
|
8
|
-
httpGet(res, req) {
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
httpGet(res, req, serializeRequest) {
|
|
13
|
+
const action = req?.get("__action");
|
|
14
|
+
if (action?.startsWith(liveQueryAction))
|
|
15
|
+
return this.liveQuery(res, req, undefined, serializeRequest, getLiveQueryChannel(action.substring(liveQueryAction.length), this.remult.user?.id));
|
|
16
|
+
switch (action) {
|
|
17
|
+
case "get":
|
|
18
|
+
case "count":
|
|
19
|
+
return this.count(res, req, undefined);
|
|
11
20
|
}
|
|
12
|
-
|
|
13
|
-
return this.getArray(res, req);
|
|
21
|
+
return this.getArray(res, req, undefined);
|
|
14
22
|
}
|
|
15
|
-
httpPost(res, req, body) {
|
|
16
|
-
|
|
23
|
+
async httpPost(res, req, body, serializeRequest) {
|
|
24
|
+
const action = req?.get("__action");
|
|
25
|
+
if (action?.startsWith(liveQueryAction))
|
|
26
|
+
return this.liveQuery(res, req, undefined, serializeRequest, getLiveQueryChannel(action.substring(liveQueryAction.length), this.remult.user?.id));
|
|
27
|
+
switch (action) {
|
|
17
28
|
case "get":
|
|
18
29
|
return this.getArray(res, req, body);
|
|
19
30
|
case "count":
|
|
20
31
|
return this.count(res, req, body);
|
|
32
|
+
case "endLiveQuery":
|
|
33
|
+
await this.remult.liveQueryStorage.remove(getLiveQueryChannel(body.id, this.remult.user?.id));
|
|
34
|
+
res.success("ok");
|
|
35
|
+
return;
|
|
21
36
|
default:
|
|
22
37
|
return this.post(res, body);
|
|
23
38
|
}
|
|
@@ -41,61 +56,95 @@ export class DataApi {
|
|
|
41
56
|
response.error(err);
|
|
42
57
|
}
|
|
43
58
|
}
|
|
59
|
+
async getArrayImpl(response, request, filterBody) {
|
|
60
|
+
let findOptions = { load: () => [] };
|
|
61
|
+
findOptions.where = await this.buildWhere(request, filterBody);
|
|
62
|
+
if (request) {
|
|
63
|
+
let sort = request.get("_sort");
|
|
64
|
+
if (sort != undefined) {
|
|
65
|
+
let dir = request.get('_order');
|
|
66
|
+
findOptions.orderBy = determineSort(sort, dir);
|
|
67
|
+
}
|
|
68
|
+
let limit = +request.get("_limit");
|
|
69
|
+
if (!limit && DataApi.defaultGetLimit)
|
|
70
|
+
limit = DataApi.defaultGetLimit;
|
|
71
|
+
findOptions.limit = limit;
|
|
72
|
+
findOptions.page = +request.get("_page");
|
|
73
|
+
}
|
|
74
|
+
if (this.remult.isAllowed(this.repository.metadata.options.apiRequireId)) {
|
|
75
|
+
let hasId = false;
|
|
76
|
+
let w = await Filter.fromEntityFilter(this.repository.metadata, findOptions.where);
|
|
77
|
+
if (w) {
|
|
78
|
+
w.__applyToConsumer({
|
|
79
|
+
containsCaseInsensitive: () => { },
|
|
80
|
+
isDifferentFrom: () => { },
|
|
81
|
+
isEqualTo: (col, val) => {
|
|
82
|
+
if (this.repository.metadata.idMetadata.isIdField(col))
|
|
83
|
+
hasId = true;
|
|
84
|
+
},
|
|
85
|
+
custom: () => { },
|
|
86
|
+
databaseCustom: () => { },
|
|
87
|
+
isGreaterOrEqualTo: () => { },
|
|
88
|
+
isGreaterThan: () => { },
|
|
89
|
+
isIn: () => { },
|
|
90
|
+
isLessOrEqualTo: () => { },
|
|
91
|
+
isLessThan: () => { },
|
|
92
|
+
isNotNull: () => { },
|
|
93
|
+
isNull: () => { },
|
|
94
|
+
or: () => { }
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (!hasId) {
|
|
98
|
+
response.forbidden();
|
|
99
|
+
throw new ForbiddenError();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const r = await this.repository.find(findOptions)
|
|
103
|
+
.then(async (r) => {
|
|
104
|
+
return await Promise.all(r.map(async (y) => this.repository.getEntityRef(y).toApiJson()));
|
|
105
|
+
});
|
|
106
|
+
return { r, findOptions };
|
|
107
|
+
}
|
|
44
108
|
async getArray(response, request, filterBody) {
|
|
45
109
|
if (!this.repository.metadata.apiReadAllowed) {
|
|
46
110
|
response.forbidden();
|
|
47
111
|
return;
|
|
48
112
|
}
|
|
49
113
|
try {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
response.forbidden();
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
if (request) {
|
|
81
|
-
let sort = request.get("_sort");
|
|
82
|
-
if (sort != undefined) {
|
|
83
|
-
let dir = request.get('_order');
|
|
84
|
-
findOptions.orderBy = determineSort(sort, dir);
|
|
85
|
-
}
|
|
86
|
-
let limit = +request.get("_limit");
|
|
87
|
-
if (!limit && DataApi.defaultGetLimit)
|
|
88
|
-
limit = DataApi.defaultGetLimit;
|
|
89
|
-
findOptions.limit = limit;
|
|
90
|
-
findOptions.page = +request.get("_page");
|
|
91
|
-
}
|
|
92
|
-
await this.repository.find(findOptions)
|
|
93
|
-
.then(async (r) => {
|
|
94
|
-
response.success(await Promise.all(r.map(async (y) => this.repository.getEntityRef(y).toApiJson())));
|
|
114
|
+
const { r } = await this.getArrayImpl(response, request, filterBody);
|
|
115
|
+
response.success(r);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
if (err.isForbiddenError)
|
|
119
|
+
response.forbidden();
|
|
120
|
+
else
|
|
121
|
+
response.error(err);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async liveQuery(response, request, filterBody, serializeRequest, queryChannel) {
|
|
125
|
+
if (!this.repository.metadata.apiReadAllowed) {
|
|
126
|
+
response.forbidden();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
const r = await this.getArrayImpl(response, request, filterBody);
|
|
131
|
+
const data = {
|
|
132
|
+
requestJson: serializeRequest(),
|
|
133
|
+
findOptionsJson: findOptionsToJson(r.findOptions, this.repository.metadata),
|
|
134
|
+
lastIds: r.r.map(y => getId(this.repository.metadata, y))
|
|
135
|
+
};
|
|
136
|
+
await this.remult.liveQueryStorage.add({
|
|
137
|
+
entityKey: this.repository.metadata.key,
|
|
138
|
+
id: queryChannel,
|
|
139
|
+
data
|
|
95
140
|
});
|
|
141
|
+
response.success(r.r);
|
|
96
142
|
}
|
|
97
143
|
catch (err) {
|
|
98
|
-
|
|
144
|
+
if (err.isForbiddenError)
|
|
145
|
+
response.forbidden();
|
|
146
|
+
else
|
|
147
|
+
response.error(err);
|
|
99
148
|
}
|
|
100
149
|
}
|
|
101
150
|
async buildWhere(request, filterBody) {
|
|
@@ -146,8 +195,8 @@ export class DataApi {
|
|
|
146
195
|
response.forbidden();
|
|
147
196
|
return;
|
|
148
197
|
}
|
|
149
|
-
await
|
|
150
|
-
response.success(
|
|
198
|
+
await ref.save();
|
|
199
|
+
response.success(ref.toApiJson());
|
|
151
200
|
});
|
|
152
201
|
}
|
|
153
202
|
async delete(response, id) {
|
|
@@ -162,17 +211,32 @@ export class DataApi {
|
|
|
162
211
|
}
|
|
163
212
|
async post(response, body) {
|
|
164
213
|
try {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
214
|
+
const insert = async (what) => {
|
|
215
|
+
let newr = this.repository.create();
|
|
216
|
+
await this.repository.getEntityRef(newr)._updateEntityBasedOnApi(what);
|
|
217
|
+
if (!this.repository.getEntityRef(newr).apiInsertAllowed) {
|
|
218
|
+
throw new ForbiddenError();
|
|
219
|
+
}
|
|
220
|
+
await this.repository.getEntityRef(newr).save();
|
|
221
|
+
return this.repository.getEntityRef(newr).toApiJson();
|
|
222
|
+
};
|
|
223
|
+
if (Array.isArray(body)) {
|
|
224
|
+
const result = [];
|
|
225
|
+
await doTransaction(this.remult, async () => {
|
|
226
|
+
for (const item of body) {
|
|
227
|
+
result.push(await insert(item));
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
response.created(result);
|
|
170
231
|
}
|
|
171
|
-
|
|
172
|
-
|
|
232
|
+
else
|
|
233
|
+
response.created(await insert(body));
|
|
173
234
|
}
|
|
174
235
|
catch (err) {
|
|
175
|
-
|
|
236
|
+
if (err.isForbiddenError)
|
|
237
|
+
response.forbidden();
|
|
238
|
+
else
|
|
239
|
+
response.error(err);
|
|
176
240
|
}
|
|
177
241
|
}
|
|
178
242
|
}
|
|
@@ -202,3 +266,6 @@ export function serializeError(data) {
|
|
|
202
266
|
data = { message: x };
|
|
203
267
|
return data;
|
|
204
268
|
}
|
|
269
|
+
export function getLiveQueryChannel(queryId, userId) {
|
|
270
|
+
return `users:${userId}:queries:${queryId}`;
|
|
271
|
+
}
|
|
@@ -4,7 +4,11 @@ import { EntityMetadata, OmitEB } from './remult3';
|
|
|
4
4
|
export interface DataProvider {
|
|
5
5
|
getEntityDataProvider(entity: EntityMetadata): EntityDataProvider;
|
|
6
6
|
transaction(action: (dataProvider: DataProvider) => Promise<void>): Promise<void>;
|
|
7
|
-
|
|
7
|
+
supportsRawFilter?: boolean;
|
|
8
|
+
ensureSchema?(entities: EntityMetadata[]): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export interface Storage {
|
|
11
|
+
ensureSchema(): Promise<void>;
|
|
8
12
|
}
|
|
9
13
|
export interface EntityDataProvider {
|
|
10
14
|
count(where: Filter): Promise<number>;
|
|
@@ -4,7 +4,7 @@ import { EntityMetadata, EntityFilter } from '../remult3';
|
|
|
4
4
|
export declare class ArrayEntityDataProvider implements EntityDataProvider {
|
|
5
5
|
private entity;
|
|
6
6
|
private rows?;
|
|
7
|
-
static
|
|
7
|
+
static rawFilter(filter: CustomArrayFilter): EntityFilter<any>;
|
|
8
8
|
constructor(entity: EntityMetadata, rows?: any[]);
|
|
9
9
|
private verifyThatRowHasAllNotNullColumns;
|
|
10
10
|
count(where?: Filter): Promise<number>;
|
|
@@ -12,7 +12,7 @@ export class ArrayEntityDataProvider {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
static
|
|
15
|
+
static rawFilter(filter) {
|
|
16
16
|
return {
|
|
17
17
|
[customDatabaseFilterToken]: {
|
|
18
18
|
arrayFilter: filter
|
|
@@ -66,22 +66,7 @@ export class ArrayEntityDataProvider {
|
|
|
66
66
|
}
|
|
67
67
|
if (options.orderBy) {
|
|
68
68
|
rows = rows.sort((a, b) => {
|
|
69
|
-
|
|
70
|
-
for (let i = 0; i < options.orderBy.Segments.length; i++) {
|
|
71
|
-
let seg = options.orderBy.Segments[i];
|
|
72
|
-
let left = a[seg.field.key];
|
|
73
|
-
let right = b[seg.field.key];
|
|
74
|
-
if (left > right)
|
|
75
|
-
r = 1;
|
|
76
|
-
else if (left < right)
|
|
77
|
-
r = -1;
|
|
78
|
-
if (r != 0) {
|
|
79
|
-
if (seg.isDescending)
|
|
80
|
-
r *= -1;
|
|
81
|
-
return r;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return r;
|
|
69
|
+
return options.orderBy.compare(a, b);
|
|
85
70
|
});
|
|
86
71
|
}
|
|
87
72
|
rows = pageArray(rows, options);
|
|
@@ -6,9 +6,9 @@ import { ApiClient } from '../context';
|
|
|
6
6
|
export declare class RestDataProvider implements DataProvider {
|
|
7
7
|
private apiProvider;
|
|
8
8
|
constructor(apiProvider: () => ApiClient);
|
|
9
|
-
getEntityDataProvider(entity: EntityMetadata):
|
|
9
|
+
getEntityDataProvider(entity: EntityMetadata): RestEntityDataProvider;
|
|
10
10
|
transaction(action: (dataProvider: DataProvider) => Promise<void>): Promise<void>;
|
|
11
|
-
|
|
11
|
+
supportsRawFilter: boolean;
|
|
12
12
|
}
|
|
13
13
|
export declare class RestEntityDataProvider implements EntityDataProvider {
|
|
14
14
|
private url;
|
|
@@ -18,7 +18,7 @@ export declare class RestEntityDataProvider implements EntityDataProvider {
|
|
|
18
18
|
translateFromJson(row: any): {};
|
|
19
19
|
translateToJson(row: any): {};
|
|
20
20
|
count(where: Filter): Promise<number>;
|
|
21
|
-
find(options
|
|
21
|
+
find(options?: EntityDataProviderFindOptions): Promise<Array<any>>;
|
|
22
22
|
update(id: any, data: any): Promise<any>;
|
|
23
23
|
delete(id: any): Promise<void>;
|
|
24
24
|
insert(data: any): Promise<any>;
|
|
@@ -36,3 +36,4 @@ export declare class RestDataProviderHttpProviderUsingFetch implements RestDataP
|
|
|
36
36
|
}): Promise<any>;
|
|
37
37
|
}
|
|
38
38
|
export declare function addFilterToUrlAndReturnTrueIfSuccessful(filter: any, url: UrlBuilder): boolean;
|
|
39
|
+
export declare const liveQueryAction = "liveQuery-";
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { UrlBuilder } from '../../urlBuilder';
|
|
2
|
-
import { customUrlToken } from '../filter/filter-interfaces';
|
|
3
|
-
import { buildRestDataProvider, retry } from
|
|
2
|
+
import { customUrlToken, Filter } from '../filter/filter-interfaces';
|
|
3
|
+
import { buildRestDataProvider, retry } from "../buildRestDataProvider";
|
|
4
|
+
import { actionInfo } from '../server-action';
|
|
4
5
|
export class RestDataProvider {
|
|
5
6
|
constructor(apiProvider) {
|
|
6
7
|
this.apiProvider = apiProvider;
|
|
7
|
-
this.
|
|
8
|
+
this.supportsRawFilter = true;
|
|
8
9
|
}
|
|
9
10
|
getEntityDataProvider(entity) {
|
|
10
11
|
return new RestEntityDataProvider(() => {
|
|
@@ -20,6 +21,29 @@ export class RestDataProvider {
|
|
|
20
21
|
throw new Error("Method not implemented.");
|
|
21
22
|
}
|
|
22
23
|
}
|
|
24
|
+
//@internal
|
|
25
|
+
export function findOptionsToJson(options, meta) {
|
|
26
|
+
return {
|
|
27
|
+
limit: options.limit,
|
|
28
|
+
page: options.page,
|
|
29
|
+
where: Filter.entityFilterToJson(meta, options.where),
|
|
30
|
+
orderBy: options.orderBy
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//@internal
|
|
34
|
+
export function findOptionsFromJson(json, meta) {
|
|
35
|
+
let r = {};
|
|
36
|
+
for (const key of ["limit", "page", "where", "orderBy"]) {
|
|
37
|
+
if (json[key] !== undefined) {
|
|
38
|
+
if (key === "where") {
|
|
39
|
+
r[key] = Filter.entityFilterFromJson(meta, json.where);
|
|
40
|
+
}
|
|
41
|
+
else
|
|
42
|
+
r[key] = json[key];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return r;
|
|
46
|
+
}
|
|
23
47
|
export class RestEntityDataProvider {
|
|
24
48
|
constructor(url, http, entity) {
|
|
25
49
|
this.url = url;
|
|
@@ -41,20 +65,15 @@ export class RestEntityDataProvider {
|
|
|
41
65
|
return result;
|
|
42
66
|
}
|
|
43
67
|
async count(where) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
let filterObject;
|
|
47
|
-
if (where) {
|
|
48
|
-
filterObject = where.toJson();
|
|
49
|
-
if (addFilterToUrlAndReturnTrueIfSuccessful(filterObject, url))
|
|
50
|
-
filterObject = undefined;
|
|
51
|
-
}
|
|
52
|
-
if (filterObject)
|
|
53
|
-
return this.http().post(url.url, filterObject).then(r => +(r.count));
|
|
54
|
-
else
|
|
55
|
-
return this.http().get(url.url).then(r => +(r.count));
|
|
68
|
+
const { run } = this.buildFindRequest({ where });
|
|
69
|
+
return run("count").then(r => +r.count);
|
|
56
70
|
}
|
|
57
71
|
find(options) {
|
|
72
|
+
let { run } = this.buildFindRequest(options);
|
|
73
|
+
return run().then(x => x.map(y => this.translateFromJson(y)));
|
|
74
|
+
}
|
|
75
|
+
//@internal
|
|
76
|
+
buildFindRequest(options) {
|
|
58
77
|
let url = new UrlBuilder(this.url());
|
|
59
78
|
let filterObject;
|
|
60
79
|
if (options) {
|
|
@@ -82,13 +101,34 @@ export class RestEntityDataProvider {
|
|
|
82
101
|
if (options.page)
|
|
83
102
|
url.add('_page', options.page);
|
|
84
103
|
}
|
|
85
|
-
|
|
86
|
-
url.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
104
|
+
const run = (action) => {
|
|
105
|
+
let u = new UrlBuilder(url.url);
|
|
106
|
+
if (!action && filterObject) {
|
|
107
|
+
action = "get";
|
|
108
|
+
}
|
|
109
|
+
if (action)
|
|
110
|
+
u.add("__action", action);
|
|
111
|
+
if (filterObject) {
|
|
112
|
+
return this.http().post(u.url, filterObject);
|
|
113
|
+
}
|
|
114
|
+
else
|
|
115
|
+
return this.http().get(u.url);
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
createKey: () => JSON.stringify({ url, filterObject }),
|
|
119
|
+
run,
|
|
120
|
+
subscribe: async (queryId) => {
|
|
121
|
+
const result = await run(liveQueryAction + queryId);
|
|
122
|
+
return {
|
|
123
|
+
result,
|
|
124
|
+
unsubscribe: async () => {
|
|
125
|
+
return actionInfo.runActionWithoutBlockingUI(() => this.http().post(this.url() + "?__action=endLiveQuery", {
|
|
126
|
+
id: queryId
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
};
|
|
92
132
|
}
|
|
93
133
|
update(id, data) {
|
|
94
134
|
let result = {};
|
|
@@ -186,7 +226,7 @@ export function addFilterToUrlAndReturnTrueIfSuccessful(filter, url) {
|
|
|
186
226
|
if (Object.prototype.hasOwnProperty.call(filter, key)) {
|
|
187
227
|
const element = filter[key];
|
|
188
228
|
if (Array.isArray(element)) {
|
|
189
|
-
if (key.endsWith("
|
|
229
|
+
if (key.endsWith(".in"))
|
|
190
230
|
url.add(key, JSON.stringify(element));
|
|
191
231
|
else
|
|
192
232
|
element.forEach(e => url.add(key, e));
|
|
@@ -199,3 +239,4 @@ export function addFilterToUrlAndReturnTrueIfSuccessful(filter, url) {
|
|
|
199
239
|
}
|
|
200
240
|
return true;
|
|
201
241
|
}
|
|
242
|
+
export const liveQueryAction = "liveQuery-";
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { EntityDataProvider, DataProvider } from "../data-interfaces";
|
|
2
|
-
import { SqlCommand, SqlImplementation, SqlResult } from "../sql-command";
|
|
2
|
+
import { SqlCommand, SqlCommandWithParameters, SqlImplementation, SqlResult } from "../sql-command";
|
|
3
3
|
import { CustomSqlFilterBuilderFunction } from "../filter/filter-consumer-bridge-to-sql-request";
|
|
4
|
-
import { EntityMetadata, EntityFilter } from "../remult3";
|
|
4
|
+
import { EntityMetadata, EntityFilter, RepositoryOverloads } from "../remult3";
|
|
5
|
+
import { Remult } from "../context";
|
|
5
6
|
export declare class SqlDatabase implements DataProvider {
|
|
6
7
|
private sql;
|
|
8
|
+
static getDb(remult?: Remult): SqlDatabase;
|
|
7
9
|
createCommand(): SqlCommand;
|
|
8
10
|
execute(sql: string): Promise<SqlResult>;
|
|
11
|
+
ensureSchema(entities: EntityMetadata<any>[]): Promise<void>;
|
|
9
12
|
getEntityDataProvider(entity: EntityMetadata): EntityDataProvider;
|
|
10
13
|
transaction(action: (dataProvider: DataProvider) => Promise<void>): Promise<void>;
|
|
11
|
-
static
|
|
14
|
+
static rawFilter(build: CustomSqlFilterBuilderFunction): EntityFilter<any>;
|
|
15
|
+
static filterToRaw<entityType>(repo: RepositoryOverloads<entityType>, condition: EntityFilter<entityType>, sqlCommand?: SqlCommandWithParameters): Promise<string>;
|
|
12
16
|
static LogToConsole: boolean;
|
|
13
17
|
static durationThreshold: number;
|
|
14
18
|
constructor(sql: SqlImplementation);
|
|
@@ -1,23 +1,39 @@
|
|
|
1
1
|
import { CompoundIdField } from "../column";
|
|
2
|
-
import { FilterConsumerBridgeToSqlRequest,
|
|
2
|
+
import { dbNamesOf, FilterConsumerBridgeToSqlRequest, isDbReadonly } from "../filter/filter-consumer-bridge-to-sql-request";
|
|
3
3
|
import { customDatabaseFilterToken, Filter } from '../filter/filter-interfaces';
|
|
4
4
|
import { Sort } from '../sort';
|
|
5
|
+
import { getRepository } from "../remult3";
|
|
6
|
+
import { RemultProxy } from "../remult-proxy";
|
|
5
7
|
// @dynamic
|
|
6
8
|
export class SqlDatabase {
|
|
7
9
|
constructor(sql) {
|
|
8
10
|
this.sql = sql;
|
|
9
11
|
this.createdEntities = [];
|
|
10
12
|
}
|
|
13
|
+
static getDb(remult) {
|
|
14
|
+
const r = (remult || RemultProxy.defaultRemult).dataProvider;
|
|
15
|
+
if (!r.createCommand)
|
|
16
|
+
throw "the data provider is not an SqlDatabase";
|
|
17
|
+
return r;
|
|
18
|
+
}
|
|
11
19
|
createCommand() {
|
|
12
20
|
return new LogSQLCommand(this.sql.createCommand(), SqlDatabase.LogToConsole);
|
|
13
21
|
}
|
|
14
22
|
async execute(sql) {
|
|
15
23
|
return await this.createCommand().execute(sql);
|
|
16
24
|
}
|
|
25
|
+
/* @internal*/
|
|
26
|
+
_getSourceSql() {
|
|
27
|
+
return this.sql;
|
|
28
|
+
}
|
|
29
|
+
async ensureSchema(entities) {
|
|
30
|
+
if (this.sql.ensureSchema)
|
|
31
|
+
await this.sql.ensureSchema(entities);
|
|
32
|
+
}
|
|
17
33
|
getEntityDataProvider(entity) {
|
|
18
34
|
return new ActualSQLServerDataProvider(entity, this, async (dbName) => {
|
|
19
|
-
if (this.createdEntities.indexOf(dbName
|
|
20
|
-
this.createdEntities.push(dbName
|
|
35
|
+
if (this.createdEntities.indexOf(dbName.$entityName) < 0) {
|
|
36
|
+
this.createdEntities.push(dbName.$entityName);
|
|
21
37
|
await this.sql.entityIsUsedForTheFirstTime(entity);
|
|
22
38
|
}
|
|
23
39
|
}, this.sql);
|
|
@@ -48,13 +64,23 @@ export class SqlDatabase {
|
|
|
48
64
|
}
|
|
49
65
|
});
|
|
50
66
|
}
|
|
51
|
-
static
|
|
67
|
+
static rawFilter(build) {
|
|
52
68
|
return {
|
|
53
69
|
[customDatabaseFilterToken]: {
|
|
54
70
|
buildSql: build
|
|
55
71
|
}
|
|
56
72
|
};
|
|
57
73
|
}
|
|
74
|
+
static async filterToRaw(repo, condition, sqlCommand) {
|
|
75
|
+
if (!sqlCommand) {
|
|
76
|
+
sqlCommand = new myDummySQLCommand();
|
|
77
|
+
}
|
|
78
|
+
const r = getRepository(repo);
|
|
79
|
+
var b = new FilterConsumerBridgeToSqlRequest(sqlCommand, await dbNamesOf(r.metadata));
|
|
80
|
+
b._addWhere = false;
|
|
81
|
+
await (await (r.translateWhereToFilter(condition))).__applyToConsumer(b);
|
|
82
|
+
return await b.resolveWhere();
|
|
83
|
+
}
|
|
58
84
|
}
|
|
59
85
|
SqlDatabase.LogToConsole = false;
|
|
60
86
|
SqlDatabase.durationThreshold = 0;
|
|
@@ -76,7 +102,7 @@ class LogSQLCommand {
|
|
|
76
102
|
if (this.allQueries) {
|
|
77
103
|
var d = new Date().valueOf() - start.valueOf();
|
|
78
104
|
if (d > SqlDatabase.durationThreshold) {
|
|
79
|
-
console.
|
|
105
|
+
console.info({ query: sql, arguments: this.args, duration: d / 1000 });
|
|
80
106
|
}
|
|
81
107
|
}
|
|
82
108
|
return r;
|
|
@@ -95,13 +121,13 @@ class ActualSQLServerDataProvider {
|
|
|
95
121
|
this.strategy = strategy;
|
|
96
122
|
}
|
|
97
123
|
async init() {
|
|
98
|
-
let dbNameProvider = await
|
|
124
|
+
let dbNameProvider = await dbNamesOf(this.entity);
|
|
99
125
|
await this.iAmUsed(dbNameProvider);
|
|
100
126
|
return dbNameProvider;
|
|
101
127
|
}
|
|
102
128
|
async count(where) {
|
|
103
129
|
let e = await this.init();
|
|
104
|
-
let select = 'select count(*) count from ' + e
|
|
130
|
+
let select = 'select count(*) count from ' + e.$entityName;
|
|
105
131
|
let r = this.sql.createCommand();
|
|
106
132
|
if (where) {
|
|
107
133
|
let wc = new FilterConsumerBridgeToSqlRequest(r, e);
|
|
@@ -116,7 +142,7 @@ class ActualSQLServerDataProvider {
|
|
|
116
142
|
let e = await this.init();
|
|
117
143
|
let { colKeys, select } = this.buildSelect(e);
|
|
118
144
|
select = 'select ' + select;
|
|
119
|
-
select += '\n from ' + e
|
|
145
|
+
select += '\n from ' + e.$entityName;
|
|
120
146
|
let r = this.sql.createCommand();
|
|
121
147
|
if (options) {
|
|
122
148
|
if (options.where) {
|
|
@@ -147,7 +173,7 @@ class ActualSQLServerDataProvider {
|
|
|
147
173
|
}
|
|
148
174
|
else
|
|
149
175
|
select += ', ';
|
|
150
|
-
select += e
|
|
176
|
+
select += e.$dbNameOf(c.field);
|
|
151
177
|
if (c.isDescending)
|
|
152
178
|
select += ' desc';
|
|
153
179
|
}
|
|
@@ -189,7 +215,7 @@ class ActualSQLServerDataProvider {
|
|
|
189
215
|
else {
|
|
190
216
|
if (colKeys.length > 0)
|
|
191
217
|
select += ', ';
|
|
192
|
-
select += e
|
|
218
|
+
select += e.$dbNameOf(x);
|
|
193
219
|
colKeys.push(x);
|
|
194
220
|
}
|
|
195
221
|
}
|
|
@@ -200,12 +226,12 @@ class ActualSQLServerDataProvider {
|
|
|
200
226
|
let r = this.sql.createCommand();
|
|
201
227
|
let f = new FilterConsumerBridgeToSqlRequest(r, e);
|
|
202
228
|
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
|
|
203
|
-
let statement = 'update ' + e
|
|
229
|
+
let statement = 'update ' + e.$entityName + ' set ';
|
|
204
230
|
let added = false;
|
|
205
231
|
for (const x of this.entity.fields) {
|
|
206
232
|
if (x instanceof CompoundIdField) {
|
|
207
233
|
}
|
|
208
|
-
if (
|
|
234
|
+
if (isDbReadonly(x, e)) { }
|
|
209
235
|
else if (data[x.key] !== undefined) {
|
|
210
236
|
let v = x.valueConverter.toDb(data[x.key]);
|
|
211
237
|
if (v !== undefined) {
|
|
@@ -213,7 +239,7 @@ class ActualSQLServerDataProvider {
|
|
|
213
239
|
added = true;
|
|
214
240
|
else
|
|
215
241
|
statement += ', ';
|
|
216
|
-
statement += e
|
|
242
|
+
statement += e.$dbNameOf(x) + ' = ' + r.addParameterAndReturnSqlToken(v);
|
|
217
243
|
}
|
|
218
244
|
}
|
|
219
245
|
}
|
|
@@ -229,7 +255,7 @@ class ActualSQLServerDataProvider {
|
|
|
229
255
|
let r = this.sql.createCommand();
|
|
230
256
|
let f = new FilterConsumerBridgeToSqlRequest(r, e);
|
|
231
257
|
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
|
|
232
|
-
let statement = 'delete from ' + e
|
|
258
|
+
let statement = 'delete from ' + e.$entityName;
|
|
233
259
|
statement += await f.resolveWhere();
|
|
234
260
|
return r.execute(statement).then(() => { });
|
|
235
261
|
}
|
|
@@ -240,7 +266,7 @@ class ActualSQLServerDataProvider {
|
|
|
240
266
|
let vals = '';
|
|
241
267
|
let added = false;
|
|
242
268
|
for (const x of this.entity.fields) {
|
|
243
|
-
if (
|
|
269
|
+
if (isDbReadonly(x, e)) { }
|
|
244
270
|
else {
|
|
245
271
|
let v = x.valueConverter.toDb(data[x.key]);
|
|
246
272
|
if (v != undefined) {
|
|
@@ -250,15 +276,32 @@ class ActualSQLServerDataProvider {
|
|
|
250
276
|
cols += ', ';
|
|
251
277
|
vals += ', ';
|
|
252
278
|
}
|
|
253
|
-
cols += e
|
|
279
|
+
cols += e.$dbNameOf(x);
|
|
254
280
|
vals += r.addParameterAndReturnSqlToken(v);
|
|
255
281
|
}
|
|
256
282
|
}
|
|
257
283
|
}
|
|
258
|
-
let statement = `insert into ${e
|
|
284
|
+
let statement = `insert into ${e.$entityName} (${cols}) values (${vals})`;
|
|
259
285
|
let { colKeys, select } = this.buildSelect(e);
|
|
260
286
|
statement += ' returning ' + select;
|
|
261
287
|
return await r.execute(statement).then(sql => this.buildResultRow(colKeys, sql.rows[0], sql));
|
|
262
288
|
}
|
|
263
289
|
}
|
|
264
290
|
ActualSQLServerDataProvider.LogToConsole = false;
|
|
291
|
+
class myDummySQLCommand {
|
|
292
|
+
execute(sql) {
|
|
293
|
+
throw new Error("Method not implemented.");
|
|
294
|
+
}
|
|
295
|
+
addParameterAndReturnSqlToken(val) {
|
|
296
|
+
if (val === null)
|
|
297
|
+
return "null";
|
|
298
|
+
if (val instanceof Date)
|
|
299
|
+
val = val.toISOString();
|
|
300
|
+
if (typeof (val) == "string") {
|
|
301
|
+
if (val == undefined)
|
|
302
|
+
val = '';
|
|
303
|
+
return '\'' + val.replace(/'/g, '\'\'') + '\'';
|
|
304
|
+
}
|
|
305
|
+
return val.toString();
|
|
306
|
+
}
|
|
307
|
+
}
|