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.
Files changed (58) hide show
  1. package/SseSubscriptionServer.d.ts +4 -0
  2. package/SseSubscriptionServer.js +44 -2
  3. package/esm/SseSubscriptionServer.js +16 -1
  4. package/esm/internals.js +2 -0
  5. package/esm/live-query/data-provider-live-query-storage.js +3 -1
  6. package/esm/remult-knex/index.js +46 -73
  7. package/esm/remult-mongo.js +18 -12
  8. package/esm/server/remult-api-server.js +21 -15
  9. package/esm/src/data-api.js +61 -38
  10. package/esm/src/data-providers/sql-database.js +197 -134
  11. package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +35 -34
  12. package/esm/src/filter/walk-filter.js +29 -0
  13. package/esm/src/live-query/LiveQueryClient.js +192 -53
  14. package/esm/src/live-query/SseSubscriptionClient.js +130 -43
  15. package/esm/src/live-query/SubscriptionChannel.js +61 -3
  16. package/esm/src/live-query/SubscriptionServer.js +11 -3
  17. package/esm/src/remult3/Fields.js +12 -2
  18. package/esm/src/remult3/RepositoryImplementation.js +81 -21
  19. package/esm/src/remult3/remult3.js +6 -0
  20. package/esm/src/sort.js +0 -3
  21. package/esm/src/valueConverters.js +11 -0
  22. package/index.d.ts +1 -0
  23. package/internals.d.ts +4 -0
  24. package/internals.js +6 -1
  25. package/live-query/data-provider-live-query-storage.d.ts +4 -1
  26. package/live-query/data-provider-live-query-storage.js +18 -15
  27. package/package.json +1 -1
  28. package/remult-knex/index.d.ts +3 -2
  29. package/remult-knex/index.js +118 -164
  30. package/remult-mongo.js +23 -16
  31. package/server/remult-api-server.d.ts +12 -0
  32. package/server/remult-api-server.js +32 -48
  33. package/src/column-interfaces.d.ts +5 -1
  34. package/src/data-api.d.ts +6 -5
  35. package/src/data-api.js +153 -121
  36. package/src/data-providers/sql-database.d.ts +57 -5
  37. package/src/data-providers/sql-database.js +386 -255
  38. package/src/entity.d.ts +12 -0
  39. package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +8 -3
  40. package/src/filter/filter-consumer-bridge-to-sql-request.js +40 -47
  41. package/src/filter/walk-filter.d.ts +3 -0
  42. package/src/filter/walk-filter.js +56 -0
  43. package/src/live-query/LiveQueryClient.js +267 -99
  44. package/src/live-query/SseSubscriptionClient.js +175 -84
  45. package/src/live-query/SubscriptionChannel.d.ts +20 -0
  46. package/src/live-query/SubscriptionChannel.js +73 -10
  47. package/src/live-query/SubscriptionServer.d.ts +11 -2
  48. package/src/live-query/SubscriptionServer.js +20 -10
  49. package/src/remult3/Fields.js +6 -0
  50. package/src/remult3/RepositoryImplementation.d.ts +9 -2
  51. package/src/remult3/RepositoryImplementation.js +299 -232
  52. package/src/remult3/remult3.d.ts +8 -13
  53. package/src/remult3/remult3.js +6 -0
  54. package/src/remult3/repository-internals.d.ts +4 -2
  55. package/src/sort.js +0 -3
  56. package/src/sql-command.d.ts +5 -0
  57. package/src/valueConverters.d.ts +4 -1
  58. package/src/valueConverters.js +13 -0
@@ -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
- if (err.isForbiddenError)
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 res.success(await this.query(res, req, body));
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
- if (err.isForbiddenError)
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
- if (err.isForbiddenError)
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
- response.error(err, this.repository.metadata);
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
- response.error(err, this.repository.metadata);
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.repository.fields.find(element.field)?.includedInApi())
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?.filter((x) => this.repository.fields.find(x).includedInApi());
218
- const min = body?.min?.filter((x) => this.repository.fields.find(x).includedInApi());
219
- const max = body?.max?.filter((x) => this.repository.fields.find(x).includedInApi());
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?.filter((x) => this.repository.fields.find(x).includedInApi()),
228
- avg: body?.avg?.filter((x) => this.repository.fields.find(x).includedInApi()),
219
+ sum: this.fieldsInApi(body?.sum),
220
+ avg: this.fieldsInApi(body?.avg),
229
221
  min,
230
222
  max,
231
- distinctCount: body?.distinctCount?.filter((x) => this.repository.fields.find(x).includedInApi()),
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
- if (err.isForbiddenError)
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
- if (err.isForbiddenError)
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
- response.error(err, this.repository.metadata);
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
- const result = [];
527
+ let result = [];
516
528
  await doTransaction(this.remult, async () => {
529
+ const items = [];
517
530
  for (const item of body) {
518
- result.push(await insert(item));
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
  }