@woltz/rich-domain 1.2.4 → 1.3.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/dist/aggregate-changes.d.ts +56 -14
- package/dist/aggregate-changes.d.ts.map +1 -1
- package/dist/aggregate-changes.js +103 -23
- 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 +28 -13
- package/dist/base-entity.js.map +1 -1
- package/dist/change-tracker.d.ts +2 -1
- package/dist/change-tracker.d.ts.map +1 -1
- package/dist/change-tracker.js +61 -35
- 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 +105 -81
- package/dist/criteria.js.map +1 -1
- package/dist/domain-event-bus.js +4 -4
- package/dist/domain-event-bus.js.map +1 -1
- package/dist/domain-event.js +3 -0
- package/dist/domain-event.js.map +1 -1
- package/dist/entity-changes.js +1 -0
- package/dist/entity-changes.js.map +1 -1
- package/dist/entity-schema-registry.d.ts +137 -3
- package/dist/entity-schema-registry.d.ts.map +1 -1
- package/dist/entity-schema-registry.js +160 -7
- package/dist/entity-schema-registry.js.map +1 -1
- package/dist/exceptions.js +26 -1
- package/dist/exceptions.js.map +1 -1
- package/dist/id.js +2 -0
- package/dist/id.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.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 +14 -19
- package/dist/paginated-result.js.map +1 -1
- package/dist/repository/unit-of-work.js +3 -7
- package/dist/repository/unit-of-work.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 -1
- 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/validation-error.d.ts +15 -1
- package/dist/validation-error.d.ts.map +1 -1
- package/dist/validation-error.js +46 -3
- 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 +30 -2
- package/dist/value-object.js.map +1 -1
- package/package.json +17 -3
- package/src/aggregate-changes.ts +133 -24
- package/src/base-entity.ts +22 -11
- package/src/change-tracker.ts +113 -54
- package/src/criteria.ts +151 -109
- package/src/entity-schema-registry.ts +256 -6
- package/src/index.ts +1 -1
- package/src/paginated-result.ts +21 -29
- package/src/types/change-tracker.ts +31 -0
- package/src/types/criteria.ts +1 -4
- package/src/types/domain.ts +2 -1
- package/src/types/utils.ts +2 -2
- package/src/utils/helpers.ts +28 -0
- package/src/validation-error.ts +54 -4
- package/src/value-object.ts +6 -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/tests/criteria.test.ts
DELETED
|
@@ -1,716 +0,0 @@
|
|
|
1
|
-
import { Pagination } from "../src";
|
|
2
|
-
import { Criteria } from "../src/criteria";
|
|
3
|
-
import { PaginatedResult } from "../src/paginated-result";
|
|
4
|
-
import { CriteriaAdapter } from "../src/types";
|
|
5
|
-
import { Post } from "./utils";
|
|
6
|
-
|
|
7
|
-
interface TestUser {
|
|
8
|
-
id: string;
|
|
9
|
-
name: string;
|
|
10
|
-
email: string;
|
|
11
|
-
age: number;
|
|
12
|
-
status: "active" | "inactive";
|
|
13
|
-
createdAt: Date;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
interface UserWithPostsDto {
|
|
17
|
-
id: string;
|
|
18
|
-
name: string;
|
|
19
|
-
posts: { title: string; content: string }[];
|
|
20
|
-
leads: {
|
|
21
|
-
contact: {
|
|
22
|
-
name: string;
|
|
23
|
-
};
|
|
24
|
-
}[];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface UserWithNestedObject {
|
|
28
|
-
id: string;
|
|
29
|
-
name: string;
|
|
30
|
-
address: {
|
|
31
|
-
street: string;
|
|
32
|
-
city: string;
|
|
33
|
-
country: string;
|
|
34
|
-
};
|
|
35
|
-
tags: string[];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const testUsers: TestUser[] = [
|
|
39
|
-
{
|
|
40
|
-
id: "1",
|
|
41
|
-
name: "Alice",
|
|
42
|
-
email: "alice@example.com",
|
|
43
|
-
age: 25,
|
|
44
|
-
status: "active",
|
|
45
|
-
createdAt: new Date("2024-01-01"),
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
id: "2",
|
|
49
|
-
name: "Bob",
|
|
50
|
-
email: "bob@example.com",
|
|
51
|
-
age: 30,
|
|
52
|
-
status: "active",
|
|
53
|
-
createdAt: new Date("2024-02-01"),
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
id: "3",
|
|
57
|
-
name: "Charlie",
|
|
58
|
-
email: "charlie@test.com",
|
|
59
|
-
age: 35,
|
|
60
|
-
status: "inactive",
|
|
61
|
-
createdAt: new Date("2024-03-01"),
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
id: "4",
|
|
65
|
-
name: "Diana",
|
|
66
|
-
email: "diana@example.com",
|
|
67
|
-
age: 28,
|
|
68
|
-
status: "active",
|
|
69
|
-
createdAt: new Date("2024-04-01"),
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
id: "5",
|
|
73
|
-
name: "Eve",
|
|
74
|
-
email: "eve@test.com",
|
|
75
|
-
age: 22,
|
|
76
|
-
status: "inactive",
|
|
77
|
-
createdAt: new Date("2024-05-01"),
|
|
78
|
-
},
|
|
79
|
-
];
|
|
80
|
-
|
|
81
|
-
describe("Criteria", () => {
|
|
82
|
-
describe("Fluent API", () => {
|
|
83
|
-
it("should create empty criteria", () => {
|
|
84
|
-
const criteria = Criteria.create<TestUser>();
|
|
85
|
-
expect(criteria.hasFilters()).toBe(false);
|
|
86
|
-
expect(criteria.hasOrders()).toBe(false);
|
|
87
|
-
expect(criteria.hasPagination()).toBe(true);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("should chain methods fluently", () => {
|
|
91
|
-
const criteria = Criteria.create<TestUser>()
|
|
92
|
-
.where("status", "equals", "active")
|
|
93
|
-
.where("age", "greaterThan", 18)
|
|
94
|
-
.orderBy("name", "asc")
|
|
95
|
-
.paginate(1, 10);
|
|
96
|
-
|
|
97
|
-
expect(criteria.hasFilters()).toBe(true);
|
|
98
|
-
expect(criteria.hasOrders()).toBe(true);
|
|
99
|
-
expect(criteria.hasPagination()).toBe(true);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("should use shorthand methods", () => {
|
|
103
|
-
const criteria = Criteria.create<TestUser>()
|
|
104
|
-
.whereEquals("status", "active")
|
|
105
|
-
.whereContains("name", "ali")
|
|
106
|
-
.whereIn("age", [25, 30, 35])
|
|
107
|
-
.orderByDesc("createdAt");
|
|
108
|
-
|
|
109
|
-
const filters = criteria.getFilters();
|
|
110
|
-
expect(filters).toHaveLength(3);
|
|
111
|
-
expect(filters[0].operator).toBe("equals");
|
|
112
|
-
expect(filters[1].operator).toBe("contains");
|
|
113
|
-
expect(filters[2].operator).toBe("in");
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
describe("Filtering", () => {
|
|
118
|
-
it("should filter by search", () => {
|
|
119
|
-
const criteria = Criteria.create<TestUser>().search(["name"], "Bob");
|
|
120
|
-
|
|
121
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
122
|
-
expect(result.data).toHaveLength(1);
|
|
123
|
-
expect(result.data[0].name).toBe("Bob");
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it("should search by all items ignoring pagination and limit", () => {
|
|
127
|
-
const criteria = Criteria.create<TestUser>()
|
|
128
|
-
.search(["name"], "Eve")
|
|
129
|
-
.paginate(3, 1);
|
|
130
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
131
|
-
expect(result.data).toHaveLength(1);
|
|
132
|
-
expect(result.data[0].name).toBe("Eve");
|
|
133
|
-
expect(result.meta.total).toBe(1);
|
|
134
|
-
expect(result.meta.totalPages).toBe(1);
|
|
135
|
-
expect(result.meta.page).toBe(1);
|
|
136
|
-
expect(result.meta.limit).toBe(1);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("should filter by greaterThan", () => {
|
|
140
|
-
const criteria = Criteria.create<TestUser>().where(
|
|
141
|
-
"age",
|
|
142
|
-
"greaterThan",
|
|
143
|
-
28
|
|
144
|
-
);
|
|
145
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
146
|
-
expect(result.data).toHaveLength(2);
|
|
147
|
-
expect(result.data.map((u) => u.name)).toEqual(["Bob", "Charlie"]);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("should filter by lessThan", () => {
|
|
151
|
-
const criteria = Criteria.create<TestUser>().where("age", "lessThan", 26);
|
|
152
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
153
|
-
expect(result.data).toHaveLength(2);
|
|
154
|
-
expect(result.data.map((u) => u.name)).toEqual(["Alice", "Eve"]);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("should filter by contains", () => {
|
|
158
|
-
const criteria = Criteria.create<TestUser>().whereContains(
|
|
159
|
-
"email",
|
|
160
|
-
"example"
|
|
161
|
-
);
|
|
162
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
163
|
-
expect(result.data).toHaveLength(3);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it("should filter by startsWith", () => {
|
|
167
|
-
const criteria = Criteria.create<TestUser>().where(
|
|
168
|
-
"name",
|
|
169
|
-
"startsWith",
|
|
170
|
-
"A"
|
|
171
|
-
);
|
|
172
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
173
|
-
expect(result.data).toHaveLength(1);
|
|
174
|
-
expect(result.data[0].name).toBe("Alice");
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it("should filter by endsWith", () => {
|
|
178
|
-
const criteria = Criteria.create<TestUser>().where(
|
|
179
|
-
"email",
|
|
180
|
-
"endsWith",
|
|
181
|
-
".com"
|
|
182
|
-
);
|
|
183
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
184
|
-
expect(result.data).toHaveLength(5);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it("should filter by in", () => {
|
|
188
|
-
const criteria = Criteria.create<TestUser>().whereIn("age", [25, 35]);
|
|
189
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
190
|
-
expect(result.data).toHaveLength(2);
|
|
191
|
-
expect(result.data.map((u) => u.name)).toEqual(["Alice", "Charlie"]);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it("should filter by notIn", () => {
|
|
195
|
-
const criteria = Criteria.create<TestUser>().where(
|
|
196
|
-
"age",
|
|
197
|
-
"notIn",
|
|
198
|
-
[25, 35]
|
|
199
|
-
);
|
|
200
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
201
|
-
expect(result.data).toHaveLength(3);
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it("should filter by between", () => {
|
|
205
|
-
const criteria = Criteria.create<TestUser>().whereBetween("age", 25, 30);
|
|
206
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
207
|
-
expect(result.data).toHaveLength(3);
|
|
208
|
-
expect(result.data.map((u) => u.name)).toEqual(["Alice", "Bob", "Diana"]);
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it("should combine multiple filters", () => {
|
|
212
|
-
const criteria = Criteria.create<TestUser>()
|
|
213
|
-
.whereEquals("status", "active")
|
|
214
|
-
.where("age", "greaterThan", 25);
|
|
215
|
-
|
|
216
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
217
|
-
expect(result.data).toHaveLength(2);
|
|
218
|
-
expect(result.data.map((u) => u.name)).toEqual(["Bob", "Diana"]);
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it("should filter, order, and paginate", () => {
|
|
222
|
-
const criteria = Criteria.create<TestUser>()
|
|
223
|
-
.whereEquals("status", "active")
|
|
224
|
-
.orderByDesc("age")
|
|
225
|
-
.paginate(1, 2);
|
|
226
|
-
|
|
227
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
228
|
-
|
|
229
|
-
expect(result.data).toHaveLength(2);
|
|
230
|
-
expect(result.data.map((u) => u.name)).toEqual(["Bob", "Diana"]);
|
|
231
|
-
expect(result.meta.total).toBe(3);
|
|
232
|
-
expect(result.meta.totalPages).toBe(2);
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
describe("Ordering", () => {
|
|
237
|
-
it("should order by ascending", () => {
|
|
238
|
-
const criteria = Criteria.create<TestUser>().orderByAsc("age");
|
|
239
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
240
|
-
const ages = result.data.map((u) => u.age);
|
|
241
|
-
expect(ages).toEqual([22, 25, 28, 30, 35]);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it("should order by descending", () => {
|
|
245
|
-
const criteria = Criteria.create<TestUser>().orderByDesc("age");
|
|
246
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
247
|
-
const ages = result.data.map((u) => u.age);
|
|
248
|
-
expect(ages).toEqual([35, 30, 28, 25, 22]);
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
it("should order by string field", () => {
|
|
252
|
-
const criteria = Criteria.create<TestUser>().orderByAsc("name");
|
|
253
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
254
|
-
const names = result.data.map((u) => u.name);
|
|
255
|
-
expect(names).toEqual(["Alice", "Bob", "Charlie", "Diana", "Eve"]);
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it("should order by date field", () => {
|
|
259
|
-
const criteria = Criteria.create<TestUser>().orderByDesc("createdAt");
|
|
260
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
261
|
-
const names = result.data.map((u) => u.name);
|
|
262
|
-
expect(names).toEqual(["Eve", "Diana", "Charlie", "Bob", "Alice"]);
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
describe("Pagination", () => {
|
|
267
|
-
it("should paginate results", () => {
|
|
268
|
-
const criteria = Criteria.create<TestUser>().paginate(1, 2);
|
|
269
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
270
|
-
|
|
271
|
-
expect(result.data).toHaveLength(2);
|
|
272
|
-
expect(result.meta.page).toBe(1);
|
|
273
|
-
expect(result.meta.limit).toBe(2);
|
|
274
|
-
expect(result.meta.total).toBe(5);
|
|
275
|
-
expect(result.meta.totalPages).toBe(3);
|
|
276
|
-
expect(result.meta.hasNext).toBe(true);
|
|
277
|
-
expect(result.meta.hasPrevious).toBe(false);
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
it("should apply limit shorthand", () => {
|
|
281
|
-
const criteria = Criteria.create<TestUser>().limit(3);
|
|
282
|
-
const result = PaginatedResult.fromArray(testUsers, criteria);
|
|
283
|
-
|
|
284
|
-
expect(result.data).toHaveLength(3);
|
|
285
|
-
expect(result.meta.page).toBe(1);
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
it("should create pagination meta", () => {
|
|
289
|
-
const pagination = { page: 2, limit: 10, offset: 10 };
|
|
290
|
-
const meta = PaginatedResult.createMeta(pagination, 45);
|
|
291
|
-
|
|
292
|
-
expect(meta.page).toBe(2);
|
|
293
|
-
expect(meta.limit).toBe(10);
|
|
294
|
-
expect(meta.total).toBe(45);
|
|
295
|
-
expect(meta.totalPages).toBe(5);
|
|
296
|
-
expect(meta.hasNext).toBe(true);
|
|
297
|
-
expect(meta.hasPrevious).toBe(true);
|
|
298
|
-
});
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
describe("Serialization", () => {
|
|
302
|
-
it("should convert to object", () => {
|
|
303
|
-
const criteria = Criteria.create<TestUser>()
|
|
304
|
-
.whereEquals("status", "active")
|
|
305
|
-
.orderByDesc("age")
|
|
306
|
-
.paginate(1, 10);
|
|
307
|
-
|
|
308
|
-
const obj = criteria.toJSON();
|
|
309
|
-
|
|
310
|
-
expect(obj.filters).toHaveLength(1);
|
|
311
|
-
expect(obj.orders).toHaveLength(1);
|
|
312
|
-
expect(obj.pagination).toBeDefined();
|
|
313
|
-
expect(obj.pagination?.page).toBe(1);
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
it("should create from object", () => {
|
|
317
|
-
const criteria = Criteria.fromObject<TestUser>({
|
|
318
|
-
filters: [{ field: "status", operator: "equals", value: "active" }],
|
|
319
|
-
orders: [{ field: "age", direction: "desc" }],
|
|
320
|
-
pagination: { page: 1, limit: 10, offset: 0 },
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
expect(criteria.getFilters()).toHaveLength(1);
|
|
324
|
-
expect(criteria.getOrders()).toHaveLength(1);
|
|
325
|
-
expect(criteria.getPagination()?.page).toBe(1);
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
it("should clone criteria", () => {
|
|
329
|
-
const original = Criteria.create<TestUser>()
|
|
330
|
-
.whereEquals("status", "active")
|
|
331
|
-
.orderByDesc("age");
|
|
332
|
-
|
|
333
|
-
const cloned = original.clone();
|
|
334
|
-
cloned.whereEquals("age", 30);
|
|
335
|
-
|
|
336
|
-
expect(original.getFilters()).toHaveLength(1);
|
|
337
|
-
expect(cloned.getFilters()).toHaveLength(2);
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
it("should deserialize pagination result with entities", () => {
|
|
341
|
-
const pagination: Pagination = { page: 1, limit: 10, offset: 0 };
|
|
342
|
-
|
|
343
|
-
const data = [
|
|
344
|
-
new Post({
|
|
345
|
-
title: "Post 1",
|
|
346
|
-
content: "Content 1",
|
|
347
|
-
published: true,
|
|
348
|
-
comments: [],
|
|
349
|
-
}),
|
|
350
|
-
new Post({
|
|
351
|
-
title: "Post 2",
|
|
352
|
-
content: "Content 2",
|
|
353
|
-
published: true,
|
|
354
|
-
comments: [],
|
|
355
|
-
}),
|
|
356
|
-
];
|
|
357
|
-
|
|
358
|
-
const total = data.length;
|
|
359
|
-
|
|
360
|
-
const paginationResult = PaginatedResult.create(data, pagination, total);
|
|
361
|
-
|
|
362
|
-
const result = paginationResult.toJSON();
|
|
363
|
-
|
|
364
|
-
expect(result.data).toHaveLength(2);
|
|
365
|
-
expect(result.data[0].title).toBe("Post 1");
|
|
366
|
-
expect(result.data[1].title).toBe("Post 2");
|
|
367
|
-
expect(result.meta.total).toBe(total);
|
|
368
|
-
expect(result.meta.totalPages).toBe(1);
|
|
369
|
-
expect(result.meta.page).toBe(1);
|
|
370
|
-
expect(result.meta.limit).toBe(10);
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
it("should deserialize pagination result with plain objects", () => {
|
|
374
|
-
const pagination: Pagination = { page: 1, limit: 10, offset: 0 };
|
|
375
|
-
const total = testUsers.length;
|
|
376
|
-
|
|
377
|
-
const paginationResult = PaginatedResult.create(
|
|
378
|
-
testUsers,
|
|
379
|
-
pagination,
|
|
380
|
-
total
|
|
381
|
-
);
|
|
382
|
-
|
|
383
|
-
const result = paginationResult.toJSON();
|
|
384
|
-
|
|
385
|
-
expect(result.data).toHaveLength(testUsers.length);
|
|
386
|
-
expect(result.data.map((u) => u.name)).toEqual(
|
|
387
|
-
testUsers.map((u) => u.name)
|
|
388
|
-
);
|
|
389
|
-
expect(result.meta.total).toBe(total);
|
|
390
|
-
expect(result.meta.totalPages).toBe(1);
|
|
391
|
-
expect(result.meta.page).toBe(1);
|
|
392
|
-
expect(result.meta.limit).toBe(10);
|
|
393
|
-
});
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
describe("Criteria from Query Params", () => {
|
|
397
|
-
it("should create criteria from query params", () => {
|
|
398
|
-
const queryParams = {
|
|
399
|
-
"status:equals": "active",
|
|
400
|
-
"age:greaterThan": "25",
|
|
401
|
-
orderBy: "age",
|
|
402
|
-
orderDirection: "desc",
|
|
403
|
-
page: "1",
|
|
404
|
-
limit: "2",
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
const criteria = Criteria.fromQueryParams<TestUser>(queryParams);
|
|
408
|
-
expect(criteria.getFilters()).toHaveLength(2);
|
|
409
|
-
expect(criteria.getOrders()).toHaveLength(1);
|
|
410
|
-
expect(criteria.getPagination()?.page).toBe(1);
|
|
411
|
-
expect(criteria.getPagination()?.limit).toBe(2);
|
|
412
|
-
});
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
describe("Quantifiers", () => {
|
|
416
|
-
describe("Fluent API methods", () => {
|
|
417
|
-
it("should use quantifiers horthand methods", () => {
|
|
418
|
-
// CHECK: Devemos permitir o encadeamento de metodos quantifiers?
|
|
419
|
-
const criteria = Criteria.create<TestUser>()
|
|
420
|
-
.whereSome("name", "contains", "ali")
|
|
421
|
-
.whereEvery("name", "contains", "ali")
|
|
422
|
-
.whereNone("name", "contains", "ali");
|
|
423
|
-
|
|
424
|
-
const filters = criteria.getFilters().map((f) => f.options?.quantifier);
|
|
425
|
-
|
|
426
|
-
expect(filters).toHaveLength(3);
|
|
427
|
-
expect(filters).toEqual(["some", "every", "none"]);
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
it("should create filter with whereSome", () => {
|
|
431
|
-
const criteria = Criteria.create<UserWithPostsDto>().whereSome(
|
|
432
|
-
"posts.title",
|
|
433
|
-
"contains",
|
|
434
|
-
"test"
|
|
435
|
-
);
|
|
436
|
-
|
|
437
|
-
const filters = criteria.getFilters();
|
|
438
|
-
|
|
439
|
-
expect(filters).toHaveLength(1);
|
|
440
|
-
expect(filters[0].field).toBe("posts.title");
|
|
441
|
-
expect(filters[0].operator).toBe("contains");
|
|
442
|
-
expect(filters[0].value).toBe("test");
|
|
443
|
-
expect(filters[0].options?.quantifier).toBe("some");
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
it("should create filter with whereEvery", () => {
|
|
447
|
-
const criteria = Criteria.create<UserWithPostsDto>().whereEvery(
|
|
448
|
-
"posts.title",
|
|
449
|
-
"contains",
|
|
450
|
-
"test"
|
|
451
|
-
);
|
|
452
|
-
|
|
453
|
-
const filters = criteria.getFilters();
|
|
454
|
-
expect(filters).toHaveLength(1);
|
|
455
|
-
expect(filters[0].options?.quantifier).toBe("every");
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
it("should create filter with whereNone", () => {
|
|
459
|
-
const criteria = Criteria.create<UserWithPostsDto>().whereNone(
|
|
460
|
-
"posts.title",
|
|
461
|
-
"contains",
|
|
462
|
-
"test"
|
|
463
|
-
);
|
|
464
|
-
|
|
465
|
-
const filters = criteria.getFilters();
|
|
466
|
-
expect(filters).toHaveLength(1);
|
|
467
|
-
expect(filters[0].options?.quantifier).toBe("none");
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
it("should parse quantifier from query params with @some", () => {
|
|
471
|
-
const queryParams = {
|
|
472
|
-
"posts.title:contains@some": "test",
|
|
473
|
-
};
|
|
474
|
-
|
|
475
|
-
const criteria =
|
|
476
|
-
Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
|
|
477
|
-
const filters = criteria.getFilters();
|
|
478
|
-
|
|
479
|
-
expect(filters).toHaveLength(1);
|
|
480
|
-
expect(filters[0].field).toBe("posts.title");
|
|
481
|
-
expect(filters[0].operator).toBe("contains");
|
|
482
|
-
expect(filters[0].value).toBe("test");
|
|
483
|
-
expect(filters[0].options?.quantifier).toBe("some");
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
it("should work with in operator and quantifier", () => {
|
|
487
|
-
const queryParams = {
|
|
488
|
-
"posts.title:in@some": "test1,test2,test3",
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
const criteria =
|
|
492
|
-
Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
|
|
493
|
-
const filters = criteria.getFilters();
|
|
494
|
-
|
|
495
|
-
expect(filters).toHaveLength(1);
|
|
496
|
-
expect(filters[0].operator).toBe("in");
|
|
497
|
-
expect(filters[0].value).toEqual(["test1", "test2", "test3"]);
|
|
498
|
-
expect(filters[0].options?.quantifier).toBe("some");
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
it("should work with between operator and quantifier", () => {
|
|
502
|
-
const queryParams = {
|
|
503
|
-
"posts.likes:between@some": "10,100",
|
|
504
|
-
};
|
|
505
|
-
|
|
506
|
-
const criteria =
|
|
507
|
-
Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
|
|
508
|
-
const filters = criteria.getFilters();
|
|
509
|
-
|
|
510
|
-
expect(filters).toHaveLength(1);
|
|
511
|
-
expect(filters[0].operator).toBe("between");
|
|
512
|
-
expect(filters[0].value).toEqual([10, 100]);
|
|
513
|
-
expect(filters[0].options?.quantifier).toBe("some");
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
it("should throw error for invalid quantifier", () => {
|
|
517
|
-
const queryParams = {
|
|
518
|
-
"posts.title:contains@invalid": "test",
|
|
519
|
-
};
|
|
520
|
-
|
|
521
|
-
expect(() => {
|
|
522
|
-
Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
|
|
523
|
-
}).toThrow("Invalid quantifier");
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
it("should handle multiple filters with mixed quantifiers", () => {
|
|
527
|
-
const queryParams = {
|
|
528
|
-
"posts.title:contains@some": "test",
|
|
529
|
-
"posts.content:equals@every": "content",
|
|
530
|
-
"name:contains": "John",
|
|
531
|
-
};
|
|
532
|
-
|
|
533
|
-
const criteria =
|
|
534
|
-
Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
|
|
535
|
-
const filters = criteria.getFilters();
|
|
536
|
-
|
|
537
|
-
expect(filters).toHaveLength(3);
|
|
538
|
-
expect(filters[0].options?.quantifier).toBe("some");
|
|
539
|
-
expect(filters[1].options?.quantifier).toBe("every");
|
|
540
|
-
expect(filters[2].options).toBeUndefined();
|
|
541
|
-
});
|
|
542
|
-
});
|
|
543
|
-
|
|
544
|
-
describe("fromObject with quantifier", () => {
|
|
545
|
-
it("should create criteria from object with quantifier", () => {
|
|
546
|
-
const criteria = Criteria.fromObject<UserWithPostsDto>({
|
|
547
|
-
filters: [
|
|
548
|
-
{
|
|
549
|
-
field: "posts.title",
|
|
550
|
-
operator: "contains",
|
|
551
|
-
value: "test",
|
|
552
|
-
options: { quantifier: "some" },
|
|
553
|
-
},
|
|
554
|
-
],
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
const filters = criteria.getFilters();
|
|
558
|
-
expect(filters).toHaveLength(1);
|
|
559
|
-
expect(filters[0].options?.quantifier).toBe("some");
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
it("should preserve quantifier when cloning", () => {
|
|
563
|
-
const original = Criteria.create<UserWithPostsDto>().whereSome(
|
|
564
|
-
"posts.title",
|
|
565
|
-
"contains",
|
|
566
|
-
"test"
|
|
567
|
-
);
|
|
568
|
-
|
|
569
|
-
const cloned = original.clone();
|
|
570
|
-
const filters = cloned.getFilters();
|
|
571
|
-
|
|
572
|
-
expect(filters).toHaveLength(1);
|
|
573
|
-
expect(filters[0].options?.quantifier).toBe("some");
|
|
574
|
-
});
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
describe("toJSON with quantifier", () => {
|
|
578
|
-
it("should serialize quantifier to JSON", () => {
|
|
579
|
-
const criteria = Criteria.create<UserWithPostsDto>()
|
|
580
|
-
.whereSome("posts.title", "contains", "test")
|
|
581
|
-
.whereEvery("posts.content", "equals", "content");
|
|
582
|
-
|
|
583
|
-
const json = criteria.toJSON();
|
|
584
|
-
|
|
585
|
-
expect(json.filters).toHaveLength(2);
|
|
586
|
-
expect(json.filters[0].options?.quantifier).toBe("some");
|
|
587
|
-
expect(json.filters[1].options?.quantifier).toBe("every");
|
|
588
|
-
});
|
|
589
|
-
});
|
|
590
|
-
});
|
|
591
|
-
|
|
592
|
-
describe("Adapter", () => {
|
|
593
|
-
type UserInDatabase = {
|
|
594
|
-
id: string;
|
|
595
|
-
name: string;
|
|
596
|
-
user_posts: { title: string; content: string }[];
|
|
597
|
-
leads: {
|
|
598
|
-
contact: {
|
|
599
|
-
fullName: string;
|
|
600
|
-
};
|
|
601
|
-
};
|
|
602
|
-
};
|
|
603
|
-
const UserWithPostsAdapter: CriteriaAdapter<
|
|
604
|
-
UserWithPostsDto,
|
|
605
|
-
UserInDatabase
|
|
606
|
-
> = {
|
|
607
|
-
posts: "user_posts",
|
|
608
|
-
"leads.contact.name": "leads.contact.fullName",
|
|
609
|
-
};
|
|
610
|
-
|
|
611
|
-
let criteria: Criteria<UserWithPostsDto>;
|
|
612
|
-
beforeEach(() => {
|
|
613
|
-
criteria =
|
|
614
|
-
Criteria.create<UserWithPostsDto>().useAdapter(UserWithPostsAdapter);
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
it("should resolve field path", () => {
|
|
618
|
-
const filters = criteria
|
|
619
|
-
.where("leads.contact.name", "contains", "John")
|
|
620
|
-
.getFilters();
|
|
621
|
-
|
|
622
|
-
expect(filters[0].field).toBe("leads.contact.fullName");
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
it("should resolve field path from query params", () => {
|
|
626
|
-
const queryParams = {
|
|
627
|
-
"posts:contains": "test",
|
|
628
|
-
};
|
|
629
|
-
|
|
630
|
-
const result = Criteria.fromQueryParams<UserWithPostsDto>(
|
|
631
|
-
queryParams,
|
|
632
|
-
UserWithPostsAdapter
|
|
633
|
-
);
|
|
634
|
-
const filters = result.getFilters();
|
|
635
|
-
|
|
636
|
-
expect(filters).toHaveLength(1);
|
|
637
|
-
expect(filters[0].field).toBe("user_posts");
|
|
638
|
-
expect(filters[0].operator).toBe("contains");
|
|
639
|
-
expect(filters[0].value).toBe("test");
|
|
640
|
-
});
|
|
641
|
-
|
|
642
|
-
it("should resolve field path from object", () => {
|
|
643
|
-
const criteria = Criteria.fromObject<UserWithPostsDto>(
|
|
644
|
-
{
|
|
645
|
-
filters: [
|
|
646
|
-
{
|
|
647
|
-
field: "posts",
|
|
648
|
-
operator: "in",
|
|
649
|
-
value: [{ title: "test" }] as any,
|
|
650
|
-
},
|
|
651
|
-
],
|
|
652
|
-
},
|
|
653
|
-
UserWithPostsAdapter
|
|
654
|
-
);
|
|
655
|
-
const filters = criteria.getFilters();
|
|
656
|
-
expect(filters).toHaveLength(1);
|
|
657
|
-
expect(filters[0].field).toBe("user_posts");
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
it("should only allow nested paths in fromObject for object fields", () => {
|
|
661
|
-
// Only nested paths should work for object fields
|
|
662
|
-
const criteria = Criteria.fromObject<UserWithNestedObject>({
|
|
663
|
-
filters: [
|
|
664
|
-
{
|
|
665
|
-
field: "address.street",
|
|
666
|
-
operator: "equals",
|
|
667
|
-
value: "123 Main St",
|
|
668
|
-
},
|
|
669
|
-
{
|
|
670
|
-
field: "address.city",
|
|
671
|
-
operator: "equals",
|
|
672
|
-
value: "New York",
|
|
673
|
-
},
|
|
674
|
-
{
|
|
675
|
-
field: "id",
|
|
676
|
-
operator: "equals",
|
|
677
|
-
value: "user-1",
|
|
678
|
-
},
|
|
679
|
-
],
|
|
680
|
-
});
|
|
681
|
-
|
|
682
|
-
expect(criteria.getFilters()).toHaveLength(3);
|
|
683
|
-
});
|
|
684
|
-
});
|
|
685
|
-
|
|
686
|
-
describe("Nested Object Field Paths", () => {
|
|
687
|
-
it("should only allow nested paths for object fields, not the root", () => {
|
|
688
|
-
const criteria = Criteria.create<UserWithNestedObject>();
|
|
689
|
-
|
|
690
|
-
// These should work - nested paths
|
|
691
|
-
criteria.whereEquals("address.city", "123 Main St");
|
|
692
|
-
criteria.whereEquals("address.city", "New York");
|
|
693
|
-
criteria.whereEquals("address.country", "USA");
|
|
694
|
-
|
|
695
|
-
// These should work - primitives and arrays
|
|
696
|
-
criteria.whereEquals("id", "user-1");
|
|
697
|
-
criteria.whereEquals("name", "John Doe");
|
|
698
|
-
criteria.where("tags", "in", ["tag1", "tag2"]);
|
|
699
|
-
|
|
700
|
-
const filters = criteria.getFilters();
|
|
701
|
-
expect(filters).toHaveLength(6);
|
|
702
|
-
});
|
|
703
|
-
|
|
704
|
-
it("should work with array of objects (keeps root + nested)", () => {
|
|
705
|
-
const criteria = Criteria.create<UserWithPostsDto>();
|
|
706
|
-
|
|
707
|
-
// These should work - array root and nested paths
|
|
708
|
-
criteria.where("posts", "in", []);
|
|
709
|
-
criteria.whereEquals("posts.title", "Test Post");
|
|
710
|
-
criteria.whereEquals("posts.content", "Test Content");
|
|
711
|
-
|
|
712
|
-
const filters = criteria.getFilters();
|
|
713
|
-
expect(filters).toHaveLength(3);
|
|
714
|
-
});
|
|
715
|
-
});
|
|
716
|
-
});
|