@woltz/rich-domain 1.2.4 → 1.3.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.
- package/dist/aggregate-changes.d.ts +56 -14
- package/dist/aggregate-changes.d.ts.map +1 -1
- package/dist/aggregate-changes.js +102 -22
- package/dist/aggregate-changes.js.map +1 -1
- package/dist/base-entity.d.ts +1 -1
- package/dist/base-entity.d.ts.map +1 -1
- package/dist/base-entity.js +11 -8
- package/dist/base-entity.js.map +1 -1
- package/dist/change-tracker.d.ts +1 -0
- package/dist/change-tracker.d.ts.map +1 -1
- package/dist/change-tracker.js +41 -27
- package/dist/change-tracker.js.map +1 -1
- package/dist/criteria.d.ts +7 -15
- package/dist/criteria.d.ts.map +1 -1
- package/dist/criteria.js +99 -76
- package/dist/criteria.js.map +1 -1
- package/dist/entity-schema-registry.d.ts +133 -3
- package/dist/entity-schema-registry.d.ts.map +1 -1
- package/dist/entity-schema-registry.js +155 -4
- package/dist/entity-schema-registry.js.map +1 -1
- package/dist/paginated-result.d.ts +4 -4
- package/dist/paginated-result.d.ts.map +1 -1
- package/dist/paginated-result.js +6 -20
- package/dist/paginated-result.js.map +1 -1
- package/dist/types/change-tracker.d.ts +30 -0
- package/dist/types/change-tracker.d.ts.map +1 -1
- package/dist/types/criteria.d.ts +1 -4
- package/dist/types/criteria.d.ts.map +1 -1
- package/dist/types/domain.d.ts +2 -0
- package/dist/types/domain.d.ts.map +1 -1
- package/dist/types/utils.d.ts +2 -2
- package/dist/utils/helpers.d.ts +1 -0
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/utils/helpers.js +23 -0
- package/dist/utils/helpers.js.map +1 -1
- package/dist/value-object.d.ts +1 -1
- package/dist/value-object.js +1 -1
- package/package.json +17 -3
- package/src/aggregate-changes.ts +133 -24
- package/src/base-entity.ts +13 -8
- package/src/change-tracker.ts +107 -38
- package/src/criteria.ts +151 -109
- package/src/entity-schema-registry.ts +253 -6
- package/src/paginated-result.ts +10 -30
- package/src/types/change-tracker.ts +31 -0
- package/src/types/criteria.ts +1 -4
- package/src/types/domain.ts +2 -0
- package/src/types/utils.ts +2 -2
- package/src/utils/helpers.ts +28 -0
- package/src/value-object.ts +1 -1
- package/.versionrc.json +0 -21
- package/CHANGELOG.md +0 -163
- package/tests/aggregate-changes.test.ts +0 -284
- package/tests/criteria.test.ts +0 -716
- package/tests/depth/deep-tracking.test.ts +0 -554
- package/tests/domain-events.test.ts +0 -431
- package/tests/entity-equality.test.ts +0 -464
- package/tests/entity-schema-registry.test.ts +0 -382
- package/tests/entity-validation.test.ts +0 -252
- package/tests/history-tracker.spec.ts +0 -439
- package/tests/id.test.ts +0 -338
- package/tests/load-test/data.json +0 -347211
- package/tests/load-test/entities.ts +0 -97
- package/tests/load-test/generate-data.ts +0 -81
- package/tests/load-test/lead-to-domain.mapper.ts +0 -24
- package/tests/load-test/load.test.ts +0 -38
- package/tests/repository.test.ts +0 -635
- package/tests/to-json.test.ts +0 -99
- package/tests/utils.ts +0 -290
- package/tests/value-object-validation.test.ts +0 -219
- package/tests/value-objects.test.ts +0 -80
- package/tsconfig.json +0 -9
package/src/change-tracker.ts
CHANGED
|
@@ -431,9 +431,18 @@ export class ChangeTracker {
|
|
|
431
431
|
|
|
432
432
|
const { depth, parentId, parentEntity } = arrayState.metadata;
|
|
433
433
|
|
|
434
|
+
const relationField = this.extractRelationField(path);
|
|
435
|
+
|
|
434
436
|
for (const item of created) {
|
|
435
437
|
const itemEntityName = this.getEntityName(item);
|
|
436
|
-
changes.addCreate(
|
|
438
|
+
changes.addCreate(
|
|
439
|
+
itemEntityName,
|
|
440
|
+
item,
|
|
441
|
+
depth,
|
|
442
|
+
parentId,
|
|
443
|
+
parentEntity,
|
|
444
|
+
relationField
|
|
445
|
+
);
|
|
437
446
|
|
|
438
447
|
this.markNestedItemsAsCreated(item, depth, changes);
|
|
439
448
|
}
|
|
@@ -458,7 +467,15 @@ export class ChangeTracker {
|
|
|
458
467
|
if (id || key) {
|
|
459
468
|
const itemEntityName = this.getEntityName(item);
|
|
460
469
|
const deleteId = id || key!;
|
|
461
|
-
changes.addDelete(
|
|
470
|
+
changes.addDelete(
|
|
471
|
+
itemEntityName,
|
|
472
|
+
deleteId,
|
|
473
|
+
item,
|
|
474
|
+
depth,
|
|
475
|
+
relationField,
|
|
476
|
+
parentId,
|
|
477
|
+
parentEntity
|
|
478
|
+
);
|
|
462
479
|
|
|
463
480
|
this.markNestedItemsAsDeleted(item, depth, changes, rootTracker);
|
|
464
481
|
}
|
|
@@ -476,37 +493,41 @@ export class ChangeTracker {
|
|
|
476
493
|
): void {
|
|
477
494
|
if (!item || typeof item !== "object") return;
|
|
478
495
|
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
const props = item.props || item;
|
|
496
|
+
const propsToScan = item.props || item;
|
|
497
|
+
const parentId = this.getEntityId(item);
|
|
498
|
+
const parentEntity = this.getEntityName(item);
|
|
483
499
|
|
|
484
|
-
for (const [propName, value] of Object.entries(
|
|
500
|
+
for (const [propName, value] of Object.entries(propsToScan)) {
|
|
485
501
|
if (propName === "id") continue;
|
|
486
502
|
|
|
487
503
|
if (Array.isArray(value)) {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
this.markNestedItemsAsCreated(
|
|
503
|
-
nestedItem,
|
|
504
|
-
parentDepth + 1,
|
|
505
|
-
changes
|
|
506
|
-
);
|
|
507
|
-
}
|
|
504
|
+
const relationField = propName;
|
|
505
|
+
|
|
506
|
+
for (const child of value) {
|
|
507
|
+
if (this.isEntityOrVO(child)) {
|
|
508
|
+
const childEntityName = this.getEntityName(child);
|
|
509
|
+
changes.addCreate(
|
|
510
|
+
childEntityName,
|
|
511
|
+
child,
|
|
512
|
+
parentDepth + 1,
|
|
513
|
+
parentId,
|
|
514
|
+
parentEntity,
|
|
515
|
+
relationField
|
|
516
|
+
);
|
|
517
|
+
this.markNestedItemsAsCreated(child, parentDepth + 1, changes);
|
|
508
518
|
}
|
|
509
519
|
}
|
|
520
|
+
} else if (this.isEntityOrVO(value)) {
|
|
521
|
+
const childEntityName = this.getEntityName(value);
|
|
522
|
+
changes.addCreate(
|
|
523
|
+
childEntityName,
|
|
524
|
+
value,
|
|
525
|
+
parentDepth + 1,
|
|
526
|
+
parentId,
|
|
527
|
+
parentEntity,
|
|
528
|
+
propName
|
|
529
|
+
);
|
|
530
|
+
this.markNestedItemsAsCreated(value, parentDepth + 1, changes);
|
|
510
531
|
}
|
|
511
532
|
}
|
|
512
533
|
}
|
|
@@ -526,8 +547,12 @@ export class ChangeTracker {
|
|
|
526
547
|
const itemId = this.getEntityId(item);
|
|
527
548
|
if (!itemId) return;
|
|
528
549
|
|
|
529
|
-
for (const [, arrayState] of rootTracker.trackedArrays) {
|
|
550
|
+
for (const [path, arrayState] of rootTracker.trackedArrays) {
|
|
530
551
|
if (arrayState.metadata.parentId === itemId) {
|
|
552
|
+
const relationField = this.extractRelationField(path);
|
|
553
|
+
const parentEntity = arrayState.metadata.parentEntity;
|
|
554
|
+
const parentId = arrayState.metadata.parentId;
|
|
555
|
+
|
|
531
556
|
for (const nestedItem of arrayState.cloned) {
|
|
532
557
|
const id =
|
|
533
558
|
typeof nestedItem === "object" && nestedItem !== null
|
|
@@ -535,7 +560,15 @@ export class ChangeTracker {
|
|
|
535
560
|
: undefined;
|
|
536
561
|
if (id) {
|
|
537
562
|
const entityName = arrayState.metadata.entityName;
|
|
538
|
-
changes.addDelete(
|
|
563
|
+
changes.addDelete(
|
|
564
|
+
entityName,
|
|
565
|
+
id,
|
|
566
|
+
nestedItem,
|
|
567
|
+
parentDepth + 1,
|
|
568
|
+
relationField,
|
|
569
|
+
parentEntity,
|
|
570
|
+
parentId
|
|
571
|
+
);
|
|
539
572
|
|
|
540
573
|
this.markNestedJsonItemAsDeleted(
|
|
541
574
|
id,
|
|
@@ -559,21 +592,28 @@ export class ChangeTracker {
|
|
|
559
592
|
changes: AggregateChanges<any>,
|
|
560
593
|
rootTracker: ChangeTracker
|
|
561
594
|
): void {
|
|
562
|
-
for (const [, arrayState] of rootTracker.trackedArrays) {
|
|
595
|
+
for (const [path, arrayState] of rootTracker.trackedArrays) {
|
|
563
596
|
if (arrayState.metadata.parentId === itemId) {
|
|
597
|
+
const relationField = this.extractRelationField(path);
|
|
598
|
+
|
|
564
599
|
for (const nestedJsonItem of arrayState.cloned) {
|
|
565
600
|
if (typeof nestedJsonItem !== "object" || nestedJsonItem === null)
|
|
566
601
|
continue;
|
|
567
602
|
|
|
568
603
|
const nestedId = nestedJsonItem.id;
|
|
569
604
|
const entityName = arrayState.metadata.entityName;
|
|
605
|
+
const parentEntity = arrayState.metadata.parentEntity;
|
|
606
|
+
const parentId = arrayState.metadata.parentId;
|
|
570
607
|
|
|
571
608
|
if (nestedId) {
|
|
572
609
|
changes.addDelete(
|
|
573
610
|
entityName,
|
|
574
611
|
nestedId,
|
|
575
612
|
nestedJsonItem,
|
|
576
|
-
parentDepth + 1
|
|
613
|
+
parentDepth + 1,
|
|
614
|
+
relationField,
|
|
615
|
+
parentId,
|
|
616
|
+
parentEntity
|
|
577
617
|
);
|
|
578
618
|
|
|
579
619
|
this.markNestedJsonItemAsDeleted(
|
|
@@ -592,7 +632,10 @@ export class ChangeTracker {
|
|
|
592
632
|
entityName,
|
|
593
633
|
key,
|
|
594
634
|
nestedJsonItem,
|
|
595
|
-
parentDepth + 1
|
|
635
|
+
parentDepth + 1,
|
|
636
|
+
relationField,
|
|
637
|
+
parentId,
|
|
638
|
+
parentEntity
|
|
596
639
|
);
|
|
597
640
|
}
|
|
598
641
|
}
|
|
@@ -668,6 +711,8 @@ export class ChangeTracker {
|
|
|
668
711
|
const { entityName, depth, parentId, parentEntity } =
|
|
669
712
|
trackedItem.metadata;
|
|
670
713
|
|
|
714
|
+
const relationField = this.extractRelationField(path);
|
|
715
|
+
|
|
671
716
|
const state = this.detectEntityChangeState(originalValue, currentValue);
|
|
672
717
|
|
|
673
718
|
switch (state) {
|
|
@@ -677,28 +722,46 @@ export class ChangeTracker {
|
|
|
677
722
|
currentValue,
|
|
678
723
|
depth,
|
|
679
724
|
parentId,
|
|
680
|
-
parentEntity
|
|
725
|
+
parentEntity,
|
|
726
|
+
relationField
|
|
681
727
|
);
|
|
682
728
|
break;
|
|
683
729
|
|
|
684
730
|
case "deleted":
|
|
685
731
|
const id = this.getEntityId(originalValue);
|
|
686
732
|
if (id) {
|
|
687
|
-
changes.addDelete(
|
|
733
|
+
changes.addDelete(
|
|
734
|
+
entityName,
|
|
735
|
+
id,
|
|
736
|
+
originalEntity,
|
|
737
|
+
depth,
|
|
738
|
+
relationField,
|
|
739
|
+
parentId,
|
|
740
|
+
parentEntity
|
|
741
|
+
);
|
|
688
742
|
}
|
|
689
743
|
break;
|
|
690
744
|
|
|
691
745
|
case "replaced":
|
|
692
746
|
const oldId = this.getEntityId(originalValue);
|
|
693
747
|
if (oldId) {
|
|
694
|
-
changes.addDelete(
|
|
748
|
+
changes.addDelete(
|
|
749
|
+
entityName,
|
|
750
|
+
oldId,
|
|
751
|
+
originalEntity,
|
|
752
|
+
depth,
|
|
753
|
+
relationField,
|
|
754
|
+
parentId,
|
|
755
|
+
parentEntity
|
|
756
|
+
);
|
|
695
757
|
}
|
|
696
758
|
changes.addCreate(
|
|
697
759
|
entityName,
|
|
698
760
|
currentValue,
|
|
699
761
|
depth,
|
|
700
762
|
parentId,
|
|
701
|
-
parentEntity
|
|
763
|
+
parentEntity,
|
|
764
|
+
relationField
|
|
702
765
|
);
|
|
703
766
|
break;
|
|
704
767
|
|
|
@@ -894,6 +957,12 @@ export class ChangeTracker {
|
|
|
894
957
|
return current;
|
|
895
958
|
}
|
|
896
959
|
|
|
960
|
+
private extractRelationField(path: string): string {
|
|
961
|
+
const withoutIndices = path.replace(/\[\d+\]/g, "");
|
|
962
|
+
const parts = withoutIndices.split(".");
|
|
963
|
+
return parts[parts.length - 1];
|
|
964
|
+
}
|
|
965
|
+
|
|
897
966
|
private getItemKey(item: any): string | undefined {
|
|
898
967
|
const id = this.getEntityId(item);
|
|
899
968
|
if (id) return id;
|
|
@@ -998,8 +1067,8 @@ export class ChangeTracker {
|
|
|
998
1067
|
return obj.value;
|
|
999
1068
|
}
|
|
1000
1069
|
|
|
1001
|
-
if (typeof obj.
|
|
1002
|
-
return obj.
|
|
1070
|
+
if (typeof obj.toJSON === "function") {
|
|
1071
|
+
return obj.toJSON();
|
|
1003
1072
|
}
|
|
1004
1073
|
|
|
1005
1074
|
if (Array.isArray(obj)) {
|
package/src/criteria.ts
CHANGED
|
@@ -27,7 +27,7 @@ export class Criteria<T = any> {
|
|
|
27
27
|
private _filters: Filter<FieldPath<T>, any>[] = [];
|
|
28
28
|
private _orders: Order[] = [];
|
|
29
29
|
private _pagination: Pagination = { page: 1, limit: 20, offset: 0 };
|
|
30
|
-
private _search?: Search
|
|
30
|
+
private _search?: Search;
|
|
31
31
|
private _adapter?: CriteriaAdapter<any, any>;
|
|
32
32
|
|
|
33
33
|
private constructor() {}
|
|
@@ -155,11 +155,8 @@ export class Criteria<T = any> {
|
|
|
155
155
|
return this.orderBy(field, "desc");
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
search
|
|
159
|
-
this._search =
|
|
160
|
-
fields: fields.map(this.resolveFieldPath),
|
|
161
|
-
value,
|
|
162
|
-
};
|
|
158
|
+
search(value: string): this {
|
|
159
|
+
this._search = value;
|
|
163
160
|
return this;
|
|
164
161
|
}
|
|
165
162
|
|
|
@@ -167,13 +164,8 @@ export class Criteria<T = any> {
|
|
|
167
164
|
return !!this._search;
|
|
168
165
|
}
|
|
169
166
|
|
|
170
|
-
getSearch() {
|
|
171
|
-
return this._search
|
|
172
|
-
? {
|
|
173
|
-
fields: this._search.fields.map(this.resolveFieldPath),
|
|
174
|
-
value: this._search.value,
|
|
175
|
-
}
|
|
176
|
-
: undefined;
|
|
167
|
+
getSearch(): Search | undefined {
|
|
168
|
+
return this._search;
|
|
177
169
|
}
|
|
178
170
|
|
|
179
171
|
paginate(page: number, limit: number): this {
|
|
@@ -241,12 +233,7 @@ export class Criteria<T = any> {
|
|
|
241
233
|
})),
|
|
242
234
|
];
|
|
243
235
|
cloned._pagination = { ...this._pagination };
|
|
244
|
-
cloned._search = this._search
|
|
245
|
-
? {
|
|
246
|
-
fields: this._search.fields.map(this.resolveFieldPath),
|
|
247
|
-
value: this._search.value,
|
|
248
|
-
}
|
|
249
|
-
: undefined;
|
|
236
|
+
cloned._search = this._search;
|
|
250
237
|
|
|
251
238
|
if (this._adapter) {
|
|
252
239
|
cloned.useAdapter(this._adapter);
|
|
@@ -268,12 +255,7 @@ export class Criteria<T = any> {
|
|
|
268
255
|
direction: order.direction,
|
|
269
256
|
})),
|
|
270
257
|
pagination: this._pagination,
|
|
271
|
-
search: this._search
|
|
272
|
-
? {
|
|
273
|
-
fields: this._search.fields.map(this.resolveFieldPath),
|
|
274
|
-
value: this._search.value,
|
|
275
|
-
}
|
|
276
|
-
: undefined,
|
|
258
|
+
search: this._search,
|
|
277
259
|
};
|
|
278
260
|
}
|
|
279
261
|
|
|
@@ -282,7 +264,7 @@ export class Criteria<T = any> {
|
|
|
282
264
|
filters?: TypedFilter<T>[];
|
|
283
265
|
orders?: TypedOrder<T>[];
|
|
284
266
|
pagination?: Pagination;
|
|
285
|
-
search?:
|
|
267
|
+
search?: Search;
|
|
286
268
|
},
|
|
287
269
|
adapter?: CriteriaAdapter<any, any>
|
|
288
270
|
): Criteria<T> {
|
|
@@ -307,11 +289,7 @@ export class Criteria<T = any> {
|
|
|
307
289
|
})),
|
|
308
290
|
];
|
|
309
291
|
if (obj.pagination) criteria._pagination = { ...obj.pagination };
|
|
310
|
-
if (obj.search)
|
|
311
|
-
criteria._search = {
|
|
312
|
-
...obj.search,
|
|
313
|
-
fields: obj.search.fields.map(criteria.resolveFieldPath),
|
|
314
|
-
};
|
|
292
|
+
if (obj.search) criteria._search = obj.search;
|
|
315
293
|
|
|
316
294
|
return criteria;
|
|
317
295
|
}
|
|
@@ -337,10 +315,12 @@ export class Criteria<T = any> {
|
|
|
337
315
|
return field;
|
|
338
316
|
}
|
|
339
317
|
|
|
340
|
-
static fromQueryParams<T>(
|
|
341
|
-
query: Record<string, any
|
|
318
|
+
static fromQueryParams<T = any>(
|
|
319
|
+
query: Record<string, any> | undefined,
|
|
342
320
|
adapter?: CriteriaAdapter<any, any>
|
|
343
321
|
): Criteria<T> {
|
|
322
|
+
if (!query) return Criteria.create<T>();
|
|
323
|
+
|
|
344
324
|
const criteria = Criteria.create<T>();
|
|
345
325
|
|
|
346
326
|
if (adapter) {
|
|
@@ -354,80 +334,95 @@ export class Criteria<T = any> {
|
|
|
354
334
|
if (key === "limit") {
|
|
355
335
|
continue;
|
|
356
336
|
}
|
|
357
|
-
if (key === "sort") {
|
|
358
|
-
continue;
|
|
359
|
-
}
|
|
360
337
|
|
|
361
|
-
|
|
338
|
+
if (key === "filters") {
|
|
339
|
+
const filters: Record<string, any> = criteria.parseFilterValue(value);
|
|
362
340
|
|
|
363
|
-
|
|
341
|
+
for (let [filterKey, filterValue] of Object.entries(filters)) {
|
|
342
|
+
const [field, operatorWithQuantifier] = filterKey.split(":");
|
|
364
343
|
|
|
365
|
-
|
|
366
|
-
const operator = isOperator(operatorRaw) ? operatorRaw : null;
|
|
367
|
-
if (!operator) {
|
|
368
|
-
throw new InvalidCriteriaError(`Invalid filter operator`, operatorRaw);
|
|
369
|
-
}
|
|
344
|
+
if (!operatorWithQuantifier || !field) continue;
|
|
370
345
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
quantifierRaw
|
|
381
|
-
);
|
|
382
|
-
}
|
|
346
|
+
const [operatorRaw, quantifierRaw] =
|
|
347
|
+
operatorWithQuantifier.split("@");
|
|
348
|
+
const operator = isOperator(operatorRaw) ? operatorRaw : null;
|
|
349
|
+
if (!operator) {
|
|
350
|
+
throw new InvalidCriteriaError(
|
|
351
|
+
`Invalid filter operator`,
|
|
352
|
+
operatorRaw
|
|
353
|
+
);
|
|
354
|
+
}
|
|
383
355
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
356
|
+
const validQuantifiers = ["some", "every", "none"];
|
|
357
|
+
const quantifier =
|
|
358
|
+
quantifierRaw && validQuantifiers.includes(quantifierRaw)
|
|
359
|
+
? (quantifierRaw as CriteriaOptions["quantifier"])
|
|
360
|
+
: undefined;
|
|
361
|
+
|
|
362
|
+
if (quantifierRaw && !quantifier) {
|
|
363
|
+
throw new InvalidCriteriaError(
|
|
364
|
+
`Invalid quantifier. Valid values: ${validQuantifiers.join(
|
|
365
|
+
", "
|
|
366
|
+
)}`,
|
|
367
|
+
quantifierRaw
|
|
368
|
+
);
|
|
369
|
+
}
|
|
387
370
|
|
|
388
|
-
|
|
371
|
+
const options: CriteriaOptions | undefined = quantifier
|
|
372
|
+
? { quantifier }
|
|
373
|
+
: undefined;
|
|
389
374
|
|
|
390
|
-
|
|
375
|
+
let parsedValue: any = filterValue;
|
|
391
376
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
.split(",")
|
|
395
|
-
.map((v: any) => parseQueryValue(v.trim()));
|
|
396
|
-
if (parsedValue.length === 2) {
|
|
397
|
-
criteria.where(
|
|
398
|
-
resolvedField,
|
|
399
|
-
"between" as OperatorsForType<PathValue<T, FieldPath<T>>>,
|
|
400
|
-
[parsedValue[0], parsedValue[1]] as [
|
|
401
|
-
PathValue<T, FieldPath<T>>,
|
|
402
|
-
PathValue<T, FieldPath<T>>
|
|
403
|
-
],
|
|
404
|
-
options
|
|
377
|
+
const resolvedField = criteria.resolveFieldPath(
|
|
378
|
+
field as FieldPath<T>
|
|
405
379
|
);
|
|
406
|
-
}
|
|
407
|
-
continue;
|
|
408
|
-
}
|
|
409
380
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
381
|
+
if (operator === "between") {
|
|
382
|
+
parsedValue = criteria
|
|
383
|
+
.parseFilterValue(filterValue)
|
|
384
|
+
.map((v: any) => parseQueryValue(v.trim()));
|
|
385
|
+
|
|
386
|
+
if (parsedValue.length === 2) {
|
|
387
|
+
criteria.where(
|
|
388
|
+
resolvedField,
|
|
389
|
+
"between" as OperatorsForType<PathValue<T, FieldPath<T>>>,
|
|
390
|
+
[parsedValue[0], parsedValue[1]] as [
|
|
391
|
+
PathValue<T, FieldPath<T>>,
|
|
392
|
+
PathValue<T, FieldPath<T>>
|
|
393
|
+
],
|
|
394
|
+
options
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
420
399
|
|
|
421
|
-
|
|
400
|
+
if (operator === "in" || operator === "notIn") {
|
|
401
|
+
parsedValue = criteria
|
|
402
|
+
.parseFilterValue(filterValue)
|
|
403
|
+
.map(parseQueryValue);
|
|
404
|
+
|
|
405
|
+
criteria.where(
|
|
406
|
+
field as any,
|
|
407
|
+
operator as OperatorsForType<PathValue<T, FieldPath<T>>>,
|
|
408
|
+
parsedValue,
|
|
409
|
+
options
|
|
410
|
+
);
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
422
413
|
|
|
423
|
-
|
|
414
|
+
const parsedFinalValue = parseQueryValue(filterValue);
|
|
424
415
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
416
|
+
criteria.validateOperator(operator, parsedFinalValue);
|
|
417
|
+
|
|
418
|
+
criteria.where(
|
|
419
|
+
field as FieldPath<T>,
|
|
420
|
+
operator as OperatorsForType<PathValue<T, FieldPath<T>>>,
|
|
421
|
+
parsedFinalValue,
|
|
422
|
+
options
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
431
426
|
}
|
|
432
427
|
|
|
433
428
|
const page = query.page ? parseInt(query.page) : undefined;
|
|
@@ -437,24 +432,60 @@ export class Criteria<T = any> {
|
|
|
437
432
|
criteria.paginate(page, limit);
|
|
438
433
|
}
|
|
439
434
|
|
|
435
|
+
// 1. orderBy=["field:asc","field2:desc"]
|
|
440
436
|
if (query.orderBy) {
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
437
|
+
const orderByValue = query.orderBy;
|
|
438
|
+
|
|
439
|
+
if (
|
|
440
|
+
typeof orderByValue === "string" &&
|
|
441
|
+
orderByValue.trim().startsWith("[")
|
|
442
|
+
) {
|
|
443
|
+
try {
|
|
444
|
+
const orderArray = JSON.parse(orderByValue);
|
|
445
|
+
if (Array.isArray(orderArray)) {
|
|
446
|
+
orderArray.forEach((item: string) => {
|
|
447
|
+
const [field, direction] = item.split(":");
|
|
448
|
+
criteria.orderBy(
|
|
449
|
+
field as FieldPath<T>,
|
|
450
|
+
(direction as OrderDirection) || "asc"
|
|
451
|
+
);
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
} catch {
|
|
455
|
+
throw new InvalidCriteriaError(
|
|
456
|
+
"Invalid JSON array format for orderBy",
|
|
457
|
+
orderByValue
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
} else if (Array.isArray(orderByValue)) {
|
|
461
|
+
orderByValue.forEach((item: string) => {
|
|
462
|
+
const [field, direction] = item.split(":");
|
|
463
|
+
criteria.orderBy(
|
|
464
|
+
field as FieldPath<T>,
|
|
465
|
+
(direction as OrderDirection) || "asc"
|
|
466
|
+
);
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
// 2. orderBy="field:asc,field2:desc"
|
|
470
|
+
else if (typeof orderByValue === "string" && orderByValue.includes(":")) {
|
|
471
|
+
const sortParts = orderByValue.split(",");
|
|
472
|
+
sortParts.forEach((part: string) => {
|
|
473
|
+
const [field, direction] = part.split(":");
|
|
474
|
+
criteria.orderBy(
|
|
475
|
+
field as FieldPath<T>,
|
|
476
|
+
(direction as OrderDirection) || "asc"
|
|
477
|
+
);
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
// 3. orderBy="field" + orderDirection="asc"
|
|
481
|
+
else {
|
|
482
|
+
const direction = (query.orderDirection as OrderDirection) || "asc";
|
|
483
|
+
criteria.orderBy(orderByValue as FieldPath<T>, direction);
|
|
484
|
+
}
|
|
449
485
|
}
|
|
450
486
|
|
|
451
|
-
if (query.search && query.
|
|
452
|
-
|
|
453
|
-
.split(",")
|
|
454
|
-
.filter(Boolean) as FieldPath<T>[];
|
|
455
|
-
|
|
456
|
-
const resolvedFields = fields.map(criteria.resolveFieldPath);
|
|
457
|
-
criteria.search(resolvedFields, query.search as string);
|
|
487
|
+
if (query.search && typeof query.search === "string") {
|
|
488
|
+
criteria.search(query.search);
|
|
458
489
|
}
|
|
459
490
|
|
|
460
491
|
return criteria;
|
|
@@ -476,4 +507,15 @@ export class Criteria<T = any> {
|
|
|
476
507
|
);
|
|
477
508
|
}
|
|
478
509
|
}
|
|
510
|
+
|
|
511
|
+
private parseFilterValue(value: any) {
|
|
512
|
+
if (typeof value === "string") {
|
|
513
|
+
try {
|
|
514
|
+
return JSON.parse(value);
|
|
515
|
+
} catch {
|
|
516
|
+
throw new InvalidCriteriaError(`Invalid filter value`, value);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return parseQueryValue(value);
|
|
520
|
+
}
|
|
479
521
|
}
|