remult 3.1.2 → 3.2.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.
@@ -146,6 +146,8 @@ class KnexEntityDataProvider {
146
146
  let cols = [];
147
147
  let colKeys = [];
148
148
  for (const x of this.entity.fields) {
149
+ if (options.select && !options.select.includes(x.key))
150
+ continue;
149
151
  if (x.isServerExpression) {
150
152
  }
151
153
  else {
@@ -96,9 +96,11 @@ class MongoEntityDataProvider {
96
96
  this.entity = entity;
97
97
  this.session = session;
98
98
  }
99
- translateFromDb(row, nameProvider) {
99
+ translateFromDb(row, nameProvider, select) {
100
100
  let result = {};
101
101
  for (const col of this.entity.fields) {
102
+ if (select && !select.includes(col.key))
103
+ continue;
102
104
  let val = row[nameProvider.$dbNameOf(col)];
103
105
  if (val === undefined) {
104
106
  if (!col.allowNull) {
@@ -241,7 +243,7 @@ class MongoEntityDataProvider {
241
243
  }
242
244
  return await Promise.all(await collection
243
245
  .find(where, op)
244
- .map((x) => this.translateFromDb(x, e))
246
+ .map((x) => this.translateFromDb(x, e, options.select))
245
247
  .toArray());
246
248
  }
247
249
  async update(id, data) {
@@ -244,6 +244,15 @@ export class DataApi {
244
244
  limit = DataApi.defaultGetLimit;
245
245
  findOptions.limit = limit;
246
246
  findOptions.page = +request.get('_page');
247
+ const select = request.get('_select');
248
+ if (select) {
249
+ findOptions.select = select
250
+ .split(',')
251
+ .reduce((acc, key) => {
252
+ acc[key] = true;
253
+ return acc;
254
+ }, {});
255
+ }
247
256
  }
248
257
  if (this.remult.isAllowed(this.repository.metadata.options.apiRequireId)) {
249
258
  let hasId = false;
@@ -249,14 +249,16 @@ export class ArrayEntityDataProvider {
249
249
  }
250
250
  if (rows)
251
251
  return rows.map((i) => {
252
- return this.translateFromJson(i, dbNames);
252
+ return this.translateFromJson(i, dbNames, options?.select);
253
253
  });
254
254
  return [];
255
255
  }
256
256
  //@internal
257
- translateFromJson(row, dbNames) {
257
+ translateFromJson(row, dbNames, select) {
258
258
  let result = {};
259
259
  for (const col of this.entity.fields) {
260
+ if (select && !select.includes(col.key))
261
+ continue;
260
262
  result[col.key] = col.valueConverter.fromJson(row[dbNames.$dbNameOf(col)]);
261
263
  }
262
264
  return result;
@@ -56,6 +56,9 @@ export function findOptionsToJson(options, meta) {
56
56
  ...options,
57
57
  load: options.load(meta.fields).map((y) => y.key),
58
58
  };
59
+ if (options.select) {
60
+ options = { ...options, select: Object.keys(options.select) };
61
+ }
59
62
  return options;
60
63
  }
61
64
  //@internal
@@ -67,6 +70,7 @@ export function findOptionsFromJson(json, meta) {
67
70
  'where',
68
71
  'orderBy',
69
72
  'include',
73
+ 'select',
70
74
  ]) {
71
75
  if (json[key] !== undefined) {
72
76
  if (key === 'where') {
@@ -88,6 +92,12 @@ export function findOptionsFromJson(json, meta) {
88
92
  }
89
93
  r[key] = newInclude;
90
94
  }
95
+ else if (key === 'select') {
96
+ r[key] = json.select.reduce((acc, key) => {
97
+ acc[key] = true;
98
+ return acc;
99
+ }, {});
100
+ }
91
101
  else
92
102
  r[key] = json[key];
93
103
  }
@@ -216,6 +226,9 @@ export class RestEntityDataProvider {
216
226
  url.add('_limit', options.limit);
217
227
  if (options.page)
218
228
  url.add('_page', options.page);
229
+ if (options.select) {
230
+ url.add('_select', options.select.join(','));
231
+ }
219
232
  }
220
233
  const run = (action, body) => {
221
234
  let u = new UrlBuilder(url.url);
@@ -292,7 +292,7 @@ class ActualSQLEntityDataProvider {
292
292
  }
293
293
  async find(options) {
294
294
  let e = await this.init();
295
- let { colKeys, select } = this.buildSelect(e);
295
+ let { colKeys, select } = this.buildSelect(e, options?.select);
296
296
  select = 'select ' + select;
297
297
  select += '\n from ' + e.$entityName;
298
298
  let r = this.sql.createCommand();
@@ -362,10 +362,12 @@ class ActualSQLEntityDataProvider {
362
362
  }
363
363
  return result;
364
364
  }
365
- buildSelect(e) {
365
+ buildSelect(e, selectedFields) {
366
366
  let select = '';
367
367
  let colKeys = [];
368
368
  for (const x of this.entity.fields) {
369
+ if (selectedFields && !selectedFields.includes(x.key))
370
+ continue;
369
371
  if (x.isServerExpression) {
370
372
  }
371
373
  else {
@@ -583,6 +583,20 @@ export class RepositoryImplementation {
583
583
  options = { ...options };
584
584
  let opt = {};
585
585
  opt = {};
586
+ if (options.select) {
587
+ opt.select = Object.keys(options.select)
588
+ .filter((x) => options.select[x])
589
+ .map((x) => {
590
+ let f = this.metadata.fields.find(x);
591
+ const r = getRelationFieldInfo(f);
592
+ if (r) {
593
+ throw new Error(`select is not allowed for relation field ${x} in entity ${this.metadata.label}, use include instead`);
594
+ }
595
+ if (!f)
596
+ throw new Error(`Field ${x} not found in entity ${this.metadata.label}`);
597
+ return f.key;
598
+ });
599
+ }
586
600
  if (!options.orderBy || Object.keys(options.orderBy).length === 0) {
587
601
  if (!this._dataProvider.isProxy)
588
602
  options.orderBy = this._info.entityInfo.defaultOrderBy;
@@ -731,6 +745,7 @@ export class RepositoryImplementation {
731
745
  'limit',
732
746
  'include',
733
747
  'orderBy',
748
+ 'select',
734
749
  ]) {
735
750
  //@ts-ignore
736
751
  if (source[key])
@@ -2246,8 +2261,8 @@ export function ValueListFieldType(...options) {
2246
2261
  return (type, context) => {
2247
2262
  FieldType((o) => {
2248
2263
  ;
2249
- (o.valueConverter = ValueListInfo.get(type)),
2250
- (o.displayValue = (item, val) => val?.label ?? val?.caption);
2264
+ ((o.valueConverter = ValueListInfo.get(type)),
2265
+ (o.displayValue = (item, val) => val?.label ?? val?.caption));
2251
2266
  o.validate = (entity, ref) => {
2252
2267
  const values = ValueListInfo.get(type).getValues();
2253
2268
  if (ref.value && !values.find((v) => v === ref.value)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "3.1.2",
3
+ "version": "3.2.0",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "homepage": "https://remult.dev",
6
6
  "repository": {
@@ -266,6 +266,8 @@ var KnexEntityDataProvider = /** @class */ (function () {
266
266
  try {
267
267
  for (_a = tslib_1.__values(this.entity.fields), _b = _a.next(); !_b.done; _b = _a.next()) {
268
268
  x = _b.value;
269
+ if (options.select && !options.select.includes(x.key))
270
+ continue;
269
271
  if (x.isServerExpression) {
270
272
  }
271
273
  else {
package/remult-mongo.js CHANGED
@@ -191,12 +191,14 @@ var MongoEntityDataProvider = /** @class */ (function () {
191
191
  });
192
192
  });
193
193
  };
194
- MongoEntityDataProvider.prototype.translateFromDb = function (row, nameProvider) {
194
+ MongoEntityDataProvider.prototype.translateFromDb = function (row, nameProvider, select) {
195
195
  var e_3, _a;
196
196
  var result = {};
197
197
  try {
198
198
  for (var _b = tslib_1.__values(this.entity.fields), _c = _b.next(); !_c.done; _c = _b.next()) {
199
199
  var col = _c.value;
200
+ if (select && !select.includes(col.key))
201
+ continue;
200
202
  var val = row[nameProvider.$dbNameOf(col)];
201
203
  if (val === undefined) {
202
204
  if (!col.allowNull) {
@@ -466,7 +468,7 @@ var MongoEntityDataProvider = /** @class */ (function () {
466
468
  _e = (_d = Promise).all;
467
469
  return [4 /*yield*/, collection
468
470
  .find(where, op)
469
- .map(function (x) { return _this.translateFromDb(x, e); })
471
+ .map(function (x) { return _this.translateFromDb(x, e, options.select); })
470
472
  .toArray()];
471
473
  case 3: return [4 /*yield*/, _e.apply(_d, [_g.sent()])];
472
474
  case 4: return [2 /*return*/, _g.sent()];
package/src/data-api.js CHANGED
@@ -469,7 +469,7 @@ var DataApi = /** @class */ (function () {
469
469
  };
470
470
  DataApi.prototype.findOptionsFromRequest = function (request, body) {
471
471
  return tslib_1.__awaiter(this, void 0, void 0, function () {
472
- var findOptions, _a, sort, dir, limit, hasId_1, w;
472
+ var findOptions, _a, sort, dir, limit, select, hasId_1, w;
473
473
  var _this = this;
474
474
  return tslib_1.__generator(this, function (_b) {
475
475
  switch (_b.label) {
@@ -493,6 +493,15 @@ var DataApi = /** @class */ (function () {
493
493
  limit = DataApi.defaultGetLimit;
494
494
  findOptions.limit = limit;
495
495
  findOptions.page = +request.get('_page');
496
+ select = request.get('_select');
497
+ if (select) {
498
+ findOptions.select = select
499
+ .split(',')
500
+ .reduce(function (acc, key) {
501
+ acc[key] = true;
502
+ return acc;
503
+ }, {});
504
+ }
496
505
  }
497
506
  if (!this.remult.isAllowed(this.repository.metadata.options.apiRequireId)) return [3 /*break*/, 3];
498
507
  hasId_1 = false;
@@ -46,6 +46,7 @@ export interface ProxyEntityDataProvider {
46
46
  }[]): Promise<any[]>;
47
47
  }
48
48
  export interface EntityDataProviderFindOptions {
49
+ select?: string[];
49
50
  where?: Filter;
50
51
  limit?: number;
51
52
  page?: number;
@@ -406,7 +406,7 @@ var ArrayEntityDataProvider = /** @class */ (function () {
406
406
  }
407
407
  if (rows)
408
408
  return [2 /*return*/, rows.map(function (i) {
409
- return _this.translateFromJson(i, dbNames);
409
+ return _this.translateFromJson(i, dbNames, options === null || options === void 0 ? void 0 : options.select);
410
410
  })];
411
411
  return [2 /*return*/, []];
412
412
  }
@@ -414,12 +414,14 @@ var ArrayEntityDataProvider = /** @class */ (function () {
414
414
  });
415
415
  };
416
416
  //@internal
417
- ArrayEntityDataProvider.prototype.translateFromJson = function (row, dbNames) {
417
+ ArrayEntityDataProvider.prototype.translateFromJson = function (row, dbNames, select) {
418
418
  var e_12, _a;
419
419
  var result = {};
420
420
  try {
421
421
  for (var _b = tslib_1.__values(this.entity.fields), _c = _b.next(); !_c.done; _c = _b.next()) {
422
422
  var col = _c.value;
423
+ if (select && !select.includes(col.key))
424
+ continue;
423
425
  result[col.key] = col.valueConverter.fromJson(row[dbNames.$dbNameOf(col)]);
424
426
  }
425
427
  }
@@ -65,6 +65,9 @@ function findOptionsToJson(options, meta) {
65
65
  options = tslib_1.__assign(tslib_1.__assign({}, options), { where: filter_interfaces_js_1.Filter.entityFilterToJson(meta, options.where) });
66
66
  if (options.load)
67
67
  options = tslib_1.__assign(tslib_1.__assign({}, options), { load: options.load(meta.fields).map(function (y) { return y.key; }) });
68
+ if (options.select) {
69
+ options = tslib_1.__assign(tslib_1.__assign({}, options), { select: Object.keys(options.select) });
70
+ }
68
71
  return options;
69
72
  }
70
73
  //@internal
@@ -78,6 +81,7 @@ function findOptionsFromJson(json, meta) {
78
81
  'where',
79
82
  'orderBy',
80
83
  'include',
84
+ 'select',
81
85
  ]), _c = _b.next(); !_c.done; _c = _b.next()) {
82
86
  var key = _c.value;
83
87
  if (json[key] !== undefined) {
@@ -100,6 +104,12 @@ function findOptionsFromJson(json, meta) {
100
104
  }
101
105
  r[key] = newInclude;
102
106
  }
107
+ else if (key === 'select') {
108
+ r[key] = json.select.reduce(function (acc, key) {
109
+ acc[key] = true;
110
+ return acc;
111
+ }, {});
112
+ }
103
113
  else
104
114
  r[key] = json[key];
105
115
  }
@@ -304,6 +314,9 @@ var RestEntityDataProvider = /** @class */ (function () {
304
314
  url.add('_limit', options.limit);
305
315
  if (options.page)
306
316
  url.add('_page', options.page);
317
+ if (options.select) {
318
+ url.add('_select', options.select.join(','));
319
+ }
307
320
  }
308
321
  var run = function (action, body) {
309
322
  var u = new urlBuilder_js_1.UrlBuilder(url.url);
@@ -421,7 +421,7 @@ var ActualSQLEntityDataProvider = /** @class */ (function () {
421
421
  case 0: return [4 /*yield*/, this.init()];
422
422
  case 1:
423
423
  e = _g.sent();
424
- _a = this.buildSelect(e), colKeys = _a.colKeys, select = _a.select;
424
+ _a = this.buildSelect(e, options === null || options === void 0 ? void 0 : options.select), colKeys = _a.colKeys, select = _a.select;
425
425
  select = 'select ' + select;
426
426
  select += '\n from ' + e.$entityName;
427
427
  r = this.sql.createCommand();
@@ -518,13 +518,15 @@ var ActualSQLEntityDataProvider = /** @class */ (function () {
518
518
  }
519
519
  return result;
520
520
  };
521
- ActualSQLEntityDataProvider.prototype.buildSelect = function (e) {
521
+ ActualSQLEntityDataProvider.prototype.buildSelect = function (e, selectedFields) {
522
522
  var e_4, _a;
523
523
  var select = '';
524
524
  var colKeys = [];
525
525
  try {
526
526
  for (var _b = tslib_1.__values(this.entity.fields), _c = _b.next(); !_c.done; _c = _b.next()) {
527
527
  var x = _c.value;
528
+ if (selectedFields && !selectedFields.includes(x.key))
529
+ continue;
528
530
  if (x.isServerExpression) {
529
531
  }
530
532
  else {
@@ -945,12 +945,27 @@ var RepositoryImplementation = /** @class */ (function () {
945
945
  RepositoryImplementation.prototype._buildEntityDataProviderFindOptions = function (options) {
946
946
  return tslib_1.__awaiter(this, void 0, void 0, function () {
947
947
  var opt, _a;
948
+ var _this = this;
948
949
  return tslib_1.__generator(this, function (_b) {
949
950
  switch (_b.label) {
950
951
  case 0:
951
952
  options = tslib_1.__assign({}, options);
952
953
  opt = {};
953
954
  opt = {};
955
+ if (options.select) {
956
+ opt.select = Object.keys(options.select)
957
+ .filter(function (x) { return options.select[x]; })
958
+ .map(function (x) {
959
+ var f = _this.metadata.fields.find(x);
960
+ var r = (0, relationInfoMember_js_1.getRelationFieldInfo)(f);
961
+ if (r) {
962
+ throw new Error("select is not allowed for relation field ".concat(x, " in entity ").concat(_this.metadata.label, ", use include instead"));
963
+ }
964
+ if (!f)
965
+ throw new Error("Field ".concat(x, " not found in entity ").concat(_this.metadata.label));
966
+ return f.key;
967
+ });
968
+ }
954
969
  if (!options.orderBy || Object.keys(options.orderBy).length === 0) {
955
970
  if (!this._dataProvider.isProxy)
956
971
  options.orderBy = this._info.entityInfo.defaultOrderBy;
@@ -1245,6 +1260,7 @@ var RepositoryImplementation = /** @class */ (function () {
1245
1260
  'limit',
1246
1261
  'include',
1247
1262
  'orderBy',
1263
+ 'select',
1248
1264
  ])), _f = _e.next(); !_f.done; _f = _e.next()) {
1249
1265
  var key = _f.value;
1250
1266
  //@ts-ignore
@@ -3673,8 +3689,8 @@ function ValueListFieldType() {
3673
3689
  return function (type, context) {
3674
3690
  FieldType.apply(void 0, tslib_1.__spreadArray([function (o) {
3675
3691
  ;
3676
- (o.valueConverter = ValueListInfo.get(type)),
3677
- (o.displayValue = function (item, val) { var _a; return (_a = val === null || val === void 0 ? void 0 : val.label) !== null && _a !== void 0 ? _a : val === null || val === void 0 ? void 0 : val.caption; });
3692
+ ((o.valueConverter = ValueListInfo.get(type)),
3693
+ (o.displayValue = function (item, val) { var _a; return (_a = val === null || val === void 0 ? void 0 : val.label) !== null && _a !== void 0 ? _a : val === null || val === void 0 ? void 0 : val.caption; }));
3678
3694
  o.validate = function (entity, ref) {
3679
3695
  var values = ValueListInfo.get(type).getValues();
3680
3696
  if (ref.value && !values.find(function (v) { return v === ref.value; })) {
@@ -1023,8 +1023,8 @@ export interface LoadOptions<entityType> {
1023
1023
  */
1024
1024
  load?: (entity: FieldsMetadata<entityType>) => FieldMetadata[];
1025
1025
  /**
1026
- * An option used in the `find` and `findFirst` methods to specify which related entities should be included
1027
- * when querying the source entity. It allows you to eagerly load related data to avoid N+1 query problems.
1026
+ * An option used to specify which related entities should be included when querying the source entity.
1027
+ * It allows you to eagerly load related data to avoid N+1 query problems.
1028
1028
  *
1029
1029
  * @param include An object specifying the related entities to include, their options, and filtering criteria.
1030
1030
  *
@@ -1046,6 +1046,14 @@ export interface LoadOptions<entityType> {
1046
1046
  include?: MembersToInclude<entityType>;
1047
1047
  }
1048
1048
  export interface FindOptionsBase<entityType> extends LoadOptions<entityType> {
1049
+ /**
1050
+ * An option used to specify which fields should be included in the result.
1051
+ * @example
1052
+ * ```ts
1053
+ * await repo(Task).find({ select: { id: true, title: true } })
1054
+ * ```
1055
+ */
1056
+ select?: EntitySelectFields<entityType>;
1049
1057
  /** filters the data
1050
1058
  * @example
1051
1059
  * await taskRepo.find({where: { completed:false }})
@@ -1184,6 +1192,9 @@ export type RepositoryRelationsForEntityBase<entityType> = {
1184
1192
  export declare type EntityIdFields<entityType> = {
1185
1193
  [Properties in keyof Partial<MembersOnly<entityType>>]?: true;
1186
1194
  };
1195
+ export declare type EntitySelectFields<entityType> = {
1196
+ [Properties in keyof Partial<MembersOnly<entityType>>]?: boolean;
1197
+ };
1187
1198
  export interface ClassFieldDecoratorContextStub<entityType, valueType> {
1188
1199
  readonly access: {
1189
1200
  set(object: entityType, value: valueType | null): void;