@loopback/repository 2.11.1 → 3.1.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.
Files changed (63) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/dist/common-types.d.ts +5 -3
  3. package/dist/common-types.js +9 -3
  4. package/dist/common-types.js.map +1 -1
  5. package/dist/connectors/crud.connector.d.ts +3 -3
  6. package/dist/connectors/kv.connector.d.ts +3 -3
  7. package/dist/decorators/model.decorator.js +7 -0
  8. package/dist/decorators/model.decorator.js.map +1 -1
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.js +9 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/mixins/repository.mixin.d.ts +9 -3
  13. package/dist/model.d.ts +29 -6
  14. package/dist/model.js.map +1 -1
  15. package/dist/relations/belongs-to/belongs-to.decorator.js +10 -1
  16. package/dist/relations/belongs-to/belongs-to.decorator.js.map +1 -1
  17. package/dist/relations/has-many/has-many-through-repository.factory.d.ts +8 -2
  18. package/dist/relations/has-many/has-many-through-repository.factory.js +6 -0
  19. package/dist/relations/has-many/has-many-through-repository.factory.js.map +1 -1
  20. package/dist/relations/has-many/has-many-through.inclusion.resolver.d.ts +16 -0
  21. package/dist/relations/has-many/has-many-through.inclusion.resolver.js +74 -0
  22. package/dist/relations/has-many/has-many-through.inclusion.resolver.js.map +1 -0
  23. package/dist/relations/has-many/has-many.inclusion-resolver.js.map +1 -1
  24. package/dist/relations/has-many/has-many.repository.d.ts +1 -1
  25. package/dist/relations/has-one/has-one.repository.d.ts +1 -1
  26. package/dist/relations/has-one/has-one.repository.js.map +1 -1
  27. package/dist/relations/relation.helpers.d.ts +2 -2
  28. package/dist/relations/relation.helpers.js +1 -1
  29. package/dist/relations/relation.helpers.js.map +1 -1
  30. package/dist/relations/relation.types.d.ts +1 -1
  31. package/dist/relations/relation.types.js +1 -1
  32. package/dist/repositories/constraint-utils.d.ts +1 -1
  33. package/dist/repositories/constraint-utils.js +4 -4
  34. package/dist/repositories/constraint-utils.js.map +1 -1
  35. package/dist/repositories/legacy-juggler-bridge.d.ts +1 -1
  36. package/dist/repositories/legacy-juggler-bridge.js +1 -1
  37. package/dist/repositories/legacy-juggler-bridge.js.map +1 -1
  38. package/dist/repositories/repository.d.ts +1 -1
  39. package/dist/repositories/repository.js.map +1 -1
  40. package/package.json +16 -12
  41. package/src/common-types.ts +10 -4
  42. package/src/connectors/crud.connector.ts +3 -3
  43. package/src/connectors/kv.connector.ts +3 -3
  44. package/src/decorators/model.decorator.ts +9 -0
  45. package/src/index.ts +1 -1
  46. package/src/model.ts +32 -2
  47. package/src/relations/belongs-to/belongs-to.decorator.ts +21 -5
  48. package/src/relations/belongs-to/belongs-to.inclusion-resolver.ts +1 -1
  49. package/src/relations/has-many/has-many-through-repository.factory.ts +20 -3
  50. package/src/relations/has-many/has-many-through.inclusion.resolver.ts +135 -0
  51. package/src/relations/has-many/has-many.inclusion-resolver.ts +1 -1
  52. package/src/relations/has-many/has-many.repository.ts +1 -1
  53. package/src/relations/has-one/has-one.inclusion-resolver.ts +1 -1
  54. package/src/relations/has-one/has-one.repository.ts +1 -1
  55. package/src/relations/relation.helpers.ts +2 -5
  56. package/src/relations/relation.types.ts +1 -1
  57. package/src/repositories/constraint-utils.ts +1 -1
  58. package/src/repositories/legacy-juggler-bridge.ts +8 -7
  59. package/src/repositories/repository.ts +3 -2
  60. package/dist/query.d.ts +0 -382
  61. package/dist/query.js +0 -487
  62. package/dist/query.js.map +0 -1
  63. package/src/query.ts +0 -729
package/src/query.ts DELETED
@@ -1,729 +0,0 @@
1
- // Copyright IBM Corp. 2017,2020. All Rights Reserved.
2
- // Node module: @loopback/repository
3
- // This file is licensed under the MIT License.
4
- // License text available at https://opensource.org/licenses/MIT
5
-
6
- import assert from 'assert';
7
- import {AnyObject} from './common-types';
8
-
9
- /* eslint-disable @typescript-eslint/no-explicit-any */
10
-
11
- const nonWhereFields = [
12
- 'fields',
13
- 'order',
14
- 'limit',
15
- 'skip',
16
- 'offset',
17
- 'include',
18
- ];
19
-
20
- const filterFields = ['where', ...nonWhereFields];
21
-
22
- /**
23
- * Operators for where clauses
24
- */
25
- export type Operators =
26
- | 'eq' // Equal
27
- | 'neq' // Not Equal
28
- | 'gt' // >
29
- | 'gte' // >=
30
- | 'lt' // <
31
- | 'lte' // <=
32
- | 'inq' // IN
33
- | 'nin' // NOT IN
34
- | 'between' // BETWEEN [val1, val2]
35
- | 'exists'
36
- | 'and' // AND
37
- | 'or' // OR
38
- | 'like' // LIKE
39
- | 'nlike' // NOT LIKE
40
- | 'ilike' // ILIKE'
41
- | 'nilike' // NOT ILIKE
42
- | 'regexp'; // REGEXP'
43
-
44
- /**
45
- * Matching predicate comparison
46
- */
47
- export type PredicateComparison<PT> = {
48
- eq?: PT;
49
- neq?: PT;
50
- gt?: PT;
51
- gte?: PT;
52
- lt?: PT;
53
- lte?: PT;
54
- inq?: PT[];
55
- nin?: PT[];
56
- between?: [PT, PT];
57
- exists?: boolean;
58
- like?: PT;
59
- nlike?: PT;
60
- ilike?: PT;
61
- nilike?: PT;
62
- regexp?: string | RegExp;
63
- // [extendedOperation: string]: any;
64
- };
65
-
66
- /**
67
- * Value types for `{propertyName: value}`
68
- */
69
- export type ShortHandEqualType = string | number | boolean | Date;
70
-
71
- /**
72
- * Key types of a given model, excluding operators
73
- */
74
- export type KeyOf<MT extends object> = Exclude<
75
- Extract<keyof MT, string>,
76
- Operators
77
- >;
78
-
79
- /**
80
- * Condition clause
81
- *
82
- * @example
83
- * ```ts
84
- * {
85
- * name: {inq: ['John', 'Mary']},
86
- * status: 'ACTIVE',
87
- * age: {gte: 40}
88
- * }
89
- * ```
90
- */
91
- export type Condition<MT extends object> = {
92
- [P in KeyOf<MT>]?:
93
- | PredicateComparison<MT[P]> // {x: {lt: 1}}
94
- | (MT[P] & ShortHandEqualType); // {x: 1},
95
- };
96
-
97
- /**
98
- * Where clause
99
- *
100
- * @example
101
- * ```ts
102
- * {
103
- * name: {inq: ['John', 'Mary']},
104
- * status: 'ACTIVE'
105
- * and: [...],
106
- * or: [...],
107
- * }
108
- * ```
109
- */
110
- export type Where<MT extends object = AnyObject> =
111
- | Condition<MT>
112
- | AndClause<MT>
113
- | OrClause<MT>;
114
-
115
- /**
116
- * And clause
117
- *
118
- * @example
119
- * ```ts
120
- * {
121
- * and: [...],
122
- * }
123
- * ```
124
- */
125
- export interface AndClause<MT extends object> {
126
- and: Where<MT>[];
127
- }
128
-
129
- /**
130
- * Or clause
131
- *
132
- * @example
133
- * ```ts
134
- * {
135
- * or: [...],
136
- * }
137
- * ```
138
- */
139
- export interface OrClause<MT extends object> {
140
- or: Where<MT>[];
141
- }
142
-
143
- /**
144
- * Order by direction
145
- */
146
- export type Direction = 'ASC' | 'DESC';
147
-
148
- /**
149
- * Order by
150
- *
151
- * Example:
152
- * `{afieldname: 'ASC'}`
153
- */
154
- export type Order<MT = AnyObject> = {[P in keyof MT]: Direction};
155
-
156
- /**
157
- * Selection of fields
158
- *
159
- * Example:
160
- * `{afieldname: true}`
161
- */
162
- export type Fields<MT = AnyObject> = {[P in keyof MT]?: boolean};
163
-
164
- /**
165
- * Inclusion of related items
166
- *
167
- * Note: scope means filter on related items
168
- *
169
- * Example:
170
- * `{relation: 'aRelationName', scope: {<AFilterObject>}}`
171
- */
172
- export interface Inclusion {
173
- relation: string;
174
-
175
- // Technically, we should restrict the filter to target model.
176
- // That is unfortunately rather difficult to achieve, because
177
- // an Entity does not provide type information about related models.
178
- // We could use {ModelName}WithRelations interface for first-level inclusion,
179
- // but that won't handle second-level (and deeper) inclusions.
180
- // To keep things simple, we allow any filter here, effectively turning off
181
- // compiler checks.
182
- scope?: Filter<AnyObject>;
183
- }
184
-
185
- /**
186
- * Query filter object
187
- */
188
- export interface Filter<MT extends object = AnyObject> {
189
- /**
190
- * The matching criteria
191
- */
192
- where?: Where<MT>;
193
- /**
194
- * To include/exclude fields
195
- */
196
- fields?: Fields<MT>;
197
- /**
198
- * Sorting order for matched entities. Each item should be formatted as
199
- * `fieldName ASC` or `fieldName DESC`.
200
- * For example: `['f1 ASC', 'f2 DESC', 'f3 ASC']`.
201
- *
202
- * We might want to use `Order` in the future. Keep it as `string[]` for now
203
- * for compatibility with LoopBack 3.x.
204
- */
205
- order?: string[];
206
- /**
207
- * Maximum number of entities
208
- */
209
- limit?: number;
210
- /**
211
- * Skip N number of entities
212
- */
213
- skip?: number;
214
- /**
215
- * Offset N number of entities. An alias for `skip`
216
- */
217
- offset?: number;
218
- /**
219
- * To include related objects
220
- */
221
- include?: Inclusion[];
222
- }
223
-
224
- /**
225
- * Filter without `where` property
226
- */
227
- export type FilterExcludingWhere<MT extends object = AnyObject> = Omit<
228
- Filter<MT>,
229
- 'where'
230
- >;
231
-
232
- /**
233
- * TypeGuard for Filter
234
- * @param candidate
235
- */
236
- export function isFilter<MT extends object>(
237
- candidate: any,
238
- ): candidate is Filter<MT> {
239
- if (typeof candidate !== 'object') return false;
240
- for (const key in candidate) {
241
- if (!filterFields.includes(key)) {
242
- return false;
243
- }
244
- }
245
- return true;
246
- }
247
-
248
- /**
249
- * A builder for Where object. It provides fluent APIs to add clauses such as
250
- * `and`, `or`, and other operators.
251
- *
252
- * @example
253
- * ```ts
254
- * const whereBuilder = new WhereBuilder();
255
- * const where = whereBuilder
256
- * .eq('a', 1)
257
- * .and({x: 'x'}, {y: {gt: 1}})
258
- * .and({b: 'b'}, {c: {lt: 1}})
259
- * .or({d: 'd'}, {e: {neq: 1}})
260
- * .build();
261
- * ```
262
- */
263
- export class WhereBuilder<MT extends object = AnyObject> {
264
- where: Where<MT>;
265
-
266
- constructor(w?: Where<MT>) {
267
- this.where = w ?? {};
268
- }
269
-
270
- private add(w: Where<MT>): this {
271
- for (const k of Object.keys(w)) {
272
- if (k in this.where) {
273
- // Found conflicting keys, create an `and` operator to join the existing
274
- // conditions with the new one
275
- const where = {and: [this.where, w]};
276
- this.where = where;
277
- return this;
278
- }
279
- }
280
- // Merge the where items
281
- this.where = Object.assign(this.where, w);
282
- return this;
283
- }
284
-
285
- /**
286
- * @deprecated
287
- * Starting from TypeScript 3.2, we don't have to cast any more. This method
288
- * should be considered as `deprecated`.
289
- *
290
- * Cast an `and`, `or`, or condition clause to Where
291
- * @param clause - And/Or/Condition clause
292
- */
293
- cast(clause: AndClause<MT> | OrClause<MT> | Condition<MT>): Where<MT> {
294
- return clause;
295
- }
296
-
297
- /**
298
- * Add an `and` clause.
299
- * @param w - One or more where objects
300
- */
301
- and(...w: (Where<MT> | Where<MT>[])[]): this {
302
- let clauses: Where<MT>[] = [];
303
- w.forEach(where => {
304
- clauses = clauses.concat(Array.isArray(where) ? where : [where]);
305
- });
306
- return this.add({and: clauses});
307
- }
308
-
309
- /**
310
- * Add an `or` clause.
311
- * @param w - One or more where objects
312
- */
313
- or(...w: (Where<MT> | Where<MT>[])[]): this {
314
- let clauses: Where<MT>[] = [];
315
- w.forEach(where => {
316
- clauses = clauses.concat(Array.isArray(where) ? where : [where]);
317
- });
318
- return this.add({or: clauses});
319
- }
320
-
321
- /**
322
- * Add an `=` condition
323
- * @param key - Property name
324
- * @param val - Property value
325
- */
326
- eq<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
327
- const w: Where<MT> = {};
328
- w[key] = val;
329
- return this.add(w);
330
- }
331
-
332
- /**
333
- * Add a `!=` condition
334
- * @param key - Property name
335
- * @param val - Property value
336
- */
337
- neq<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
338
- const w: Where<MT> = {};
339
- w[key] = {neq: val};
340
- return this.add(w);
341
- }
342
-
343
- /**
344
- * Add a `>` condition
345
- * @param key - Property name
346
- * @param val - Property value
347
- */
348
- gt<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
349
- const w: Where<MT> = {};
350
- w[key] = {gt: val};
351
- return this.add(w);
352
- }
353
-
354
- /**
355
- * Add a `>=` condition
356
- * @param key - Property name
357
- * @param val - Property value
358
- */
359
- gte<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
360
- const w: Where<MT> = {};
361
- w[key] = {gte: val};
362
- return this.add(w);
363
- }
364
-
365
- /**
366
- * Add a `<` condition
367
- * @param key - Property name
368
- * @param val - Property value
369
- */
370
- lt<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
371
- const w: Where<MT> = {};
372
- w[key] = {lt: val};
373
- return this.add(w);
374
- }
375
-
376
- /**
377
- * Add a `<=` condition
378
- * @param key - Property name
379
- * @param val - Property value
380
- */
381
- lte<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
382
- const w: Where<MT> = {};
383
- w[key] = {lte: val};
384
- return this.add(w);
385
- }
386
-
387
- /**
388
- * Add a `inq` condition
389
- * @param key - Property name
390
- * @param val - An array of property values
391
- */
392
- inq<K extends KeyOf<MT>>(key: K, val: MT[K][]): this {
393
- const w: Where<MT> = {};
394
- w[key] = {inq: val};
395
- return this.add(w);
396
- }
397
-
398
- /**
399
- * Add a `nin` condition
400
- * @param key - Property name
401
- * @param val - An array of property values
402
- */
403
- nin<K extends KeyOf<MT>>(key: K, val: MT[K][]): this {
404
- const w: Where<MT> = {};
405
- w[key] = {nin: val};
406
- return this.add(w);
407
- }
408
-
409
- /**
410
- * Add a `between` condition
411
- * @param key - Property name
412
- * @param val1 - Property value lower bound
413
- * @param val2 - Property value upper bound
414
- */
415
- between<K extends KeyOf<MT>>(key: K, val1: MT[K], val2: MT[K]): this {
416
- const w: Where<MT> = {};
417
- w[key] = {between: [val1, val2]};
418
- return this.add(w);
419
- }
420
-
421
- /**
422
- * Add a `exists` condition
423
- * @param key - Property name
424
- * @param val - Exists or not
425
- */
426
- exists<K extends KeyOf<MT>>(key: K, val?: boolean): this {
427
- const w: Where<MT> = {};
428
- w[key] = {exists: !!val || val == null};
429
- return this.add(w);
430
- }
431
- /**
432
- * Add a where object. For conflicting keys with the existing where object,
433
- * create an `and` clause.
434
- * @param where - Where filter
435
- */
436
- impose(where: Where<MT>): this {
437
- if (!this.where) {
438
- this.where = where || {};
439
- } else {
440
- this.add(where);
441
- }
442
- return this;
443
- }
444
-
445
- /**
446
- * Add a `like` condition
447
- * @param key - Property name
448
- * @param val - Regexp condition
449
- */
450
- like<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
451
- const w: Where<MT> = {};
452
- w[key] = {like: val};
453
- return this.add(w);
454
- }
455
-
456
- /**
457
- * Add a `nlike` condition
458
- * @param key - Property name
459
- * @param val - Regexp condition
460
- */
461
- nlike<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
462
- const w: Where<MT> = {};
463
- w[key] = {nlike: val};
464
- return this.add(w);
465
- }
466
-
467
- /**
468
- * Add a `ilike` condition
469
- * @param key - Property name
470
- * @param val - Regexp condition
471
- */
472
- ilike<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
473
- const w: Where<MT> = {};
474
- w[key] = {ilike: val};
475
- return this.add(w);
476
- }
477
-
478
- /**
479
- * Add a `nilike` condition
480
- * @param key - Property name
481
- * @param val - Regexp condition
482
- */
483
- nilike<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
484
- const w: Where<MT> = {};
485
- w[key] = {nilike: val};
486
- return this.add(w);
487
- }
488
-
489
- /**
490
- * Add a `regexp` condition
491
- * @param key - Property name
492
- * @param val - Regexp condition
493
- */
494
- regexp<K extends KeyOf<MT>>(key: K, val: string | RegExp): this {
495
- const w: Where<MT> = {};
496
- w[key] = {regexp: val};
497
- return this.add(w);
498
- }
499
-
500
- /**
501
- * Get the where object
502
- */
503
- build() {
504
- return this.where;
505
- }
506
- }
507
-
508
- /**
509
- * A builder for Filter. It provides fleunt APIs to add clauses such as
510
- * `fields`, `order`, `where`, `limit`, `offset`, and `include`.
511
- *
512
- * @example
513
- * ```ts
514
- * const filterBuilder = new FilterBuilder();
515
- * const filter = filterBuilder
516
- * .fields('id', a', 'b')
517
- * .limit(10)
518
- * .offset(0)
519
- * .order(['a ASC', 'b DESC'])
520
- * .where({id: 1})
521
- * .build();
522
- * ```
523
- */
524
- export class FilterBuilder<MT extends object = AnyObject> {
525
- filter: Filter<MT>;
526
-
527
- constructor(f?: Filter<MT>) {
528
- this.filter = f ?? {};
529
- }
530
-
531
- /**
532
- * Set `limit`
533
- * @param limit - Maximum number of records to be returned
534
- */
535
- limit(limit: number): this {
536
- assert(limit >= 1, `Limit ${limit} must a positive number`);
537
- this.filter.limit = limit;
538
- return this;
539
- }
540
-
541
- /**
542
- * Set `offset`
543
- * @param offset - Offset of the number of records to be returned
544
- */
545
- offset(offset: number): this {
546
- this.filter.offset = offset;
547
- return this;
548
- }
549
-
550
- /**
551
- * Alias to `offset`
552
- * @param skip
553
- */
554
- skip(skip: number): this {
555
- return this.offset(skip);
556
- }
557
-
558
- /**
559
- * Describe what fields to be included/excluded
560
- * @param f - A field name to be included, an array of field names to be
561
- * included, or an Fields object for the inclusion/exclusion
562
- */
563
- fields(...f: (Fields<MT> | (keyof MT)[] | keyof MT)[]): this {
564
- if (!this.filter.fields) {
565
- this.filter.fields = {};
566
- }
567
- const fields = this.filter.fields;
568
- for (const field of f) {
569
- if (Array.isArray(field)) {
570
- (field as (keyof MT)[]).forEach(i => (fields[i] = true));
571
- } else if (typeof field === 'string') {
572
- fields[field as keyof MT] = true;
573
- } else {
574
- Object.assign(fields, field);
575
- }
576
- }
577
- return this;
578
- }
579
-
580
- private validateOrder(order: string) {
581
- assert(order.match(/^[^\s]+( (ASC|DESC))?$/), 'Invalid order: ' + order);
582
- }
583
-
584
- /**
585
- * Describe the sorting order
586
- * @param o - A field name with optional direction, an array of field names,
587
- * or an Order object for the field/direction pairs
588
- */
589
- order(...o: (string | string[] | Order<MT>)[]): this {
590
- if (!this.filter.order) {
591
- this.filter.order = [];
592
- }
593
- o.forEach(order => {
594
- if (typeof order === 'string') {
595
- this.validateOrder(order);
596
- if (!order.endsWith(' ASC') && !order.endsWith(' DESC')) {
597
- order = order + ' ASC';
598
- }
599
- this.filter.order!.push(order);
600
- return this;
601
- }
602
- if (Array.isArray(order)) {
603
- order.forEach(this.validateOrder);
604
- order = order.map(i => {
605
- if (!i.endsWith(' ASC') && !i.endsWith(' DESC')) {
606
- i = i + ' ASC';
607
- }
608
- return i;
609
- });
610
- this.filter.order = this.filter.order!.concat(order);
611
- return this;
612
- }
613
- for (const i in order) {
614
- this.filter.order!.push(`${i} ${order[i]}`);
615
- }
616
- });
617
- return this;
618
- }
619
-
620
- /**
621
- * Declare `include`
622
- * @param i - A relation name, an array of relation names, or an `Inclusion`
623
- * object for the relation/scope definitions
624
- */
625
- include(...i: (string | string[] | Inclusion)[]): this {
626
- if (this.filter.include == null) {
627
- this.filter.include = [];
628
- }
629
- for (const include of i) {
630
- if (typeof include === 'string') {
631
- this.filter.include.push({relation: include});
632
- } else if (Array.isArray(include)) {
633
- for (const inc of include) this.filter.include.push({relation: inc});
634
- } else {
635
- this.filter.include!.push(include);
636
- }
637
- }
638
- return this;
639
- }
640
-
641
- /**
642
- * Declare a where clause
643
- * @param w - Where object
644
- */
645
- where(w: Where<MT>): this {
646
- this.filter.where = w;
647
- return this;
648
- }
649
-
650
- /**
651
- * Add a Filter or Where constraint object. If it is a filter object, create
652
- * an `and` clause for conflicting keys with its where object. For any other
653
- * properties, throw an error. If it's not a Filter, coerce it to a filter,
654
- * and carry out the same logic.
655
- *
656
- * @param constraint - a constraint object to merge with own filter object
657
- */
658
- impose(constraint: Filter<MT> | Where<MT>): this {
659
- if (!this.filter) {
660
- // if constraint is a Where, turn into a Filter
661
- if (!isFilter(constraint)) {
662
- constraint = {where: constraint};
663
- }
664
- this.filter = (constraint as Filter<MT>) || {};
665
- } else {
666
- if (isFilter(constraint)) {
667
- // throw error if imposed Filter has non-where fields
668
- for (const key of Object.keys(constraint)) {
669
- if (nonWhereFields.includes(key)) {
670
- throw new Error(
671
- 'merging strategy for selection, pagination, and sorting not implemented',
672
- );
673
- }
674
- }
675
- }
676
- this.filter.where = isFilter(constraint)
677
- ? new WhereBuilder(this.filter.where).impose(constraint.where!).build()
678
- : new WhereBuilder(this.filter.where).impose(constraint).build();
679
- }
680
- return this;
681
- }
682
-
683
- /**
684
- * Return the filter object
685
- */
686
- build() {
687
- return this.filter;
688
- }
689
- }
690
-
691
- /**
692
- * Get nested properties by path
693
- * @param value - Value of an object
694
- * @param path - Path to the property
695
- */
696
- function getDeepProperty(value: AnyObject, path: string): any {
697
- const props = path.split('.');
698
- for (const p of props) {
699
- value = value[p];
700
- if (value == null) {
701
- return null;
702
- }
703
- }
704
- return value;
705
- }
706
-
707
- export function filterTemplate(strings: TemplateStringsArray, ...keys: any[]) {
708
- return function filter(ctx: AnyObject) {
709
- const tokens = [strings[0]];
710
- keys.forEach((key, i) => {
711
- if (
712
- typeof key === 'object' ||
713
- typeof key === 'boolean' ||
714
- typeof key === 'number'
715
- ) {
716
- tokens.push(JSON.stringify(key), strings[i + 1]);
717
- return;
718
- }
719
- const value = getDeepProperty(ctx, key);
720
- tokens.push(JSON.stringify(value), strings[i + 1]);
721
- });
722
- const result = tokens.join('');
723
- try {
724
- return JSON.parse(result);
725
- } catch (e) {
726
- throw new Error('Invalid JSON: ' + result);
727
- }
728
- };
729
- }