dynamo-query-engine 1.0.3 → 1.0.5
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/README.md +49 -0
- package/index.js +2 -0
- package/package.json +1 -1
- package/src/core/gridQueryBuilder.js +60 -10
- package/src/core/index.js +1 -1
- package/tests/unit/gridQueryBuilder.test.js +180 -34
package/README.md
CHANGED
|
@@ -28,6 +28,8 @@ npm install dynamoose graphql graphql-parse-resolve-info
|
|
|
28
28
|
|
|
29
29
|
### 1. Define Models with Grid Configuration
|
|
30
30
|
|
|
31
|
+
> **Note**: The library supports any hash key field name (e.g., `pk`, `tenantId`, `userId`, etc.). It automatically detects the hash key from your schema.
|
|
32
|
+
|
|
31
33
|
```javascript
|
|
32
34
|
import dynamoose from "dynamoose";
|
|
33
35
|
|
|
@@ -79,6 +81,39 @@ const UserSchema = new dynamoose.Schema({
|
|
|
79
81
|
export const UserModel = dynamoose.model("Users", UserSchema);
|
|
80
82
|
```
|
|
81
83
|
|
|
84
|
+
**Example with custom hash key name:**
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
// The library works with any hash key field name
|
|
88
|
+
const ReservationSchema = new dynamoose.Schema({
|
|
89
|
+
tenantId: {
|
|
90
|
+
type: String,
|
|
91
|
+
hashKey: true, // Automatically detected by getHashKey()
|
|
92
|
+
},
|
|
93
|
+
reservationId: {
|
|
94
|
+
type: String,
|
|
95
|
+
rangeKey: true, // Automatically detected by getRangeKey()
|
|
96
|
+
},
|
|
97
|
+
// ... other fields
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
export const ReservationModel = dynamoose.model("Reservations", ReservationSchema);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Extracting keys programmatically:**
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
import { getHashKey, getRangeKey } from "dynamo-query-engine";
|
|
107
|
+
|
|
108
|
+
// Get the hash key field name from your schema
|
|
109
|
+
const hashKeyField = getHashKey(ReservationModel.schema);
|
|
110
|
+
console.log(hashKeyField); // "tenantId"
|
|
111
|
+
|
|
112
|
+
// Get the range key field name (or null if none exists)
|
|
113
|
+
const rangeKeyField = getRangeKey(ReservationModel.schema);
|
|
114
|
+
console.log(rangeKeyField); // "reservationId"
|
|
115
|
+
```
|
|
116
|
+
|
|
82
117
|
### 2. Register Models
|
|
83
118
|
|
|
84
119
|
```javascript
|
|
@@ -265,6 +300,20 @@ Registers a Dynamoose model.
|
|
|
265
300
|
|
|
266
301
|
Retrieves a registered model.
|
|
267
302
|
|
|
303
|
+
#### `getHashKey(schema)`
|
|
304
|
+
|
|
305
|
+
Extracts the hash key (partition key) field name from a Dynamoose schema.
|
|
306
|
+
|
|
307
|
+
**Returns:** `string` - The hash key field name
|
|
308
|
+
|
|
309
|
+
**Throws:** Error if no hash key is found in the schema
|
|
310
|
+
|
|
311
|
+
#### `getRangeKey(schema)`
|
|
312
|
+
|
|
313
|
+
Extracts the range key (sort key) field name from a Dynamoose schema.
|
|
314
|
+
|
|
315
|
+
**Returns:** `string | null` - The range key field name, or null if no range key exists
|
|
316
|
+
|
|
268
317
|
## Architecture
|
|
269
318
|
|
|
270
319
|
```
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
validateFilterField,
|
|
10
10
|
validateSortField,
|
|
11
11
|
validateFilterOperator,
|
|
12
|
-
validatePaginationModel,
|
|
13
12
|
} from "../utils/validation.js";
|
|
14
13
|
import { decodeCursor, validateCursor } from "../utils/cursor.js";
|
|
15
14
|
|
|
@@ -18,13 +17,13 @@ import { decodeCursor, validateCursor } from "../utils/cursor.js";
|
|
|
18
17
|
* @param {Object} params - Query parameters
|
|
19
18
|
* @param {FilterModel} params.filterModel - Filter model
|
|
20
19
|
* @param {SortModel} params.sortModel - Sort model
|
|
21
|
-
* @param {number} params.
|
|
20
|
+
* @param {number} params.limit - Query limit
|
|
22
21
|
* @returns {string} SHA256 hash of the query parameters
|
|
23
22
|
*/
|
|
24
|
-
function hashQuery({ filterModel, sortModel,
|
|
23
|
+
function hashQuery({ filterModel, sortModel, limit }) {
|
|
25
24
|
return crypto
|
|
26
25
|
.createHash("sha256")
|
|
27
|
-
.update(JSON.stringify({ filterModel, sortModel,
|
|
26
|
+
.update(JSON.stringify({ filterModel, sortModel, limit }))
|
|
28
27
|
.digest("hex");
|
|
29
28
|
}
|
|
30
29
|
|
|
@@ -46,6 +45,40 @@ export function extractGridConfig(schema) {
|
|
|
46
45
|
return gridConfig;
|
|
47
46
|
}
|
|
48
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Get the hash key (partition key) field name from Dynamoose schema
|
|
50
|
+
* @param {*} schema - Dynamoose schema
|
|
51
|
+
* @returns {string} Hash key field name
|
|
52
|
+
*/
|
|
53
|
+
export function getHashKey(schema) {
|
|
54
|
+
const attributes = schema.getAttributes();
|
|
55
|
+
|
|
56
|
+
for (const [field, definition] of Object.entries(attributes)) {
|
|
57
|
+
if (definition.hashKey === true) {
|
|
58
|
+
return field;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
throw new Error("No hash key found in schema");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get the range key (sort key) field name from Dynamoose schema
|
|
67
|
+
* @param {*} schema - Dynamoose schema
|
|
68
|
+
* @returns {string|null} Range key field name, or null if no range key exists
|
|
69
|
+
*/
|
|
70
|
+
export function getRangeKey(schema) {
|
|
71
|
+
const attributes = schema.getAttributes();
|
|
72
|
+
|
|
73
|
+
for (const [field, definition] of Object.entries(attributes)) {
|
|
74
|
+
if (definition.rangeKey === true) {
|
|
75
|
+
return field;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
49
82
|
/**
|
|
50
83
|
* Determine which GSI to use based on filter model
|
|
51
84
|
* @param {FilterModel} filterModel - Filter model
|
|
@@ -165,7 +198,7 @@ export function buildGridQuery({
|
|
|
165
198
|
partitionKeyValue,
|
|
166
199
|
filterModel,
|
|
167
200
|
sortModel,
|
|
168
|
-
|
|
201
|
+
limit = 10,
|
|
169
202
|
cursor,
|
|
170
203
|
}) {
|
|
171
204
|
// Validate inputs
|
|
@@ -175,12 +208,29 @@ export function buildGridQuery({
|
|
|
175
208
|
if (!partitionKeyValue) {
|
|
176
209
|
throw new Error("partitionKeyValue is required");
|
|
177
210
|
}
|
|
178
|
-
|
|
211
|
+
|
|
212
|
+
// Validate and normalize limit
|
|
213
|
+
if (limit !== undefined && limit !== null) {
|
|
214
|
+
const numLimit = typeof limit === "string" ? Number(limit) : limit;
|
|
215
|
+
if (Number.isNaN(numLimit) || numLimit < 1) {
|
|
216
|
+
throw new Error("limit must be a positive number");
|
|
217
|
+
}
|
|
218
|
+
if (numLimit > 1000) {
|
|
219
|
+
throw new Error("limit cannot exceed 1000 (DynamoDB limit)");
|
|
220
|
+
}
|
|
221
|
+
limit = numLimit;
|
|
222
|
+
} else {
|
|
223
|
+
limit = 10;
|
|
224
|
+
}
|
|
225
|
+
|
|
179
226
|
validateSingleSort(sortModel);
|
|
180
227
|
|
|
181
228
|
// Extract grid config from schema
|
|
182
229
|
const gridConfig = extractGridConfig(model.schema);
|
|
183
230
|
|
|
231
|
+
// Get the hash key field name from schema
|
|
232
|
+
const hashKeyField = getHashKey(model.schema);
|
|
233
|
+
|
|
184
234
|
// Determine which index to use
|
|
185
235
|
const index = determineIndex(filterModel, gridConfig);
|
|
186
236
|
|
|
@@ -188,11 +238,11 @@ export function buildGridQuery({
|
|
|
188
238
|
const queryHash = hashQuery({
|
|
189
239
|
filterModel,
|
|
190
240
|
sortModel,
|
|
191
|
-
|
|
241
|
+
limit,
|
|
192
242
|
});
|
|
193
243
|
|
|
194
244
|
// Initialize query with partition key
|
|
195
|
-
let query = model.query(
|
|
245
|
+
let query = model.query(hashKeyField).eq(partitionKeyValue);
|
|
196
246
|
|
|
197
247
|
// Use GSI if needed
|
|
198
248
|
if (index) {
|
|
@@ -205,8 +255,8 @@ export function buildGridQuery({
|
|
|
205
255
|
// Apply sort
|
|
206
256
|
query = applySort(query, sortModel, gridConfig);
|
|
207
257
|
|
|
208
|
-
// Apply
|
|
209
|
-
query = query.limit(
|
|
258
|
+
// Apply limit
|
|
259
|
+
query = query.limit(limit);
|
|
210
260
|
|
|
211
261
|
// Handle cursor for pagination
|
|
212
262
|
if (cursor) {
|
package/src/core/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
export { modelRegistry, ModelRegistry } from "./modelRegistry.js";
|
|
6
|
-
export { buildGridQuery, extractGridConfig } from "./gridQueryBuilder.js";
|
|
6
|
+
export { buildGridQuery, extractGridConfig, getHashKey, getRangeKey } from "./gridQueryBuilder.js";
|
|
7
7
|
export {
|
|
8
8
|
resolveExpand,
|
|
9
9
|
resolveExpands,
|
|
@@ -6,6 +6,8 @@ import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
|
6
6
|
import {
|
|
7
7
|
buildGridQuery,
|
|
8
8
|
extractGridConfig,
|
|
9
|
+
getHashKey,
|
|
10
|
+
getRangeKey,
|
|
9
11
|
} from "../../src/core/gridQueryBuilder.js";
|
|
10
12
|
import {
|
|
11
13
|
createMockSchema,
|
|
@@ -58,6 +60,120 @@ describe("GridQueryBuilder", () => {
|
|
|
58
60
|
});
|
|
59
61
|
});
|
|
60
62
|
|
|
63
|
+
describe("getHashKey", () => {
|
|
64
|
+
it("should extract hash key field named 'pk'", () => {
|
|
65
|
+
const attributes = {
|
|
66
|
+
pk: { type: String, hashKey: true },
|
|
67
|
+
sk: { type: String, rangeKey: true },
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const schema = createMockSchema(attributes);
|
|
71
|
+
const hashKey = getHashKey(schema);
|
|
72
|
+
|
|
73
|
+
expect(hashKey).toBe("pk");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should extract hash key field named 'tenantId'", () => {
|
|
77
|
+
const attributes = {
|
|
78
|
+
tenantId: { type: String, hashKey: true },
|
|
79
|
+
reservationId: { type: String, rangeKey: true },
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const schema = createMockSchema(attributes);
|
|
83
|
+
const hashKey = getHashKey(schema);
|
|
84
|
+
|
|
85
|
+
expect(hashKey).toBe("tenantId");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should extract hash key field with any name", () => {
|
|
89
|
+
const attributes = {
|
|
90
|
+
customHashKey: { type: String, hashKey: true },
|
|
91
|
+
otherField: { type: String },
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const schema = createMockSchema(attributes);
|
|
95
|
+
const hashKey = getHashKey(schema);
|
|
96
|
+
|
|
97
|
+
expect(hashKey).toBe("customHashKey");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("should throw error when no hash key found", () => {
|
|
101
|
+
const attributes = {
|
|
102
|
+
field1: { type: String },
|
|
103
|
+
field2: { type: String },
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const schema = createMockSchema(attributes);
|
|
107
|
+
|
|
108
|
+
expect(() => {
|
|
109
|
+
getHashKey(schema);
|
|
110
|
+
}).toThrow("No hash key found in schema");
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe("getRangeKey", () => {
|
|
115
|
+
it("should extract range key field named 'sk'", () => {
|
|
116
|
+
const attributes = {
|
|
117
|
+
pk: { type: String, hashKey: true },
|
|
118
|
+
sk: { type: String, rangeKey: true },
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const schema = createMockSchema(attributes);
|
|
122
|
+
const rangeKey = getRangeKey(schema);
|
|
123
|
+
|
|
124
|
+
expect(rangeKey).toBe("sk");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should extract range key field named 'createdAt'", () => {
|
|
128
|
+
const attributes = {
|
|
129
|
+
pk: { type: String, hashKey: true },
|
|
130
|
+
createdAt: { type: String, rangeKey: true },
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const schema = createMockSchema(attributes);
|
|
134
|
+
const rangeKey = getRangeKey(schema);
|
|
135
|
+
|
|
136
|
+
expect(rangeKey).toBe("createdAt");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("should extract range key field named 'reservationId'", () => {
|
|
140
|
+
const attributes = {
|
|
141
|
+
tenantId: { type: String, hashKey: true },
|
|
142
|
+
reservationId: { type: String, rangeKey: true },
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const schema = createMockSchema(attributes);
|
|
146
|
+
const rangeKey = getRangeKey(schema);
|
|
147
|
+
|
|
148
|
+
expect(rangeKey).toBe("reservationId");
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("should return null when no range key exists", () => {
|
|
152
|
+
const attributes = {
|
|
153
|
+
pk: { type: String, hashKey: true },
|
|
154
|
+
field1: { type: String },
|
|
155
|
+
field2: { type: String },
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const schema = createMockSchema(attributes);
|
|
159
|
+
const rangeKey = getRangeKey(schema);
|
|
160
|
+
|
|
161
|
+
expect(rangeKey).toBeNull();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("should work with custom range key names", () => {
|
|
165
|
+
const attributes = {
|
|
166
|
+
customHashKey: { type: String, hashKey: true },
|
|
167
|
+
customSortKey: { type: String, rangeKey: true },
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const schema = createMockSchema(attributes);
|
|
171
|
+
const rangeKey = getRangeKey(schema);
|
|
172
|
+
|
|
173
|
+
expect(rangeKey).toBe("customSortKey");
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
61
177
|
describe("buildGridQuery - Basic", () => {
|
|
62
178
|
let mockModel;
|
|
63
179
|
let mockQuery;
|
|
@@ -73,7 +189,7 @@ describe("GridQueryBuilder", () => {
|
|
|
73
189
|
const { query } = buildGridQuery({
|
|
74
190
|
model: mockModel,
|
|
75
191
|
partitionKeyValue: "ORG#123",
|
|
76
|
-
|
|
192
|
+
limit: 20,
|
|
77
193
|
});
|
|
78
194
|
|
|
79
195
|
expect(mockModel.query).toHaveBeenCalledWith("pk");
|
|
@@ -87,7 +203,7 @@ describe("GridQueryBuilder", () => {
|
|
|
87
203
|
buildGridQuery({
|
|
88
204
|
model: null,
|
|
89
205
|
partitionKeyValue: "ORG#123",
|
|
90
|
-
|
|
206
|
+
limit: 20,
|
|
91
207
|
});
|
|
92
208
|
}).toThrow("model is required");
|
|
93
209
|
});
|
|
@@ -97,18 +213,48 @@ describe("GridQueryBuilder", () => {
|
|
|
97
213
|
buildGridQuery({
|
|
98
214
|
model: mockModel,
|
|
99
215
|
partitionKeyValue: null,
|
|
100
|
-
|
|
216
|
+
limit: 20,
|
|
101
217
|
});
|
|
102
218
|
}).toThrow("partitionKeyValue is required");
|
|
103
219
|
});
|
|
104
220
|
|
|
105
|
-
it("should
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
221
|
+
it("should use default limit when not provided", () => {
|
|
222
|
+
const { query } = buildGridQuery({
|
|
223
|
+
model: mockModel,
|
|
224
|
+
partitionKeyValue: "ORG#123",
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
expect(mockQuery.limit).toHaveBeenCalledWith(10);
|
|
228
|
+
expect(query).toBe(mockQuery);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("should use custom hash key name from schema", () => {
|
|
232
|
+
// Create schema with custom hash key name (tenantId instead of pk)
|
|
233
|
+
const customAttributes = {
|
|
234
|
+
tenantId: { type: String, hashKey: true },
|
|
235
|
+
reservationId: { type: String, rangeKey: true },
|
|
236
|
+
status: {
|
|
237
|
+
type: String,
|
|
238
|
+
query: {
|
|
239
|
+
filter: { type: "attribute", operators: ["eq"] },
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const customSchema = createMockSchema(customAttributes);
|
|
245
|
+
const customMockQuery = createMockQuery([]);
|
|
246
|
+
const customMockModel = createMockModel(customSchema, []);
|
|
247
|
+
customMockModel.query = vi.fn(() => customMockQuery);
|
|
248
|
+
|
|
249
|
+
buildGridQuery({
|
|
250
|
+
model: customMockModel,
|
|
251
|
+
partitionKeyValue: "tenant-123",
|
|
252
|
+
limit: 20,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Should query with 'tenantId' not 'pk'
|
|
256
|
+
expect(customMockModel.query).toHaveBeenCalledWith("tenantId");
|
|
257
|
+
expect(customMockQuery.eq).toHaveBeenCalledWith("tenant-123");
|
|
112
258
|
});
|
|
113
259
|
});
|
|
114
260
|
|
|
@@ -132,7 +278,7 @@ describe("GridQueryBuilder", () => {
|
|
|
132
278
|
model: mockModel,
|
|
133
279
|
partitionKeyValue: "ORG#123",
|
|
134
280
|
filterModel,
|
|
135
|
-
|
|
281
|
+
limit: 20,
|
|
136
282
|
});
|
|
137
283
|
|
|
138
284
|
expect(mockQuery.where).toHaveBeenCalledWith("status");
|
|
@@ -150,7 +296,7 @@ describe("GridQueryBuilder", () => {
|
|
|
150
296
|
model: mockModel,
|
|
151
297
|
partitionKeyValue: "ORG#123",
|
|
152
298
|
filterModel,
|
|
153
|
-
|
|
299
|
+
limit: 20,
|
|
154
300
|
});
|
|
155
301
|
|
|
156
302
|
expect(mockQuery.where).toHaveBeenCalledWith("createdAt");
|
|
@@ -172,7 +318,7 @@ describe("GridQueryBuilder", () => {
|
|
|
172
318
|
model: mockModel,
|
|
173
319
|
partitionKeyValue: "ORG#123",
|
|
174
320
|
filterModel,
|
|
175
|
-
|
|
321
|
+
limit: 20,
|
|
176
322
|
});
|
|
177
323
|
|
|
178
324
|
expect(mockQuery.between).toHaveBeenCalledWith("2024-01-01", "2024-12-31");
|
|
@@ -187,7 +333,7 @@ describe("GridQueryBuilder", () => {
|
|
|
187
333
|
model: mockModel,
|
|
188
334
|
partitionKeyValue: "ORG#123",
|
|
189
335
|
filterModel,
|
|
190
|
-
|
|
336
|
+
limit: 20,
|
|
191
337
|
});
|
|
192
338
|
|
|
193
339
|
expect(mockQuery.using).toHaveBeenCalledWith("status-createdAt-index");
|
|
@@ -203,7 +349,7 @@ describe("GridQueryBuilder", () => {
|
|
|
203
349
|
model: mockModel,
|
|
204
350
|
partitionKeyValue: "ORG#123",
|
|
205
351
|
filterModel,
|
|
206
|
-
|
|
352
|
+
limit: 20,
|
|
207
353
|
});
|
|
208
354
|
}).toThrow("Filtering not allowed");
|
|
209
355
|
});
|
|
@@ -218,7 +364,7 @@ describe("GridQueryBuilder", () => {
|
|
|
218
364
|
model: mockModel,
|
|
219
365
|
partitionKeyValue: "ORG#123",
|
|
220
366
|
filterModel,
|
|
221
|
-
|
|
367
|
+
limit: 20,
|
|
222
368
|
});
|
|
223
369
|
}).toThrow("not allowed");
|
|
224
370
|
});
|
|
@@ -242,7 +388,7 @@ describe("GridQueryBuilder", () => {
|
|
|
242
388
|
model: mockModel,
|
|
243
389
|
partitionKeyValue: "ORG#123",
|
|
244
390
|
sortModel,
|
|
245
|
-
|
|
391
|
+
limit: 20,
|
|
246
392
|
});
|
|
247
393
|
|
|
248
394
|
expect(mockQuery.sort).toHaveBeenCalledWith("descending");
|
|
@@ -255,7 +401,7 @@ describe("GridQueryBuilder", () => {
|
|
|
255
401
|
model: mockModel,
|
|
256
402
|
partitionKeyValue: "ORG#123",
|
|
257
403
|
sortModel,
|
|
258
|
-
|
|
404
|
+
limit: 20,
|
|
259
405
|
});
|
|
260
406
|
|
|
261
407
|
// Ascending is default, so sort() should not be called
|
|
@@ -273,7 +419,7 @@ describe("GridQueryBuilder", () => {
|
|
|
273
419
|
model: mockModel,
|
|
274
420
|
partitionKeyValue: "ORG#123",
|
|
275
421
|
sortModel,
|
|
276
|
-
|
|
422
|
+
limit: 20,
|
|
277
423
|
});
|
|
278
424
|
}).toThrow(/sorting by one field/);
|
|
279
425
|
});
|
|
@@ -286,13 +432,13 @@ describe("GridQueryBuilder", () => {
|
|
|
286
432
|
model: mockModel,
|
|
287
433
|
partitionKeyValue: "ORG#123",
|
|
288
434
|
sortModel,
|
|
289
|
-
|
|
435
|
+
limit: 20,
|
|
290
436
|
});
|
|
291
437
|
}).toThrow("Sorting not allowed");
|
|
292
438
|
});
|
|
293
439
|
});
|
|
294
440
|
|
|
295
|
-
describe("buildGridQuery -
|
|
441
|
+
describe("buildGridQuery - Limit and Cursor", () => {
|
|
296
442
|
let mockModel;
|
|
297
443
|
let mockQuery;
|
|
298
444
|
|
|
@@ -303,11 +449,11 @@ describe("GridQueryBuilder", () => {
|
|
|
303
449
|
mockModel.query = vi.fn(() => mockQuery);
|
|
304
450
|
});
|
|
305
451
|
|
|
306
|
-
it("should apply
|
|
452
|
+
it("should apply limit", () => {
|
|
307
453
|
buildGridQuery({
|
|
308
454
|
model: mockModel,
|
|
309
455
|
partitionKeyValue: "ORG#123",
|
|
310
|
-
|
|
456
|
+
limit: 50,
|
|
311
457
|
});
|
|
312
458
|
|
|
313
459
|
expect(mockQuery.limit).toHaveBeenCalledWith(50);
|
|
@@ -320,7 +466,7 @@ describe("GridQueryBuilder", () => {
|
|
|
320
466
|
const firstResult = buildGridQuery({
|
|
321
467
|
model: mockModel,
|
|
322
468
|
partitionKeyValue: "ORG#123",
|
|
323
|
-
|
|
469
|
+
limit: 20,
|
|
324
470
|
});
|
|
325
471
|
|
|
326
472
|
// Create cursor with the actual queryHash
|
|
@@ -332,7 +478,7 @@ describe("GridQueryBuilder", () => {
|
|
|
332
478
|
buildGridQuery({
|
|
333
479
|
model: mockModel,
|
|
334
480
|
partitionKeyValue: "ORG#123",
|
|
335
|
-
|
|
481
|
+
limit: 20,
|
|
336
482
|
cursor,
|
|
337
483
|
});
|
|
338
484
|
|
|
@@ -351,7 +497,7 @@ describe("GridQueryBuilder", () => {
|
|
|
351
497
|
buildGridQuery({
|
|
352
498
|
model: mockModel,
|
|
353
499
|
partitionKeyValue: "ORG#123",
|
|
354
|
-
|
|
500
|
+
limit: 50, // Different limit
|
|
355
501
|
cursor,
|
|
356
502
|
});
|
|
357
503
|
}).toThrow("Cursor does not match");
|
|
@@ -374,7 +520,7 @@ describe("GridQueryBuilder", () => {
|
|
|
374
520
|
items: [{ field: "status", operator: "eq", value: "active" }],
|
|
375
521
|
},
|
|
376
522
|
sortModel: [{ field: "createdAt", sort: "desc" }],
|
|
377
|
-
|
|
523
|
+
limit: 20,
|
|
378
524
|
};
|
|
379
525
|
|
|
380
526
|
const result1 = buildGridQuery(params);
|
|
@@ -387,7 +533,7 @@ describe("GridQueryBuilder", () => {
|
|
|
387
533
|
const baseParams = {
|
|
388
534
|
model: mockModel,
|
|
389
535
|
partitionKeyValue: "ORG#123",
|
|
390
|
-
|
|
536
|
+
limit: 20,
|
|
391
537
|
};
|
|
392
538
|
|
|
393
539
|
const result1 = buildGridQuery({
|
|
@@ -407,7 +553,7 @@ describe("GridQueryBuilder", () => {
|
|
|
407
553
|
expect(result1.queryHash).not.toBe(result2.queryHash);
|
|
408
554
|
});
|
|
409
555
|
|
|
410
|
-
it("should return different hash when
|
|
556
|
+
it("should return different hash when limit changes", () => {
|
|
411
557
|
const baseParams = {
|
|
412
558
|
model: mockModel,
|
|
413
559
|
partitionKeyValue: "ORG#123",
|
|
@@ -415,12 +561,12 @@ describe("GridQueryBuilder", () => {
|
|
|
415
561
|
|
|
416
562
|
const result1 = buildGridQuery({
|
|
417
563
|
...baseParams,
|
|
418
|
-
|
|
564
|
+
limit: 20,
|
|
419
565
|
});
|
|
420
566
|
|
|
421
567
|
const result2 = buildGridQuery({
|
|
422
568
|
...baseParams,
|
|
423
|
-
|
|
569
|
+
limit: 50,
|
|
424
570
|
});
|
|
425
571
|
|
|
426
572
|
expect(result1.queryHash).not.toBe(result2.queryHash);
|
|
@@ -438,7 +584,7 @@ describe("GridQueryBuilder", () => {
|
|
|
438
584
|
mockModel.query = vi.fn(() => mockQuery);
|
|
439
585
|
});
|
|
440
586
|
|
|
441
|
-
it("should handle query with filter, sort, and
|
|
587
|
+
it("should handle query with filter, sort, and limit", () => {
|
|
442
588
|
buildGridQuery({
|
|
443
589
|
model: mockModel,
|
|
444
590
|
partitionKeyValue: "ORG#123",
|
|
@@ -446,7 +592,7 @@ describe("GridQueryBuilder", () => {
|
|
|
446
592
|
items: [{ field: "status", operator: "eq", value: "active" }],
|
|
447
593
|
},
|
|
448
594
|
sortModel: [{ field: "createdAt", sort: "desc" }],
|
|
449
|
-
|
|
595
|
+
limit: 25,
|
|
450
596
|
});
|
|
451
597
|
|
|
452
598
|
// Verify all operations were applied
|
|
@@ -464,7 +610,7 @@ describe("GridQueryBuilder", () => {
|
|
|
464
610
|
partitionKeyValue: "ORG#123",
|
|
465
611
|
filterModel: { items: [] },
|
|
466
612
|
sortModel: [],
|
|
467
|
-
|
|
613
|
+
limit: 20,
|
|
468
614
|
});
|
|
469
615
|
|
|
470
616
|
expect(query).toBeDefined();
|