remult 3.4.0-next.9 → 3.4.1

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/src/data-api.d.ts CHANGED
@@ -24,6 +24,10 @@ export declare class DataApi<T = unknown> {
24
24
  getArray(response: DataApiResponse, request: DataApiRequest, body?: any): Promise<void>;
25
25
  liveQuery(response: DataApiResponse, request: DataApiRequest, body: any, serializeContext: () => Promise<any>, queryChannel: string): Promise<void>;
26
26
  private buildWhere;
27
+ private fieldIsInApi;
28
+ private fieldsInApi;
29
+ private omitFieldsHiddenFromApi;
30
+ private handleError;
27
31
  private doOnId;
28
32
  updateManyThroughPutRequest(response: DataApiResponse, request: DataApiRequest, body: any): Promise<void>;
29
33
  updateManyImplementation(response: DataApiResponse, request: DataApiRequest, body: {
package/src/data-api.js CHANGED
@@ -8,6 +8,7 @@ var context_js_1 = require("./context.js");
8
8
  var data_interfaces_js_1 = require("./data-interfaces.js");
9
9
  var rest_data_provider_js_1 = require("./data-providers/rest-data-provider.js");
10
10
  var filter_interfaces_js_1 = require("./filter/filter-interfaces.js");
11
+ var walk_filter_js_1 = require("./filter/walk-filter.js");
11
12
  var relationInfoMember_js_1 = require("./remult3/relationInfoMember.js");
12
13
  var remult3_js_1 = require("./remult3/remult3.js");
13
14
  var server_action_js_1 = require("./server-action.js");
@@ -41,10 +42,7 @@ var DataApi = /** @class */ (function () {
41
42
  case 4: return [2 /*return*/, this.getArray(res, req, undefined)];
42
43
  case 5:
43
44
  err_1 = _d.sent();
44
- if (err_1.isForbiddenError)
45
- res.forbidden(err_1.message);
46
- else
47
- res.error(err_1, this.repository.metadata);
45
+ this.handleError(res, err_1);
48
46
  return [3 /*break*/, 6];
49
47
  case 6: return [2 /*return*/];
50
48
  }
@@ -115,10 +113,7 @@ var DataApi = /** @class */ (function () {
115
113
  case 14: return [3 /*break*/, 16];
116
114
  case 15:
117
115
  err_2 = _f.sent();
118
- if (err_2.isForbiddenError)
119
- res.forbidden(err_2.message);
120
- else
121
- res.error(err_2, this.repository.metadata);
116
+ this.handleError(res, err_2);
122
117
  return [3 /*break*/, 16];
123
118
  case 16: return [2 /*return*/];
124
119
  }
@@ -232,10 +227,7 @@ var DataApi = /** @class */ (function () {
232
227
  })];
233
228
  case 3:
234
229
  err_3 = _c.sent();
235
- if (err_3.isForbiddenError)
236
- response.forbidden();
237
- else
238
- response.error(err_3, this.repository.metadata);
230
+ this.handleError(response, err_3);
239
231
  return [3 /*break*/, 4];
240
232
  case 4: return [2 /*return*/];
241
233
  }
@@ -287,7 +279,7 @@ var DataApi = /** @class */ (function () {
287
279
  return [3 /*break*/, 5];
288
280
  case 4:
289
281
  err_4 = _f.sent();
290
- response.error(err_4, this.repository.metadata);
282
+ this.handleError(response, err_4);
291
283
  return [3 /*break*/, 5];
292
284
  case 5: return [2 /*return*/];
293
285
  }
@@ -359,7 +351,7 @@ var DataApi = /** @class */ (function () {
359
351
  case 2: return [2 /*return*/, _a.sent()];
360
352
  case 3:
361
353
  err_5 = _a.sent();
362
- response.error(err_5, this.repository.metadata);
354
+ this.handleError(response, err_5);
363
355
  return [3 /*break*/, 4];
364
356
  case 4: return [2 /*return*/];
365
357
  }
@@ -389,16 +381,15 @@ var DataApi = /** @class */ (function () {
389
381
  var findOptions, orderBy, _a, _b, element, direction, group, min, max, result, minMax, _loop_1, _c, _d, element;
390
382
  var e_3, _e, _f, _g, e_4, _h;
391
383
  var _this = this;
392
- var _j, _k, _l, _m, _o, _p, _q;
393
- return tslib_1.__generator(this, function (_r) {
394
- switch (_r.label) {
384
+ return tslib_1.__generator(this, function (_j) {
385
+ switch (_j.label) {
395
386
  case 0:
396
387
  if (!this.repository.metadata.apiReadAllowed) {
397
388
  throw new server_action_js_1.ForbiddenError();
398
389
  }
399
390
  return [4 /*yield*/, this.findOptionsFromRequest(request, body)];
400
391
  case 1:
401
- findOptions = _r.sent();
392
+ findOptions = _j.sent();
402
393
  orderBy = {};
403
394
  if (body === null || body === void 0 ? void 0 : body.orderBy) {
404
395
  try {
@@ -406,7 +397,7 @@ var DataApi = /** @class */ (function () {
406
397
  element = _b.value;
407
398
  direction = element.isDescending ? 'desc' : 'asc';
408
399
  if (element.operation !== 'count' &&
409
- !((_j = this.repository.fields.find(element.field)) === null || _j === void 0 ? void 0 : _j.includedInApi()))
400
+ !this.fieldIsInApi(element.field))
410
401
  continue;
411
402
  switch (element.operation) {
412
403
  case undefined:
@@ -429,15 +420,9 @@ var DataApi = /** @class */ (function () {
429
420
  finally { if (e_3) throw e_3.error; }
430
421
  }
431
422
  }
432
- group = (_k = body === null || body === void 0 ? void 0 : body.groupBy) === null || _k === void 0 ? void 0 : _k.filter(function (x) {
433
- return _this.repository.fields.find(x).includedInApi();
434
- });
435
- min = (_l = body === null || body === void 0 ? void 0 : body.min) === null || _l === void 0 ? void 0 : _l.filter(function (x) {
436
- return _this.repository.fields.find(x).includedInApi();
437
- });
438
- max = (_m = body === null || body === void 0 ? void 0 : body.max) === null || _m === void 0 ? void 0 : _m.filter(function (x) {
439
- return _this.repository.fields.find(x).includedInApi();
440
- });
423
+ group = this.fieldsInApi(body === null || body === void 0 ? void 0 : body.groupBy);
424
+ min = this.fieldsInApi(body === null || body === void 0 ? void 0 : body.min);
425
+ max = this.fieldsInApi(body === null || body === void 0 ? void 0 : body.max);
441
426
  return [4 /*yield*/, this.repository.groupBy((_g = {
442
427
  where: findOptions.where,
443
428
  limit: findOptions.limit,
@@ -446,21 +431,15 @@ var DataApi = /** @class */ (function () {
446
431
  //@ts-expect-error internal key
447
432
  _g[remult3_js_1.GroupByForApiKey] = true,
448
433
  _g.group = group,
449
- _g.sum = (_o = body === null || body === void 0 ? void 0 : body.sum) === null || _o === void 0 ? void 0 : _o.filter(function (x) {
450
- return _this.repository.fields.find(x).includedInApi();
451
- }),
452
- _g.avg = (_p = body === null || body === void 0 ? void 0 : body.avg) === null || _p === void 0 ? void 0 : _p.filter(function (x) {
453
- return _this.repository.fields.find(x).includedInApi();
454
- }),
434
+ _g.sum = this.fieldsInApi(body === null || body === void 0 ? void 0 : body.sum),
435
+ _g.avg = this.fieldsInApi(body === null || body === void 0 ? void 0 : body.avg),
455
436
  _g.min = min,
456
437
  _g.max = max,
457
- _g.distinctCount = (_q = body === null || body === void 0 ? void 0 : body.distinctCount) === null || _q === void 0 ? void 0 : _q.filter(function (x) {
458
- return _this.repository.fields.find(x).includedInApi();
459
- }),
438
+ _g.distinctCount = this.fieldsInApi(body === null || body === void 0 ? void 0 : body.distinctCount),
460
439
  _g.orderBy = orderBy,
461
440
  _g))];
462
441
  case 2:
463
- result = _r.sent();
442
+ result = _j.sent();
464
443
  if (group)
465
444
  result.forEach(function (x) {
466
445
  var e_5, _a;
@@ -566,7 +545,7 @@ var DataApi = /** @class */ (function () {
566
545
  sort = request.get('_sort');
567
546
  if (sort != undefined) {
568
547
  dir = request.get('_order');
569
- findOptions.orderBy = determineSort(sort, dir);
548
+ findOptions.orderBy = this.omitFieldsHiddenFromApi(determineSort(sort, dir));
570
549
  }
571
550
  limit = +request.get('_limit');
572
551
  if (!limit && DataApi.defaultGetLimit)
@@ -664,10 +643,7 @@ var DataApi = /** @class */ (function () {
664
643
  return [3 /*break*/, 4];
665
644
  case 3:
666
645
  err_6 = _a.sent();
667
- if (err_6.isForbiddenError)
668
- response.forbidden(err_6.message);
669
- else
670
- response.error(err_6, this.repository.metadata);
646
+ this.handleError(response, err_6);
671
647
  return [3 /*break*/, 4];
672
648
  case 4: return [2 /*return*/];
673
649
  }
@@ -711,10 +687,7 @@ var DataApi = /** @class */ (function () {
711
687
  return [3 /*break*/, 6];
712
688
  case 5:
713
689
  err_7 = _b.sent();
714
- if (err_7.isForbiddenError)
715
- response.forbidden();
716
- else
717
- response.error(err_7, this.repository.metadata);
690
+ this.handleError(response, err_7);
718
691
  return [3 /*break*/, 6];
719
692
  case 6: return [2 /*return*/];
720
693
  }
@@ -755,6 +728,7 @@ var DataApi = /** @class */ (function () {
755
728
  return result;
756
729
  },
757
730
  });
731
+ f_1 = this.omitFieldsHiddenFromApi(f_1);
758
732
  if (!this.repository.metadata.options.apiPreprocessFilter) return [3 /*break*/, 5];
759
733
  return [4 /*yield*/, this.repository.metadata.options.apiPreprocessFilter(f_1, {
760
734
  metadata: this.repository.metadata,
@@ -775,6 +749,35 @@ var DataApi = /** @class */ (function () {
775
749
  });
776
750
  });
777
751
  };
752
+ // A hidden field is not on the api, same as a field that is not on the entity.
753
+ DataApi.prototype.fieldIsInApi = function (key) {
754
+ var _a;
755
+ return ((_a = this.repository.fields.find(key)) === null || _a === void 0 ? void 0 : _a.includedInApi()) === true;
756
+ };
757
+ DataApi.prototype.fieldsInApi = function (keys) {
758
+ var _this = this;
759
+ return keys === null || keys === void 0 ? void 0 : keys.filter(function (key) {
760
+ return _this.fieldIsInApi(key);
761
+ });
762
+ };
763
+ // Drop a field the api hides. Same as a field that is not on the entity:
764
+ // refusing it would confirm the name exists.
765
+ DataApi.prototype.omitFieldsHiddenFromApi = function (filterOrSort) {
766
+ var fields = this.repository.metadata.fields;
767
+ return (0, walk_filter_js_1.walkFilter)(filterOrSort, function (key, value) {
768
+ var _a;
769
+ if (!key.startsWith(filter_interfaces_js_1.customUrlToken) &&
770
+ ((_a = fields.find(key)) === null || _a === void 0 ? void 0 : _a.includedInApi()) === false)
771
+ return walk_filter_js_1.omitFilterField;
772
+ return value;
773
+ });
774
+ };
775
+ DataApi.prototype.handleError = function (response, err) {
776
+ if (err.isForbiddenError)
777
+ response.forbidden(err.message);
778
+ else
779
+ response.error(err, this.repository.metadata);
780
+ };
778
781
  DataApi.prototype.doOnId = function (response, id, what) {
779
782
  return tslib_1.__awaiter(this, void 0, void 0, function () {
780
783
  var where, _a, _b, err_8;
@@ -917,7 +920,7 @@ var DataApi = /** @class */ (function () {
917
920
  case 2: return [2 /*return*/, _a.sent()];
918
921
  case 3:
919
922
  err_9 = _a.sent();
920
- response.error(err_9, this.repository.metadata);
923
+ this.handleError(response, err_9);
921
924
  return [3 /*break*/, 4];
922
925
  case 4: return [2 /*return*/];
923
926
  }
@@ -1,8 +1,8 @@
1
1
  import type { DataProvider, EntityDataProvider, EntityDataProviderGroupByOptions, EntityDataProviderFindOptions } from '../data-interfaces.js';
2
- import type { HasWrapIdentifier, SqlCommand, SqlCommandFactory, SqlCommandWithParameters, SqlImplementation, SqlResult } from '../sql-command.js';
2
+ import type { HasJsonColumnStorage, HasWrapIdentifier, JsonColumnStorage, SqlCommand, SqlCommandFactory, SqlCommandWithParameters, SqlImplementation, SqlResult } from '../sql-command.js';
3
3
  import type { FieldMetadata } from '../column-interfaces.js';
4
4
  import type { CustomSqlFilterBuilderFunction, EntityDbNamesBase } from '../filter/filter-consumer-bridge-to-sql-request.js';
5
- import { type EntityFilter, type EntityMetadata, type FindOptions, type GroupByOptions, type GroupByResult, type MembersOnly, type NumericKeys } from '../remult3/remult3.js';
5
+ import { type EntityFilter, type EntityMetadata, type FindOptions, type GroupByOptions, type GroupByResult, type MembersOnly, type NumericKeys, type Repository } from '../remult3/remult3.js';
6
6
  import type { RepositoryOverloads } from '../remult3/RepositoryImplementation.js';
7
7
  import type { CanBuildMigrations, MigrationBuilder, MigrationCode } from '../../migrations/migration-types.js';
8
8
  /**
@@ -12,7 +12,7 @@ import type { CanBuildMigrations, MigrationBuilder, MigrationCode } from '../../
12
12
  * @see [Connecting a Database](https://remult.dev/docs/quickstart#connecting-a-database)
13
13
 
14
14
  */
15
- export declare class SqlDatabase implements DataProvider, HasWrapIdentifier, CanBuildMigrations, SqlCommandFactory {
15
+ export declare class SqlDatabase implements DataProvider, HasWrapIdentifier, HasJsonColumnStorage, CanBuildMigrations, SqlCommandFactory {
16
16
  private sql;
17
17
  /**
18
18
  * Gets the SQL database from the data provider.
@@ -38,6 +38,7 @@ export declare class SqlDatabase implements DataProvider, HasWrapIdentifier, Can
38
38
  * Wraps an identifier with the database's identifier syntax.
39
39
  */
40
40
  wrapIdentifier: (name: string) => string;
41
+ get jsonColumnStorage(): JsonColumnStorage;
41
42
  ensureSchema(entities: EntityMetadata[]): Promise<void>;
42
43
  /**
43
44
  * Gets the entity data provider.
@@ -121,20 +122,28 @@ export interface RawSelectOptions {
121
122
  wrapIdentifier?: (name: string) => string;
122
123
  /** Required only with `limit`: `(limit, offset) => 'limit 10 offset 20'`. */
123
124
  limitSyntax?: (limit: number, offset: number) => string;
125
+ /**
126
+ * JSON columns are stored as text (SQL Server, sqlite, mysql).
127
+ * Defaults to the repository's data provider.
128
+ * Set when `sqlCommand` runs against a different database than that provider.
129
+ */
130
+ jsonAsString?: boolean;
124
131
  }
132
+ export declare function jsonColumnStorageOf(r: Repository<any>): JsonColumnStorage;
125
133
  export declare function getRowAfterUpdate<entityType>(meta: EntityMetadata<entityType>, dataProvider: EntityDataProvider, data: any, id: any, operation: string): any;
126
134
  /** The select that `find` runs, and the mapping of one result row to field values. With `aliasColumns`, every column is aliased through `wrapIdentifier`, so a row can be read by alias as well as by position. */
127
- export declare function buildFindSql(entity: EntityMetadata, options: EntityDataProviderFindOptions | undefined, e: EntityDbNamesBase, r: SqlCommandWithParameters, orderByNullsFirst: boolean | undefined, limitSyntax: (limit: number, offset: number) => string, { aliasColumns }?: {
128
- aliasColumns?: boolean | undefined;
135
+ export declare function buildFindSql(entity: EntityMetadata, options: EntityDataProviderFindOptions | undefined, e: EntityDbNamesBase, r: SqlCommandWithParameters, orderByNullsFirst: boolean | undefined, limitSyntax: (limit: number, offset: number) => string, { aliasColumns, jsonColumnStorage, }?: {
136
+ aliasColumns?: boolean;
137
+ jsonColumnStorage?: JsonColumnStorage;
129
138
  }): Promise<{
130
139
  select: string;
131
140
  colKeys: FieldMetadata<unknown, unknown>[];
132
141
  /** `valueAt` reads one column of a result row, by its position in the select or by its alias (the field key). */
133
142
  toResult(valueAt: (indexInSelect: number, alias: string) => any): any;
134
143
  }>;
135
- export declare function groupByImpl(options: EntityDataProviderGroupByOptions | undefined, e: EntityDbNamesBase, r: SqlCommand, orderByNullFirst: boolean | undefined, limitSyntax: (limit: number, offset: number) => string): Promise<any[]>;
144
+ export declare function groupByImpl(options: EntityDataProviderGroupByOptions | undefined, e: EntityDbNamesBase, r: SqlCommand, orderByNullFirst: boolean | undefined, limitSyntax: (limit: number, offset: number) => string, jsonColumnStorage?: JsonColumnStorage): Promise<any[]>;
136
145
  /** The select that `groupBy` runs, and the mapping of one result row back to a `GroupByResult`. Every column is aliased through `wrapIdentifier`, so a row can be read by alias as well as by position. */
137
- export declare function buildGroupBySql(options: EntityDataProviderGroupByOptions | undefined, e: EntityDbNamesBase, r: SqlCommandWithParameters, orderByNullFirst: boolean | undefined, limitSyntax: (limit: number, offset: number) => string): Promise<{
146
+ export declare function buildGroupBySql(options: EntityDataProviderGroupByOptions | undefined, e: EntityDbNamesBase, r: SqlCommandWithParameters, orderByNullFirst: boolean | undefined, limitSyntax: (limit: number, offset: number) => string, jsonColumnStorage?: JsonColumnStorage): Promise<{
138
147
  select: string;
139
148
  /** `valueAt` reads one column of a result row, by its position in the select or by its alias. */
140
149
  toResult(valueAt: (indexInSelect: number, alias: string) => any): any;