@woltz/rich-domain 1.0.0 → 1.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.
- package/CHANGELOG.md +37 -86
- package/LICENSE +20 -20
- package/dist/base-entity.d.ts +1 -1
- package/dist/base-entity.d.ts.map +1 -1
- package/dist/base-entity.js +15 -19
- package/dist/base-entity.js.map +1 -1
- package/dist/constants.js +1 -4
- package/dist/constants.js.map +1 -1
- package/dist/criteria.d.ts +24 -14
- package/dist/criteria.d.ts.map +1 -1
- package/dist/criteria.js +154 -67
- package/dist/criteria.js.map +1 -1
- package/dist/deep-proxy.d.ts.map +1 -1
- package/dist/deep-proxy.js +19 -9
- package/dist/deep-proxy.js.map +1 -1
- package/dist/domain-event-bus.d.ts +5 -6
- package/dist/domain-event-bus.d.ts.map +1 -1
- package/dist/domain-event-bus.js +4 -17
- package/dist/domain-event-bus.js.map +1 -1
- package/dist/domain-event.d.ts +1 -31
- package/dist/domain-event.d.ts.map +1 -1
- package/dist/domain-event.js +4 -7
- package/dist/domain-event.js.map +1 -1
- package/dist/entity.d.ts +2 -2
- package/dist/entity.js +3 -8
- package/dist/entity.js.map +1 -1
- package/dist/exceptions.d.ts +251 -0
- package/dist/exceptions.d.ts.map +1 -0
- package/dist/exceptions.js +321 -0
- package/dist/exceptions.js.map +1 -0
- package/dist/id.d.ts.map +1 -1
- package/dist/id.js +14 -7
- package/dist/id.js.map +1 -1
- package/dist/index.d.ts +2 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -40
- package/dist/index.js.map +1 -1
- package/dist/mapper.js +1 -5
- package/dist/mapper.js.map +1 -1
- package/dist/paginated-result.d.ts.map +1 -1
- package/dist/paginated-result.js +17 -9
- package/dist/paginated-result.js.map +1 -1
- package/dist/repository/base-repository.js +4 -11
- package/dist/repository/base-repository.js.map +1 -1
- package/dist/repository/index.d.ts +1 -2
- package/dist/repository/index.d.ts.map +1 -1
- package/dist/repository/index.js +3 -26
- package/dist/repository/index.js.map +1 -1
- package/dist/repository/unit-of-work.d.ts +0 -11
- package/dist/repository/unit-of-work.d.ts.map +1 -1
- package/dist/repository/unit-of-work.js +2 -43
- package/dist/repository/unit-of-work.js.map +1 -1
- package/dist/types/criteria.d.ts +31 -7
- package/dist/types/criteria.d.ts.map +1 -1
- package/dist/types/criteria.js +1 -4
- package/dist/types/criteria.js.map +1 -1
- package/dist/types/domain-event.d.ts +32 -0
- package/dist/types/domain-event.d.ts.map +1 -0
- package/dist/types/domain-event.js +2 -0
- package/dist/types/domain-event.js.map +1 -0
- package/dist/types/domain.d.ts +2 -2
- package/dist/types/domain.d.ts.map +1 -1
- package/dist/types/domain.js +1 -2
- package/dist/types/history-tracker.d.ts +1 -1
- package/dist/types/history-tracker.d.ts.map +1 -1
- package/dist/types/history-tracker.js +1 -2
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +7 -22
- package/dist/types/index.js.map +1 -1
- package/dist/types/standard-schema.js +1 -2
- package/dist/types/unit-of-work.js +1 -2
- package/dist/types/utils.js +1 -2
- package/dist/utils/criteria-operator-validation.d.ts +5 -0
- package/dist/utils/criteria-operator-validation.d.ts.map +1 -0
- package/dist/utils/criteria-operator-validation.js +143 -0
- package/dist/utils/criteria-operator-validation.js.map +1 -0
- package/dist/utils/helpers.d.ts +2 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +10 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/validation-error.js +3 -9
- package/dist/validation-error.js.map +1 -1
- package/dist/value-object.d.ts +1 -1
- package/dist/value-object.d.ts.map +1 -1
- package/dist/value-object.js +7 -14
- package/dist/value-object.js.map +1 -1
- package/eslint.config.js +9 -3
- package/jest.config.js +1 -1
- package/package.json +14 -20
- package/src/base-entity.ts +3 -3
- package/src/criteria.ts +268 -87
- package/src/deep-proxy.ts +50 -38
- package/src/domain-event-bus.ts +152 -166
- package/src/domain-event.ts +53 -90
- package/src/entity.ts +16 -16
- package/src/exceptions.ts +435 -0
- package/src/id.ts +107 -94
- package/src/index.ts +32 -8
- package/src/paginated-result.ts +15 -3
- package/src/repository/index.ts +1 -6
- package/src/repository/unit-of-work.ts +1 -44
- package/src/types/criteria.ts +95 -17
- package/src/types/domain-event.ts +38 -0
- package/src/types/domain.ts +2 -3
- package/src/types/history-tracker.ts +1 -1
- package/src/types/index.ts +1 -0
- package/src/utils/criteria-operator-validation.ts +171 -0
- package/src/utils/helpers.ts +6 -0
- package/src/validation-error.ts +97 -97
- package/src/value-object.ts +3 -6
- package/tests/criteria.test.ts +324 -1
- package/tests/domain-events.test.ts +431 -445
- package/tests/entity-validation.test.ts +1 -1
- package/tests/entity.test.ts +33 -33
- package/tests/repository.test.ts +4 -2
- package/tests/utils.ts +254 -151
- package/tests/value-object-validation.test.ts +0 -9
- package/tsconfig.json +2 -24
- package/.github/workflows/ci.yml +0 -40
- package/.husky/commit-msg +0 -1
- package/.husky/pre-commit +0 -1
- package/.vscode/settings.json +0 -3
- package/commitlint.config.js +0 -23
- package/dist/repository/in-memory-repository.d.ts +0 -50
- package/dist/repository/in-memory-repository.d.ts.map +0 -1
- package/dist/repository/in-memory-repository.js +0 -97
- package/dist/repository/in-memory-repository.js.map +0 -1
- package/src/repository/in-memory-repository.ts +0 -116
package/src/criteria.ts
CHANGED
|
@@ -1,69 +1,93 @@
|
|
|
1
|
+
import { InvalidCriteriaError } from "./exceptions";
|
|
1
2
|
import {
|
|
3
|
+
CriteriaAdapter,
|
|
4
|
+
CriteriaOptions,
|
|
2
5
|
FieldPath,
|
|
3
6
|
Filter,
|
|
4
|
-
FILTER_OPERATORS,
|
|
5
7
|
FilterOperator,
|
|
6
8
|
FilterValueFor,
|
|
9
|
+
OperatorsForType,
|
|
7
10
|
Order,
|
|
8
11
|
OrderDirection,
|
|
9
12
|
Pagination,
|
|
10
13
|
PathValue,
|
|
14
|
+
Search,
|
|
11
15
|
TypedFilter,
|
|
12
16
|
} from "./types";
|
|
17
|
+
import {
|
|
18
|
+
isValidOperatorForType,
|
|
19
|
+
getValidOperatorsForType,
|
|
20
|
+
isOperator,
|
|
21
|
+
} from "./utils/criteria-operator-validation";
|
|
22
|
+
import { parseQueryValue } from "./utils/helpers";
|
|
13
23
|
|
|
14
|
-
|
|
15
|
-
// Filter Types
|
|
16
|
-
// ============================================================================
|
|
17
|
-
|
|
18
|
-
export class Criteria<T = unknown> {
|
|
24
|
+
export class Criteria<T = any> {
|
|
19
25
|
private _filters: Filter<FieldPath<T>, any>[] = [];
|
|
20
26
|
private _orders: Order[] = [];
|
|
21
27
|
private _pagination: Pagination = { page: 1, limit: 20, offset: 0 };
|
|
22
|
-
private _search?:
|
|
23
|
-
|
|
24
|
-
value: string;
|
|
25
|
-
};
|
|
28
|
+
private _search?: Search<T>;
|
|
29
|
+
private _adapter?: CriteriaAdapter<any, any>;
|
|
26
30
|
|
|
27
31
|
private constructor() {}
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
* Create a new Criteria instance
|
|
31
|
-
*/
|
|
32
|
-
static create<T = unknown>(): Criteria<T> {
|
|
33
|
+
static create<T = any>(): Criteria<T> {
|
|
33
34
|
return new Criteria<T>();
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
useAdapter<A extends CriteriaAdapter<any, any>>(map: A): this {
|
|
38
|
+
this._adapter = map;
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getAdapter(): CriteriaAdapter<any, any> | undefined {
|
|
43
|
+
return this._adapter;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
where<K extends FieldPath<T>>(
|
|
47
|
+
field: K,
|
|
48
|
+
operator: OperatorsForType<NonNullable<PathValue<T, K>>>,
|
|
49
|
+
value?: FilterValueFor<PathValue<T, K>>,
|
|
50
|
+
options?: CriteriaOptions
|
|
51
|
+
): this;
|
|
52
|
+
|
|
39
53
|
where<K extends FieldPath<T>>(
|
|
40
54
|
field: K,
|
|
41
55
|
operator: FilterOperator,
|
|
42
|
-
value?: FilterValueFor<PathValue<T, K
|
|
56
|
+
value?: FilterValueFor<PathValue<T, K>>,
|
|
57
|
+
options?: CriteriaOptions
|
|
43
58
|
): this {
|
|
59
|
+
this.validateOperator(operator, value);
|
|
60
|
+
|
|
44
61
|
this._filters.push({
|
|
45
|
-
field,
|
|
62
|
+
field: this.resolveFieldPath(field),
|
|
46
63
|
operator,
|
|
47
64
|
value,
|
|
65
|
+
options,
|
|
48
66
|
});
|
|
49
67
|
return this;
|
|
50
68
|
}
|
|
51
69
|
|
|
52
|
-
// === Shorthand methods (tipados) ===
|
|
53
|
-
|
|
54
70
|
whereEquals<K extends FieldPath<T>>(field: K, value: PathValue<T, K>): this {
|
|
55
|
-
return this.where(
|
|
71
|
+
return this.where(
|
|
72
|
+
field,
|
|
73
|
+
"equals" as OperatorsForType<PathValue<T, K>>,
|
|
74
|
+
value
|
|
75
|
+
);
|
|
56
76
|
}
|
|
57
77
|
|
|
58
78
|
whereContains<K extends FieldPath<T>>(
|
|
59
79
|
field: K,
|
|
60
80
|
value: PathValue<T, K>
|
|
61
81
|
): this {
|
|
62
|
-
return this.where(
|
|
82
|
+
return this.where(
|
|
83
|
+
field,
|
|
84
|
+
"contains" as OperatorsForType<PathValue<T, K>>,
|
|
85
|
+
value
|
|
86
|
+
);
|
|
63
87
|
}
|
|
64
88
|
|
|
65
89
|
whereIn<K extends FieldPath<T>>(field: K, values: PathValue<T, K>[]): this {
|
|
66
|
-
return this.where(field, "in", values);
|
|
90
|
+
return this.where(field, "in" as OperatorsForType<PathValue<T, K>>, values);
|
|
67
91
|
}
|
|
68
92
|
|
|
69
93
|
whereBetween<K extends FieldPath<T>>(
|
|
@@ -71,28 +95,51 @@ export class Criteria<T = unknown> {
|
|
|
71
95
|
min: PathValue<T, K>,
|
|
72
96
|
max: PathValue<T, K>
|
|
73
97
|
): this {
|
|
74
|
-
return this.where(
|
|
75
|
-
|
|
76
|
-
PathValue<T, K
|
|
77
|
-
|
|
98
|
+
return this.where(
|
|
99
|
+
field,
|
|
100
|
+
"between" as OperatorsForType<PathValue<T, K>>,
|
|
101
|
+
[min, max] as [PathValue<T, K>, PathValue<T, K>]
|
|
102
|
+
);
|
|
78
103
|
}
|
|
79
104
|
|
|
80
105
|
whereNull<K extends FieldPath<T>>(field: K): this {
|
|
81
|
-
return this.where(field, "isNull");
|
|
106
|
+
return this.where(field, "isNull" as OperatorsForType<PathValue<T, K>>);
|
|
82
107
|
}
|
|
83
108
|
|
|
84
109
|
whereNotNull<K extends FieldPath<T>>(field: K): this {
|
|
85
|
-
return this.where(field, "isNotNull");
|
|
110
|
+
return this.where(field, "isNotNull" as OperatorsForType<PathValue<T, K>>);
|
|
86
111
|
}
|
|
87
112
|
|
|
88
|
-
|
|
113
|
+
whereSome<K extends FieldPath<T>>(
|
|
114
|
+
field: K,
|
|
115
|
+
operator: OperatorsForType<NonNullable<PathValue<T, K>>>,
|
|
116
|
+
value?: FilterValueFor<PathValue<T, K>>
|
|
117
|
+
): this {
|
|
118
|
+
return this.where(field, operator, value, { quantifier: "some" });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
whereEvery<K extends FieldPath<T>>(
|
|
122
|
+
field: K,
|
|
123
|
+
operator: OperatorsForType<NonNullable<PathValue<T, K>>>,
|
|
124
|
+
value?: FilterValueFor<PathValue<T, K>>
|
|
125
|
+
): this {
|
|
126
|
+
return this.where(field, operator, value, { quantifier: "every" });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
whereNone<K extends FieldPath<T>>(
|
|
130
|
+
field: K,
|
|
131
|
+
operator: OperatorsForType<NonNullable<PathValue<T, K>>>,
|
|
132
|
+
value?: FilterValueFor<PathValue<T, K>>
|
|
133
|
+
): this {
|
|
134
|
+
return this.where(field, operator, value, { quantifier: "none" });
|
|
135
|
+
}
|
|
89
136
|
|
|
90
137
|
orderBy<K extends FieldPath<T>>(
|
|
91
138
|
field: K,
|
|
92
139
|
direction: OrderDirection = "asc"
|
|
93
140
|
): this {
|
|
94
141
|
this._orders.push({
|
|
95
|
-
field:
|
|
142
|
+
field: this.resolveFieldPath(field),
|
|
96
143
|
direction,
|
|
97
144
|
});
|
|
98
145
|
return this;
|
|
@@ -106,13 +153,9 @@ export class Criteria<T = unknown> {
|
|
|
106
153
|
return this.orderBy(field, "desc");
|
|
107
154
|
}
|
|
108
155
|
|
|
109
|
-
// --------------------------------------------------------------------------
|
|
110
|
-
// Search (tipado)
|
|
111
|
-
// --------------------------------------------------------------------------
|
|
112
|
-
|
|
113
156
|
search<K extends FieldPath<T>>(fields: K[], value: string): this {
|
|
114
157
|
this._search = {
|
|
115
|
-
fields,
|
|
158
|
+
fields: fields.map(this.resolveFieldPath),
|
|
116
159
|
value,
|
|
117
160
|
};
|
|
118
161
|
return this;
|
|
@@ -123,11 +166,14 @@ export class Criteria<T = unknown> {
|
|
|
123
166
|
}
|
|
124
167
|
|
|
125
168
|
getSearch() {
|
|
126
|
-
return this._search
|
|
169
|
+
return this._search
|
|
170
|
+
? {
|
|
171
|
+
fields: this._search.fields.map(this.resolveFieldPath),
|
|
172
|
+
value: this._search.value,
|
|
173
|
+
}
|
|
174
|
+
: undefined;
|
|
127
175
|
}
|
|
128
176
|
|
|
129
|
-
// === Pagination ===
|
|
130
|
-
|
|
131
177
|
paginate(page: number, limit: number): this {
|
|
132
178
|
if (page < 1) page = 1;
|
|
133
179
|
if (limit < 1) limit = 10;
|
|
@@ -144,14 +190,20 @@ export class Criteria<T = unknown> {
|
|
|
144
190
|
return this.paginate(1, limit);
|
|
145
191
|
}
|
|
146
192
|
|
|
147
|
-
// === Getters ===
|
|
148
|
-
|
|
149
193
|
getFilters(): Filter[] {
|
|
150
|
-
return this._filters
|
|
194
|
+
return this._filters.map((filter) => ({
|
|
195
|
+
field: this.resolveFieldPath(filter.field),
|
|
196
|
+
operator: filter.operator,
|
|
197
|
+
value: filter.value,
|
|
198
|
+
options: filter.options,
|
|
199
|
+
}));
|
|
151
200
|
}
|
|
152
201
|
|
|
153
202
|
getOrders(): Order[] {
|
|
154
|
-
return this._orders
|
|
203
|
+
return this._orders.map((order) => ({
|
|
204
|
+
field: this.resolveFieldPath(order.field as FieldPath<T>),
|
|
205
|
+
direction: order.direction,
|
|
206
|
+
}));
|
|
155
207
|
}
|
|
156
208
|
|
|
157
209
|
getPagination(): Pagination {
|
|
@@ -170,47 +222,132 @@ export class Criteria<T = unknown> {
|
|
|
170
222
|
return this._pagination !== undefined;
|
|
171
223
|
}
|
|
172
224
|
|
|
173
|
-
// === Utilities ===
|
|
174
|
-
|
|
175
225
|
clone(): Criteria<T> {
|
|
176
226
|
const cloned = Criteria.create<T>();
|
|
177
|
-
cloned._filters = [
|
|
178
|
-
|
|
227
|
+
cloned._filters = [
|
|
228
|
+
...this._filters.map((filter) => ({
|
|
229
|
+
field: this.resolveFieldPath(filter.field),
|
|
230
|
+
operator: filter.operator,
|
|
231
|
+
value: filter.value,
|
|
232
|
+
options: filter.options,
|
|
233
|
+
})),
|
|
234
|
+
];
|
|
235
|
+
cloned._orders = [
|
|
236
|
+
...this._orders.map((order) => ({
|
|
237
|
+
field: this.resolveFieldPath(order.field as FieldPath<T>),
|
|
238
|
+
direction: order.direction,
|
|
239
|
+
})),
|
|
240
|
+
];
|
|
179
241
|
cloned._pagination = { ...this._pagination };
|
|
242
|
+
cloned._search = this._search
|
|
243
|
+
? {
|
|
244
|
+
fields: this._search.fields.map(this.resolveFieldPath),
|
|
245
|
+
value: this._search.value,
|
|
246
|
+
}
|
|
247
|
+
: undefined;
|
|
248
|
+
|
|
249
|
+
if (this._adapter) {
|
|
250
|
+
cloned.useAdapter(this._adapter);
|
|
251
|
+
}
|
|
252
|
+
|
|
180
253
|
return cloned;
|
|
181
254
|
}
|
|
182
255
|
|
|
183
256
|
toJSON() {
|
|
184
257
|
return {
|
|
185
|
-
filters: this._filters
|
|
186
|
-
|
|
258
|
+
filters: this._filters.map((filter) => ({
|
|
259
|
+
field: this.resolveFieldPath(filter.field),
|
|
260
|
+
operator: filter.operator,
|
|
261
|
+
value: filter.value,
|
|
262
|
+
options: filter.options,
|
|
263
|
+
})),
|
|
264
|
+
orders: this._orders.map((order) => ({
|
|
265
|
+
field: this.resolveFieldPath(order.field as FieldPath<T>),
|
|
266
|
+
direction: order.direction,
|
|
267
|
+
})),
|
|
187
268
|
pagination: this._pagination,
|
|
188
|
-
search: this._search
|
|
269
|
+
search: this._search
|
|
270
|
+
? {
|
|
271
|
+
fields: this._search.fields.map(this.resolveFieldPath),
|
|
272
|
+
value: this._search.value,
|
|
273
|
+
}
|
|
274
|
+
: undefined,
|
|
189
275
|
};
|
|
190
276
|
}
|
|
191
277
|
|
|
192
|
-
static fromObject<T>(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
278
|
+
static fromObject<T>(
|
|
279
|
+
obj: {
|
|
280
|
+
filters?: TypedFilter<T>[];
|
|
281
|
+
orders?: Order[];
|
|
282
|
+
pagination?: Pagination;
|
|
283
|
+
search?: { fields: FieldPath<T>[]; value: string };
|
|
284
|
+
},
|
|
285
|
+
adapter?: CriteriaAdapter<any, any>
|
|
286
|
+
): Criteria<T> {
|
|
198
287
|
const criteria = Criteria.create<T>();
|
|
199
|
-
|
|
200
|
-
if (
|
|
288
|
+
|
|
289
|
+
if (adapter) {
|
|
290
|
+
criteria.useAdapter(adapter);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (obj.filters) {
|
|
294
|
+
for (const filter of obj.filters) {
|
|
295
|
+
filter.field = criteria.resolveFieldPath(filter.field);
|
|
296
|
+
criteria.validateOperator(filter.operator, filter.value);
|
|
297
|
+
}
|
|
298
|
+
criteria._filters = [...obj.filters];
|
|
299
|
+
}
|
|
300
|
+
if (obj.orders)
|
|
301
|
+
criteria._orders = [
|
|
302
|
+
...obj.orders.map((order) => ({
|
|
303
|
+
field: criteria.resolveFieldPath(order.field as FieldPath<T>),
|
|
304
|
+
direction: order.direction,
|
|
305
|
+
})),
|
|
306
|
+
];
|
|
201
307
|
if (obj.pagination) criteria._pagination = { ...obj.pagination };
|
|
202
|
-
if (obj.search)
|
|
308
|
+
if (obj.search)
|
|
309
|
+
criteria._search = {
|
|
310
|
+
...obj.search,
|
|
311
|
+
fields: obj.search.fields.map(criteria.resolveFieldPath),
|
|
312
|
+
};
|
|
203
313
|
|
|
204
314
|
return criteria;
|
|
205
315
|
}
|
|
206
316
|
|
|
207
|
-
|
|
317
|
+
protected resolveFieldPath(field: FieldPath<T>): FieldPath<T> {
|
|
318
|
+
if (!this?._adapter) return field;
|
|
319
|
+
|
|
320
|
+
if (this._adapter[field]) {
|
|
321
|
+
return this._adapter[field] as FieldPath<T>;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const parts = field.split(".");
|
|
325
|
+
for (let i = parts.length; i > 0; i--) {
|
|
326
|
+
const prefix = parts.slice(0, i).join(".");
|
|
327
|
+
if (this._adapter[prefix]) {
|
|
328
|
+
const rest = parts.slice(i).join(".");
|
|
329
|
+
return rest
|
|
330
|
+
? (`${this._adapter[prefix]}.${rest}` as FieldPath<T>)
|
|
331
|
+
: (this._adapter[prefix] as FieldPath<T>);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return field;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
static fromQueryParams<T>(
|
|
339
|
+
query: Record<string, any>,
|
|
340
|
+
adapter?: CriteriaAdapter<any, any>
|
|
341
|
+
): Criteria<T> {
|
|
208
342
|
const criteria = Criteria.create<T>();
|
|
209
343
|
|
|
344
|
+
if (adapter) {
|
|
345
|
+
criteria.useAdapter(adapter);
|
|
346
|
+
}
|
|
347
|
+
|
|
210
348
|
for (const [key, value] of Object.entries(query)) {
|
|
211
|
-
// Pagination
|
|
212
349
|
if (key === "page") {
|
|
213
|
-
continue;
|
|
350
|
+
continue;
|
|
214
351
|
}
|
|
215
352
|
if (key === "limit") {
|
|
216
353
|
continue;
|
|
@@ -219,34 +356,78 @@ export class Criteria<T = unknown> {
|
|
|
219
356
|
continue;
|
|
220
357
|
}
|
|
221
358
|
|
|
222
|
-
const [field,
|
|
359
|
+
const [field, operatorWithQuantifier] = key.split(":");
|
|
223
360
|
|
|
224
|
-
if (!
|
|
361
|
+
if (!operatorWithQuantifier || !field) continue;
|
|
362
|
+
|
|
363
|
+
const [operatorRaw, quantifierRaw] = operatorWithQuantifier.split("@");
|
|
225
364
|
const operator = isOperator(operatorRaw) ? operatorRaw : null;
|
|
226
|
-
if (!operator)
|
|
365
|
+
if (!operator) {
|
|
366
|
+
throw new InvalidCriteriaError(`Invalid filter operator`, operatorRaw);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const validQuantifiers = ["some", "every", "none"];
|
|
370
|
+
const quantifier =
|
|
371
|
+
quantifierRaw && validQuantifiers.includes(quantifierRaw)
|
|
372
|
+
? (quantifierRaw as CriteriaOptions["quantifier"])
|
|
373
|
+
: undefined;
|
|
374
|
+
|
|
375
|
+
if (quantifierRaw && !quantifier) {
|
|
376
|
+
throw new InvalidCriteriaError(
|
|
377
|
+
`Invalid quantifier. Valid values: ${validQuantifiers.join(", ")}`,
|
|
378
|
+
quantifierRaw
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const options: CriteriaOptions | undefined = quantifier
|
|
383
|
+
? { quantifier }
|
|
384
|
+
: undefined;
|
|
227
385
|
|
|
228
386
|
let parsedValue: any = value;
|
|
229
387
|
|
|
388
|
+
const resolvedField = criteria.resolveFieldPath(field as FieldPath<T>);
|
|
389
|
+
|
|
230
390
|
if (operator === "between") {
|
|
231
391
|
parsedValue = value
|
|
232
392
|
.split(",")
|
|
233
393
|
.map((v: any) => parseQueryValue(v.trim()));
|
|
234
394
|
if (parsedValue.length === 2) {
|
|
235
|
-
criteria.
|
|
395
|
+
criteria.where(
|
|
396
|
+
resolvedField,
|
|
397
|
+
"between" as OperatorsForType<PathValue<T, FieldPath<T>>>,
|
|
398
|
+
[parsedValue[0], parsedValue[1]] as [
|
|
399
|
+
PathValue<T, FieldPath<T>>,
|
|
400
|
+
PathValue<T, FieldPath<T>>
|
|
401
|
+
],
|
|
402
|
+
options
|
|
403
|
+
);
|
|
236
404
|
}
|
|
237
405
|
continue;
|
|
238
406
|
}
|
|
239
407
|
|
|
240
408
|
if (operator === "in" || operator === "notIn") {
|
|
241
409
|
parsedValue = value.split(",").map(parseQueryValue);
|
|
242
|
-
criteria.where(
|
|
410
|
+
criteria.where(
|
|
411
|
+
field as any,
|
|
412
|
+
operator as OperatorsForType<PathValue<T, FieldPath<T>>>,
|
|
413
|
+
parsedValue,
|
|
414
|
+
options
|
|
415
|
+
);
|
|
243
416
|
continue;
|
|
244
417
|
}
|
|
245
418
|
|
|
246
|
-
|
|
419
|
+
const parsedFinalValue = parseQueryValue(value);
|
|
420
|
+
|
|
421
|
+
criteria.validateOperator(operator, parsedFinalValue);
|
|
422
|
+
|
|
423
|
+
criteria.where(
|
|
424
|
+
field as FieldPath<T>,
|
|
425
|
+
operator as OperatorsForType<PathValue<T, FieldPath<T>>>,
|
|
426
|
+
parsedFinalValue,
|
|
427
|
+
options
|
|
428
|
+
);
|
|
247
429
|
}
|
|
248
430
|
|
|
249
|
-
// Pagination
|
|
250
431
|
const page = query.page ? parseInt(query.page) : undefined;
|
|
251
432
|
const limit = query.limit ? parseInt(query.limit) : undefined;
|
|
252
433
|
|
|
@@ -254,12 +435,14 @@ export class Criteria<T = unknown> {
|
|
|
254
435
|
criteria.paginate(page, limit);
|
|
255
436
|
}
|
|
256
437
|
|
|
257
|
-
// Sorting
|
|
258
438
|
if (query.orderBy) {
|
|
259
439
|
const sortParts = query.orderBy.split(",");
|
|
260
440
|
sortParts.forEach((part: string) => {
|
|
261
441
|
const [field, direction] = part.split(":");
|
|
262
|
-
criteria.orderBy(
|
|
442
|
+
criteria.orderBy(
|
|
443
|
+
field as FieldPath<T>,
|
|
444
|
+
(direction as OrderDirection) || "asc"
|
|
445
|
+
);
|
|
263
446
|
});
|
|
264
447
|
}
|
|
265
448
|
|
|
@@ -268,24 +451,22 @@ export class Criteria<T = unknown> {
|
|
|
268
451
|
.split(",")
|
|
269
452
|
.filter(Boolean) as FieldPath<T>[];
|
|
270
453
|
|
|
271
|
-
|
|
454
|
+
const resolvedFields = fields.map(criteria.resolveFieldPath);
|
|
455
|
+
criteria.search(resolvedFields, query.search as string);
|
|
272
456
|
}
|
|
273
457
|
|
|
274
458
|
return criteria;
|
|
275
459
|
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// ============================================================================
|
|
279
|
-
// Helper Functions
|
|
280
|
-
// ============================================================================
|
|
281
460
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
461
|
+
private validateOperator(operator: FilterOperator, value: any): void {
|
|
462
|
+
if (value !== undefined && !isValidOperatorForType(value, operator)) {
|
|
463
|
+
const validOps = getValidOperatorsForType(value);
|
|
464
|
+
throw new InvalidCriteriaError(
|
|
465
|
+
`Operator "${operator}" is not valid for type "${typeof value}". Valid operators: ${validOps.join(
|
|
466
|
+
", "
|
|
467
|
+
)}`,
|
|
468
|
+
operator
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
291
472
|
}
|