remult 3.3.19-next.3 → 3.4.0-next.10
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 +4 -0
- package/SseSubscriptionServer.js +44 -2
- package/esm/SseSubscriptionServer.js +16 -1
- package/esm/internals.js +2 -0
- package/esm/live-query/data-provider-live-query-storage.js +3 -1
- package/esm/remult-knex/index.js +46 -73
- package/esm/remult-mongo.js +18 -12
- package/esm/server/remult-api-server.js +21 -15
- package/esm/src/data-api.js +61 -38
- package/esm/src/data-providers/sql-database.js +197 -134
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +35 -34
- package/esm/src/filter/walk-filter.js +29 -0
- package/esm/src/live-query/LiveQueryClient.js +192 -53
- package/esm/src/live-query/SseSubscriptionClient.js +130 -43
- package/esm/src/live-query/SubscriptionChannel.js +61 -3
- package/esm/src/live-query/SubscriptionServer.js +11 -3
- package/esm/src/remult3/Fields.js +12 -2
- package/esm/src/remult3/RepositoryImplementation.js +81 -21
- package/esm/src/remult3/remult3.js +6 -0
- package/esm/src/sort.js +0 -3
- package/esm/src/valueConverters.js +11 -0
- package/index.d.ts +1 -0
- package/internals.d.ts +4 -0
- package/internals.js +6 -1
- package/live-query/data-provider-live-query-storage.d.ts +4 -1
- package/live-query/data-provider-live-query-storage.js +18 -15
- package/package.json +1 -1
- package/remult-knex/index.d.ts +3 -2
- package/remult-knex/index.js +118 -164
- package/remult-mongo.js +23 -16
- package/server/remult-api-server.d.ts +12 -0
- package/server/remult-api-server.js +32 -48
- package/src/column-interfaces.d.ts +5 -1
- package/src/data-api.d.ts +6 -5
- package/src/data-api.js +153 -121
- package/src/data-providers/sql-database.d.ts +57 -5
- package/src/data-providers/sql-database.js +386 -255
- package/src/entity.d.ts +12 -0
- package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +8 -3
- package/src/filter/filter-consumer-bridge-to-sql-request.js +40 -47
- package/src/filter/walk-filter.d.ts +3 -0
- package/src/filter/walk-filter.js +56 -0
- package/src/live-query/LiveQueryClient.js +267 -99
- package/src/live-query/SseSubscriptionClient.js +175 -84
- package/src/live-query/SubscriptionChannel.d.ts +20 -0
- package/src/live-query/SubscriptionChannel.js +73 -10
- package/src/live-query/SubscriptionServer.d.ts +11 -2
- package/src/live-query/SubscriptionServer.js +20 -10
- package/src/remult3/Fields.js +6 -0
- package/src/remult3/RepositoryImplementation.d.ts +9 -2
- package/src/remult3/RepositoryImplementation.js +299 -232
- package/src/remult3/remult3.d.ts +8 -13
- package/src/remult3/remult3.js +6 -0
- package/src/remult3/repository-internals.d.ts +4 -2
- package/src/sort.js +0 -3
- package/src/sql-command.d.ts +5 -0
- package/src/valueConverters.d.ts +4 -1
- package/src/valueConverters.js +13 -0
package/esm/src/data-api.js
CHANGED
|
@@ -2,6 +2,7 @@ import { doTransaction } from './context.js';
|
|
|
2
2
|
import { EntityError } from './data-interfaces.js';
|
|
3
3
|
import { findOptionsToJson, liveQueryAction, } from './data-providers/rest-data-provider.js';
|
|
4
4
|
import { Filter, buildFilterFromRequestParameters, customUrlToken, } from './filter/filter-interfaces.js';
|
|
5
|
+
import { omitFilterField, walkFilter } from './filter/walk-filter.js';
|
|
5
6
|
import { getRelationFieldInfo } from './remult3/relationInfoMember.js';
|
|
6
7
|
import { GroupByCountMember, GroupByForApiKey, } from './remult3/remult3.js';
|
|
7
8
|
import { ForbiddenError } from './server-action.js';
|
|
@@ -12,7 +13,7 @@ export class DataApi {
|
|
|
12
13
|
this.repository = repository;
|
|
13
14
|
this.remult = remult;
|
|
14
15
|
}
|
|
15
|
-
httpGet(res, req, serializeContext) {
|
|
16
|
+
async httpGet(res, req, serializeContext) {
|
|
16
17
|
try {
|
|
17
18
|
const action = req?.get('__action');
|
|
18
19
|
if (action?.startsWith(liveQueryAction))
|
|
@@ -22,15 +23,12 @@ export class DataApi {
|
|
|
22
23
|
case 'count':
|
|
23
24
|
return this.count(res, req, undefined);
|
|
24
25
|
case 'groupBy':
|
|
25
|
-
return res.success(this.groupBy(req, undefined));
|
|
26
|
+
return res.success(await this.groupBy(req, undefined));
|
|
26
27
|
}
|
|
27
28
|
return this.getArray(res, req, undefined);
|
|
28
29
|
}
|
|
29
30
|
catch (err) {
|
|
30
|
-
|
|
31
|
-
res.forbidden(err);
|
|
32
|
-
else
|
|
33
|
-
res.error(err, this.repository.metadata);
|
|
31
|
+
this.handleError(res, err);
|
|
34
32
|
}
|
|
35
33
|
}
|
|
36
34
|
async httpPost(res, req, body, serializeContext) {
|
|
@@ -70,16 +68,13 @@ export class DataApi {
|
|
|
70
68
|
res.success('ok');
|
|
71
69
|
return;
|
|
72
70
|
case 'query':
|
|
73
|
-
return
|
|
71
|
+
return this.query(res, req, body);
|
|
74
72
|
default:
|
|
75
73
|
return res.created(await this.post(body, req));
|
|
76
74
|
}
|
|
77
75
|
}
|
|
78
76
|
catch (err) {
|
|
79
|
-
|
|
80
|
-
res.forbidden(err.message);
|
|
81
|
-
else
|
|
82
|
-
res.error(err, this.repository.metadata);
|
|
77
|
+
this.handleError(res, err);
|
|
83
78
|
}
|
|
84
79
|
}
|
|
85
80
|
async upsertMany(response, request, body) {
|
|
@@ -124,16 +119,13 @@ export class DataApi {
|
|
|
124
119
|
this.getArrayImpl(request, rest),
|
|
125
120
|
this.groupBy(aggregateRequest, { ...aggregate, where: body.where }),
|
|
126
121
|
]);
|
|
127
|
-
return {
|
|
122
|
+
return response.success({
|
|
128
123
|
items: r,
|
|
129
124
|
aggregates,
|
|
130
|
-
};
|
|
125
|
+
});
|
|
131
126
|
}
|
|
132
127
|
catch (err) {
|
|
133
|
-
|
|
134
|
-
response.forbidden();
|
|
135
|
-
else
|
|
136
|
-
response.error(err, this.repository.metadata);
|
|
128
|
+
this.handleError(response, err);
|
|
137
129
|
}
|
|
138
130
|
}
|
|
139
131
|
static defaultGetLimit = 0;
|
|
@@ -155,7 +147,7 @@ export class DataApi {
|
|
|
155
147
|
});
|
|
156
148
|
}
|
|
157
149
|
catch (err) {
|
|
158
|
-
|
|
150
|
+
this.handleError(response, err);
|
|
159
151
|
}
|
|
160
152
|
}
|
|
161
153
|
async deleteMany(response, request, body) {
|
|
@@ -175,7 +167,7 @@ export class DataApi {
|
|
|
175
167
|
});
|
|
176
168
|
}
|
|
177
169
|
catch (err) {
|
|
178
|
-
|
|
170
|
+
this.handleError(response, err);
|
|
179
171
|
}
|
|
180
172
|
}
|
|
181
173
|
async prepareWhereForManyOperation(body, request) {
|
|
@@ -196,7 +188,7 @@ export class DataApi {
|
|
|
196
188
|
for (const element of body?.orderBy) {
|
|
197
189
|
const direction = element.isDescending ? 'desc' : 'asc';
|
|
198
190
|
if (element.operation !== 'count' &&
|
|
199
|
-
!this.
|
|
191
|
+
!this.fieldIsInApi(element.field))
|
|
200
192
|
continue;
|
|
201
193
|
switch (element.operation) {
|
|
202
194
|
case undefined:
|
|
@@ -214,9 +206,9 @@ export class DataApi {
|
|
|
214
206
|
}
|
|
215
207
|
}
|
|
216
208
|
}
|
|
217
|
-
const group = body?.groupBy
|
|
218
|
-
const min = body?.min
|
|
219
|
-
const max = body?.max
|
|
209
|
+
const group = this.fieldsInApi(body?.groupBy);
|
|
210
|
+
const min = this.fieldsInApi(body?.min);
|
|
211
|
+
const max = this.fieldsInApi(body?.max);
|
|
220
212
|
let result = await this.repository.groupBy({
|
|
221
213
|
where: findOptions.where,
|
|
222
214
|
limit: findOptions.limit,
|
|
@@ -224,11 +216,11 @@ export class DataApi {
|
|
|
224
216
|
//@ts-expect-error internal key
|
|
225
217
|
[GroupByForApiKey]: true,
|
|
226
218
|
group,
|
|
227
|
-
sum: body?.sum
|
|
228
|
-
avg: body?.avg
|
|
219
|
+
sum: this.fieldsInApi(body?.sum),
|
|
220
|
+
avg: this.fieldsInApi(body?.avg),
|
|
229
221
|
min,
|
|
230
222
|
max,
|
|
231
|
-
distinctCount: body?.distinctCount
|
|
223
|
+
distinctCount: this.fieldsInApi(body?.distinctCount),
|
|
232
224
|
orderBy: orderBy,
|
|
233
225
|
});
|
|
234
226
|
if (group)
|
|
@@ -269,7 +261,7 @@ export class DataApi {
|
|
|
269
261
|
let sort = request.get('_sort');
|
|
270
262
|
if (sort != undefined) {
|
|
271
263
|
let dir = request.get('_order');
|
|
272
|
-
findOptions.orderBy = determineSort(sort, dir);
|
|
264
|
+
findOptions.orderBy = this.omitFieldsHiddenFromApi(determineSort(sort, dir));
|
|
273
265
|
}
|
|
274
266
|
let limit = +request.get('_limit');
|
|
275
267
|
if (!limit && DataApi.defaultGetLimit)
|
|
@@ -341,10 +333,7 @@ export class DataApi {
|
|
|
341
333
|
response.success(r);
|
|
342
334
|
}
|
|
343
335
|
catch (err) {
|
|
344
|
-
|
|
345
|
-
response.forbidden(err.message);
|
|
346
|
-
else
|
|
347
|
-
response.error(err, this.repository.metadata);
|
|
336
|
+
this.handleError(response, err);
|
|
348
337
|
}
|
|
349
338
|
}
|
|
350
339
|
async liveQuery(response, request, body, serializeContext, queryChannel) {
|
|
@@ -358,6 +347,7 @@ export class DataApi {
|
|
|
358
347
|
requestJson: await serializeContext(),
|
|
359
348
|
findOptionsJson: findOptionsToJson(r.findOptions, this.repository.metadata),
|
|
360
349
|
lastIds: r.r.map((y) => this.repository.metadata.idMetadata.getId(y)),
|
|
350
|
+
version: 0,
|
|
361
351
|
};
|
|
362
352
|
await this.remult.liveQueryStorage.add({
|
|
363
353
|
entityKey: this.repository.metadata.key,
|
|
@@ -367,10 +357,7 @@ export class DataApi {
|
|
|
367
357
|
response.success(r.r);
|
|
368
358
|
}
|
|
369
359
|
catch (err) {
|
|
370
|
-
|
|
371
|
-
response.forbidden();
|
|
372
|
-
else
|
|
373
|
-
response.error(err, this.repository.metadata);
|
|
360
|
+
this.handleError(response, err);
|
|
374
361
|
}
|
|
375
362
|
}
|
|
376
363
|
async buildWhere(request, body) {
|
|
@@ -395,6 +382,7 @@ export class DataApi {
|
|
|
395
382
|
return result;
|
|
396
383
|
},
|
|
397
384
|
});
|
|
385
|
+
f = this.omitFieldsHiddenFromApi(f);
|
|
398
386
|
if (this.repository.metadata.options.apiPreprocessFilter) {
|
|
399
387
|
f = await this.repository.metadata.options.apiPreprocessFilter(f, {
|
|
400
388
|
metadata: this.repository.metadata,
|
|
@@ -407,6 +395,30 @@ export class DataApi {
|
|
|
407
395
|
}
|
|
408
396
|
return { $and: where };
|
|
409
397
|
}
|
|
398
|
+
// A hidden field is not on the api, same as a field that is not on the entity.
|
|
399
|
+
fieldIsInApi(key) {
|
|
400
|
+
return this.repository.fields.find(key)?.includedInApi() === true;
|
|
401
|
+
}
|
|
402
|
+
fieldsInApi(keys) {
|
|
403
|
+
return keys?.filter((key) => this.fieldIsInApi(key));
|
|
404
|
+
}
|
|
405
|
+
// Drop a field the api hides. Same as a field that is not on the entity:
|
|
406
|
+
// refusing it would confirm the name exists.
|
|
407
|
+
omitFieldsHiddenFromApi(filterOrSort) {
|
|
408
|
+
const fields = this.repository.metadata.fields;
|
|
409
|
+
return walkFilter(filterOrSort, function (key, value) {
|
|
410
|
+
if (!key.startsWith(customUrlToken) &&
|
|
411
|
+
fields.find(key)?.includedInApi() === false)
|
|
412
|
+
return omitFilterField;
|
|
413
|
+
return value;
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
handleError(response, err) {
|
|
417
|
+
if (err.isForbiddenError)
|
|
418
|
+
response.forbidden(err.message);
|
|
419
|
+
else
|
|
420
|
+
response.error(err, this.repository.metadata);
|
|
421
|
+
}
|
|
410
422
|
async doOnId(response, id, what) {
|
|
411
423
|
try {
|
|
412
424
|
var where = [
|
|
@@ -466,7 +478,7 @@ export class DataApi {
|
|
|
466
478
|
});
|
|
467
479
|
}
|
|
468
480
|
catch (err) {
|
|
469
|
-
|
|
481
|
+
this.handleError(response, err);
|
|
470
482
|
}
|
|
471
483
|
}
|
|
472
484
|
async actualUpdate(row, body, req, doNotSelect) {
|
|
@@ -512,11 +524,22 @@ export class DataApi {
|
|
|
512
524
|
return this.repository.getEntityRef(newr).toApiJson();
|
|
513
525
|
};
|
|
514
526
|
if (Array.isArray(body)) {
|
|
515
|
-
|
|
527
|
+
let result = [];
|
|
516
528
|
await doTransaction(this.remult, async () => {
|
|
529
|
+
const items = [];
|
|
517
530
|
for (const item of body) {
|
|
518
|
-
|
|
531
|
+
let newr = this.repository.create();
|
|
532
|
+
await this.repository.getEntityRef(newr)._updateEntityBasedOnApi(item);
|
|
533
|
+
if (!this.repository.getEntityRef(newr).apiInsertAllowed) {
|
|
534
|
+
throw new ForbiddenError();
|
|
535
|
+
}
|
|
536
|
+
items.push(newr);
|
|
519
537
|
}
|
|
538
|
+
const inserted = await this.repository.insert(items, options);
|
|
539
|
+
if (options?.select === 'none')
|
|
540
|
+
result = items.map(() => undefined);
|
|
541
|
+
else
|
|
542
|
+
result = inserted.map((row) => this.repository.getEntityRef(row).toApiJson());
|
|
520
543
|
});
|
|
521
544
|
return result;
|
|
522
545
|
}
|