@snowtop/ent 0.2.0-alpha.6.test2 → 0.2.0-alpha.8
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/core/clause.d.ts +44 -0
- package/core/clause.js +75 -2
- package/core/db.d.ts +1 -1
- package/core/db.js +2 -2
- package/core/ent.d.ts +10 -2
- package/core/ent.js +11 -18
- package/core/loaders/assoc_edge_loader.d.ts +1 -1
- package/core/loaders/assoc_edge_loader.js +3 -3
- package/core/query/assoc_query.d.ts +2 -2
- package/core/query/assoc_query.js +14 -2
- package/core/query/custom_clause_query.d.ts +8 -6
- package/core/query/custom_clause_query.js +22 -7
- package/core/query/custom_query.d.ts +2 -2
- package/core/query/custom_query.js +1 -1
- package/core/query/query.d.ts +3 -7
- package/core/query/query.js +73 -199
- package/core/query/shared_test.d.ts +3 -3
- package/core/query/shared_test.js +31 -23
- package/core/query_impl.d.ts +0 -1
- package/graphql/query/edge_connection.d.ts +5 -4
- package/graphql/query/edge_connection.js +34 -14
- package/graphql/query/shared_edge_connection.d.ts +2 -2
- package/graphql/query/shared_edge_connection.js +26 -13
- package/package.json +1 -1
- package/testutils/db_mock.d.ts +1 -1
- package/testutils/db_mock.js +1 -1
- package/testutils/ent-graphql-tests/index.d.ts +1 -1
- package/testutils/ent-graphql-tests/index.js +5 -5
- package/testutils/fake_data/user_query.d.ts +5 -5
- package/testutils/fake_data/user_query.js +11 -14
- package/tsc/compilerOptions.js +1 -4
package/core/query/query.js
CHANGED
|
@@ -27,59 +27,34 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.BaseEdgeQuery = void 0;
|
|
30
|
-
const ent_1 = require("../ent");
|
|
31
|
-
const clause = __importStar(require("../clause"));
|
|
32
30
|
const memoizee_1 = __importDefault(require("memoizee"));
|
|
31
|
+
const types_1 = require("util/types");
|
|
32
|
+
const clause = __importStar(require("../clause"));
|
|
33
|
+
const ent_1 = require("../ent");
|
|
33
34
|
const privacy_1 = require("../privacy");
|
|
34
|
-
const uuid_1 = require("uuid");
|
|
35
35
|
const query_impl_1 = require("../query_impl");
|
|
36
|
-
const types_1 = require("util/types");
|
|
37
36
|
// TODO can we generalize EdgeQuery to support any clause
|
|
38
37
|
function assertPositive(n) {
|
|
39
38
|
if (n < 0) {
|
|
40
39
|
throw new Error("cannot use a negative number");
|
|
41
40
|
}
|
|
42
41
|
}
|
|
43
|
-
function
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (isNaN(val)) {
|
|
50
|
-
return val;
|
|
42
|
+
function translateCursorToKeyValues(cursor, opts) {
|
|
43
|
+
const { keys } = opts;
|
|
44
|
+
const decoded = atob(cursor);
|
|
45
|
+
let cursorData = [];
|
|
46
|
+
try {
|
|
47
|
+
cursorData = JSON.parse(decoded);
|
|
51
48
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return val;
|
|
49
|
+
catch (error) {
|
|
50
|
+
throw new Error(`Bad cursor format ${cursor} passed`);
|
|
55
51
|
}
|
|
56
|
-
return time;
|
|
57
|
-
}
|
|
58
|
-
function assertValidCursor(cursor, opts) {
|
|
59
|
-
let decoded = Buffer.from(cursor, "base64").toString("ascii");
|
|
60
|
-
let parts = decoded.split(":");
|
|
61
|
-
const { keys } = opts;
|
|
62
52
|
// invalid or unknown cursor. nothing to do here.
|
|
63
|
-
// we should have the same number of parts as keys
|
|
64
|
-
if (
|
|
53
|
+
// we should have the same number of parts as keys
|
|
54
|
+
if (cursorData.length !== keys.length) {
|
|
65
55
|
throw new Error(`invalid cursor ${cursor} passed`);
|
|
66
56
|
}
|
|
67
|
-
|
|
68
|
-
for (let i = 0; i < keys.length; i++) {
|
|
69
|
-
const key = keys[i];
|
|
70
|
-
const keyPart = parts[i * 2];
|
|
71
|
-
if (key !== keyPart) {
|
|
72
|
-
throw new Error(`invalid cursor ${cursor} passed. expected ${key}. got ${keyPart} as key of field`);
|
|
73
|
-
}
|
|
74
|
-
const val = parts[i * 2 + 1];
|
|
75
|
-
// uuid, don't parse int since it tries to validate just first part
|
|
76
|
-
if ((0, uuid_1.validate)(val)) {
|
|
77
|
-
values.push(val);
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
values.push(convertToIntMaybe(val));
|
|
81
|
-
}
|
|
82
|
-
return values;
|
|
57
|
+
return cursorData;
|
|
83
58
|
}
|
|
84
59
|
const orderbyRegex = new RegExp(/([0-9a-z_]+)[ ]?([0-9a-z_]+)?/i);
|
|
85
60
|
class FirstFilter {
|
|
@@ -87,14 +62,14 @@ class FirstFilter {
|
|
|
87
62
|
this.options = options;
|
|
88
63
|
this.pageMap = new Map();
|
|
89
64
|
this.usedQuery = false;
|
|
90
|
-
this.
|
|
65
|
+
this.cursorKeyValues = [];
|
|
91
66
|
assertPositive(options.limit);
|
|
92
|
-
this.sortCol = options.sortCol;
|
|
93
67
|
if (options.after) {
|
|
94
|
-
this.
|
|
68
|
+
this.cursorKeyValues = translateCursorToKeyValues(options.after, {
|
|
95
69
|
keys: options.cursorKeys,
|
|
96
70
|
});
|
|
97
|
-
this.offset =
|
|
71
|
+
this.offset =
|
|
72
|
+
this.cursorKeyValues[this.cursorKeyValues.length - 1][1] ?? undefined;
|
|
98
73
|
}
|
|
99
74
|
this.edgeQuery = options.query;
|
|
100
75
|
}
|
|
@@ -158,58 +133,25 @@ class FirstFilter {
|
|
|
158
133
|
// so we'd need a way to indicate whether this is done in sql or not
|
|
159
134
|
return edges;
|
|
160
135
|
}
|
|
161
|
-
getOffsetForQuery() {
|
|
162
|
-
// cursorCol maps to offset which we get from the cursor in assertValidCursor
|
|
163
|
-
return this.options.cursorColIsDate
|
|
164
|
-
? new Date(this.offset).toISOString()
|
|
165
|
-
: this.offset;
|
|
166
|
-
}
|
|
167
|
-
getSortValueForQuery() {
|
|
168
|
-
// sortCol maps to the value we're comparing against
|
|
169
|
-
return this.options.sortColIsDate
|
|
170
|
-
? new Date(this.cursorValues[1]).toISOString()
|
|
171
|
-
: this.cursorValues[1];
|
|
172
|
-
}
|
|
173
136
|
async query(options) {
|
|
174
137
|
this.usedQuery = true;
|
|
175
138
|
// we fetch an extra one to see if we're at the end
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (this.options.cursorCol !== this.sortCol) {
|
|
183
|
-
// we also sort cursor col in same direction. (direction doesn't matter)
|
|
184
|
-
orderby.push({
|
|
185
|
-
column: this.options.cursorCol,
|
|
186
|
-
direction: orderby[0].direction,
|
|
187
|
-
});
|
|
188
|
-
if (this.offset) {
|
|
189
|
-
const res = this.edgeQuery.getTableName();
|
|
190
|
-
let tableName = (0, types_1.isPromise)(res) ? await res : res;
|
|
191
|
-
// using a join, we already know sortCol and cursorCol are different
|
|
192
|
-
// we have encoded both values in the cursor
|
|
193
|
-
// includeSortColInCursor() is true in this case
|
|
194
|
-
if (this.cursorValues.length === 2 &&
|
|
195
|
-
this.options.cursorKeys.length === 2) {
|
|
196
|
-
options.clause = clause.AndOptional(options.clause, clause.PaginationMultipleColsQuery(this.sortCol, this.options.cursorCol, less, this.getSortValueForQuery(), this.getOffsetForQuery(), this.options.fieldOptions?.fieldsAlias ??
|
|
197
|
-
this.options.fieldOptions?.alias));
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
// inner col time
|
|
201
|
-
options.clause = clause.AndOptional(options.clause, clause.PaginationMultipleColsSubQuery(this.sortCol, less ? "<" : ">", tableName, this.options.cursorCol, this.getOffsetForQuery()));
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
if (this.offset) {
|
|
207
|
-
const clauseFn = less ? clause.Less : clause.Greater;
|
|
208
|
-
const val = this.getOffsetForQuery();
|
|
209
|
-
options.clause = clause.AndOptional(options.clause, clauseFn(this.sortCol, val));
|
|
139
|
+
options.limit = this.options.limit + 1;
|
|
140
|
+
const orderBy = this.options.orderby;
|
|
141
|
+
if (this.offset) {
|
|
142
|
+
const keyValuePairs = {};
|
|
143
|
+
for (const [key, value] of this.cursorKeyValues) {
|
|
144
|
+
keyValuePairs[key] = value;
|
|
210
145
|
}
|
|
146
|
+
options.clause = clause.AndOptional(options.clause, clause.PaginationUnboundColsQuery(orderBy.map((orderBy) => ({
|
|
147
|
+
sortCol: orderBy.column,
|
|
148
|
+
sortValue: keyValuePairs[orderBy.column],
|
|
149
|
+
direction: orderBy.direction,
|
|
150
|
+
nullsPlacement: orderBy.nullsPlacement,
|
|
151
|
+
overrideAlias: orderBy.alias,
|
|
152
|
+
}))));
|
|
211
153
|
}
|
|
212
|
-
options.orderby =
|
|
154
|
+
options.orderby = orderBy;
|
|
213
155
|
return options;
|
|
214
156
|
}
|
|
215
157
|
// TODO?
|
|
@@ -223,14 +165,14 @@ class LastFilter {
|
|
|
223
165
|
constructor(options) {
|
|
224
166
|
this.options = options;
|
|
225
167
|
this.pageMap = new Map();
|
|
226
|
-
this.
|
|
168
|
+
this.cursorKeyValues = [];
|
|
227
169
|
assertPositive(options.limit);
|
|
228
|
-
this.sortCol = options.sortCol;
|
|
229
170
|
if (options.before) {
|
|
230
|
-
this.
|
|
171
|
+
this.cursorKeyValues = translateCursorToKeyValues(options.before, {
|
|
231
172
|
keys: options.cursorKeys,
|
|
232
173
|
});
|
|
233
|
-
this.offset =
|
|
174
|
+
this.offset =
|
|
175
|
+
this.cursorKeyValues[this.cursorKeyValues.length - 1][1] ?? undefined;
|
|
234
176
|
}
|
|
235
177
|
this.edgeQuery = options.query;
|
|
236
178
|
}
|
|
@@ -261,56 +203,25 @@ class LastFilter {
|
|
|
261
203
|
}
|
|
262
204
|
return ret;
|
|
263
205
|
}
|
|
264
|
-
// copied from FirstFilter
|
|
265
|
-
getOffsetForQuery() {
|
|
266
|
-
// cursorCol maps to offset which we get from the cursor in assertValidCursor
|
|
267
|
-
return this.options.cursorColIsDate
|
|
268
|
-
? new Date(this.offset).toISOString()
|
|
269
|
-
: this.offset;
|
|
270
|
-
}
|
|
271
|
-
getSortValueForQuery() {
|
|
272
|
-
// sortCol maps to the value we're comparing against
|
|
273
|
-
return this.options.sortColIsDate
|
|
274
|
-
? new Date(this.cursorValues[1]).toISOString()
|
|
275
|
-
: this.cursorValues[1];
|
|
276
|
-
}
|
|
277
206
|
async query(options) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
// we have encoded both values in the cursor
|
|
287
|
-
// includeSortColInCursor() is true in this case
|
|
288
|
-
if (this.cursorValues.length === 2 &&
|
|
289
|
-
this.options.cursorKeys.length === 2) {
|
|
290
|
-
options.clause = clause.AndOptional(options.clause, clause.PaginationMultipleColsQuery(this.sortCol, this.options.cursorCol,
|
|
291
|
-
// flipped here since we're going in the opposite direction
|
|
292
|
-
!greater, this.getSortValueForQuery(), this.getOffsetForQuery(), this.options.fieldOptions?.fieldsAlias ??
|
|
293
|
-
this.options.fieldOptions?.alias));
|
|
294
|
-
}
|
|
295
|
-
else {
|
|
296
|
-
// inner col time
|
|
297
|
-
options.clause = clause.AndOptional(options.clause, clause.PaginationMultipleColsSubQuery(this.sortCol, greater ? ">" : "<", tableName, this.options.cursorCol, this.getOffsetForQuery()));
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
// we also sort cursor col in same direction. (direction doesn't matter)
|
|
301
|
-
orderby.push({
|
|
302
|
-
column: this.options.cursorCol,
|
|
303
|
-
direction: orderby[0].direction,
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
else {
|
|
307
|
-
if (this.offset) {
|
|
308
|
-
const clauseFn = greater ? clause.Greater : clause.Less;
|
|
309
|
-
const val = this.getOffsetForQuery();
|
|
310
|
-
options.clause = clause.AndOptional(options.clause, clauseFn(this.sortCol, val));
|
|
207
|
+
// we fetch an extra one to see if we're at the end
|
|
208
|
+
options.limit = this.options.limit + 1;
|
|
209
|
+
// we also sort cursor col in same direction. (direction doesn't matter)
|
|
210
|
+
const orderBy = (0, query_impl_1.reverseOrderBy)(this.options.orderby);
|
|
211
|
+
if (this.offset) {
|
|
212
|
+
const keyValuePairs = {};
|
|
213
|
+
for (const [key, value] of this.cursorKeyValues) {
|
|
214
|
+
keyValuePairs[key] = value;
|
|
311
215
|
}
|
|
216
|
+
options.clause = clause.AndOptional(options.clause, clause.PaginationUnboundColsQuery(orderBy.map((orderBy) => ({
|
|
217
|
+
sortCol: orderBy.column,
|
|
218
|
+
sortValue: keyValuePairs[orderBy.column],
|
|
219
|
+
direction: orderBy.direction,
|
|
220
|
+
nullsPlacement: orderBy.nullsPlacement,
|
|
221
|
+
overrideAlias: orderBy.alias,
|
|
222
|
+
}))));
|
|
312
223
|
}
|
|
313
|
-
options.orderby =
|
|
224
|
+
options.orderby = orderBy;
|
|
314
225
|
return options;
|
|
315
226
|
}
|
|
316
227
|
paginationInfo(id) {
|
|
@@ -318,7 +229,7 @@ class LastFilter {
|
|
|
318
229
|
}
|
|
319
230
|
}
|
|
320
231
|
class BaseEdgeQuery {
|
|
321
|
-
constructor(viewer,
|
|
232
|
+
constructor(viewer, options) {
|
|
322
233
|
this.viewer = viewer;
|
|
323
234
|
this.filters = [];
|
|
324
235
|
this.edges = new Map();
|
|
@@ -377,57 +288,29 @@ class BaseEdgeQuery {
|
|
|
377
288
|
await Promise.all(promises);
|
|
378
289
|
return results;
|
|
379
290
|
};
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
this.edgeQueryOptions = {
|
|
388
|
-
cursorCol,
|
|
389
|
-
orderby: [
|
|
390
|
-
{
|
|
391
|
-
column: sortCol,
|
|
392
|
-
direction: "DESC",
|
|
393
|
-
},
|
|
394
|
-
],
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
else {
|
|
398
|
-
if (typeof sortColOrOptions.orderby === "string") {
|
|
399
|
-
sortCol = sortColOrOptions.orderby;
|
|
400
|
-
}
|
|
401
|
-
else {
|
|
402
|
-
// TODO this orderby isn't consistent and this logic needs to be changed anywhere that's using this and this.getSortCol()
|
|
403
|
-
sortCol = sortColOrOptions.orderby[0].column;
|
|
404
|
-
sortColIsDate = sortColOrOptions.orderby[0].dateColumn ?? false;
|
|
405
|
-
}
|
|
406
|
-
cursorCol = sortColOrOptions.cursorCol;
|
|
407
|
-
cursorColIsDate = sortColOrOptions.cursorColIsDate ?? false;
|
|
408
|
-
this.edgeQueryOptions = sortColOrOptions;
|
|
409
|
-
}
|
|
410
|
-
this.sortCol = sortCol;
|
|
411
|
-
this.sortColIsDate = sortColIsDate;
|
|
412
|
-
this.cursorColIsDate = cursorColIsDate;
|
|
413
|
-
let m = orderbyRegex.exec(sortCol);
|
|
414
|
-
if (!m) {
|
|
415
|
-
throw new Error(`invalid sort column ${sortCol}`);
|
|
416
|
-
}
|
|
417
|
-
this.sortCol = m[1];
|
|
418
|
-
if (m[2]) {
|
|
419
|
-
throw new Error(`passing direction in sort column is not supproted. use orderby`);
|
|
291
|
+
// we also sort cursor col in same direction. (direction doesn't matter)
|
|
292
|
+
const orderBy = [...options.orderby];
|
|
293
|
+
if (!options.orderby.some((orderBy) => orderBy.column === options.cursorCol)) {
|
|
294
|
+
orderBy.push({
|
|
295
|
+
column: options.cursorCol,
|
|
296
|
+
direction: orderBy?.[0].direction || "DESC",
|
|
297
|
+
});
|
|
420
298
|
}
|
|
421
|
-
|
|
299
|
+
orderBy.forEach((o) => {
|
|
300
|
+
o.alias =
|
|
301
|
+
o.alias ??
|
|
302
|
+
(options.fieldOptions?.disableFieldsAlias
|
|
303
|
+
? undefined
|
|
304
|
+
: (options.fieldOptions?.fieldsAlias ?? options.fieldOptions?.alias));
|
|
305
|
+
});
|
|
306
|
+
this.edgeQueryOptions = { ...options, orderby: orderBy };
|
|
307
|
+
this.cursorCol = options.cursorCol;
|
|
308
|
+
this.cursorKeys = orderBy.map((orderBy) => orderBy.column);
|
|
422
309
|
this.memoizedloadEdges = (0, memoizee_1.default)(this.loadEdges.bind(this));
|
|
423
310
|
this.genIDInfosToFetch = (0, memoizee_1.default)(this.genIDInfosToFetchImpl.bind(this));
|
|
424
|
-
this.cursorKeys.push(this.cursorCol);
|
|
425
|
-
if (this.includeSortColInCursor(this.edgeQueryOptions)) {
|
|
426
|
-
this.cursorKeys.push(this.sortCol);
|
|
427
|
-
}
|
|
428
311
|
}
|
|
429
|
-
|
|
430
|
-
return this.
|
|
312
|
+
getCursorCol() {
|
|
313
|
+
return this.cursorCol;
|
|
431
314
|
}
|
|
432
315
|
getPrivacyPolicy() {
|
|
433
316
|
// default PrivacyPolicy is always allow. nothing to do here
|
|
@@ -439,11 +322,8 @@ class BaseEdgeQuery {
|
|
|
439
322
|
this.filters.push(new FirstFilter({
|
|
440
323
|
limit: n,
|
|
441
324
|
after,
|
|
442
|
-
sortCol: this.sortCol,
|
|
443
325
|
cursorCol: this.cursorCol,
|
|
444
326
|
cursorKeys: this.cursorKeys,
|
|
445
|
-
cursorColIsDate: this.cursorColIsDate,
|
|
446
|
-
sortColIsDate: this.sortColIsDate,
|
|
447
327
|
orderby: this.edgeQueryOptions.orderby,
|
|
448
328
|
query: this,
|
|
449
329
|
fieldOptions: this.edgeQueryOptions.fieldOptions,
|
|
@@ -466,11 +346,8 @@ class BaseEdgeQuery {
|
|
|
466
346
|
this.filters.push(new LastFilter({
|
|
467
347
|
limit: n,
|
|
468
348
|
before,
|
|
469
|
-
sortCol: this.sortCol,
|
|
470
349
|
cursorCol: this.cursorCol,
|
|
471
350
|
cursorKeys: this.cursorKeys,
|
|
472
|
-
cursorColIsDate: this.cursorColIsDate,
|
|
473
|
-
sortColIsDate: this.sortColIsDate,
|
|
474
351
|
orderby: this.edgeQueryOptions.orderby,
|
|
475
352
|
query: this,
|
|
476
353
|
fieldOptions: this.edgeQueryOptions.fieldOptions,
|
|
@@ -583,13 +460,10 @@ class BaseEdgeQuery {
|
|
|
583
460
|
this.queryDispatched = true;
|
|
584
461
|
return this.edges;
|
|
585
462
|
}
|
|
586
|
-
includeSortColInCursor(options) {
|
|
587
|
-
return false;
|
|
588
|
-
}
|
|
589
463
|
getCursor(row) {
|
|
590
464
|
return (0, ent_1.getCursor)({
|
|
591
465
|
row,
|
|
592
|
-
|
|
466
|
+
cursorKeys: this.cursorKeys,
|
|
593
467
|
});
|
|
594
468
|
}
|
|
595
469
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Data, ID, Viewer } from "../base";
|
|
2
|
-
import { FakeUser, FakeContact } from "../../testutils/fake_data/index";
|
|
3
|
-
import { EdgeQuery } from "./query";
|
|
4
1
|
import { BuilderSchema } from "../../testutils/builder";
|
|
2
|
+
import { FakeContact, FakeUser } from "../../testutils/fake_data/index";
|
|
5
3
|
import { MockLogs } from "../../testutils/mock_log";
|
|
4
|
+
import { Data, ID, Viewer } from "../base";
|
|
6
5
|
import { Clause } from "../clause";
|
|
7
6
|
import { OrderBy } from "../query_impl";
|
|
7
|
+
import { EdgeQuery } from "./query";
|
|
8
8
|
interface options<TData extends Data> {
|
|
9
9
|
newQuery: (v: Viewer, user: FakeUser) => EdgeQuery<FakeUser, FakeContact, TData>;
|
|
10
10
|
tableName: string;
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.commonTests = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
4
|
+
const action_1 = require("../../action");
|
|
5
|
+
const builder_1 = require("../../testutils/builder");
|
|
6
|
+
const test_context_1 = require("../../testutils/context/test_context");
|
|
7
|
+
const temp_db_1 = require("../../testutils/db/temp_db");
|
|
7
8
|
const index_1 = require("../../testutils/fake_data/index");
|
|
8
9
|
const test_helpers_1 = require("../../testutils/fake_data/test_helpers");
|
|
9
|
-
const
|
|
10
|
-
const test_context_1 = require("../../testutils/context/test_context");
|
|
11
|
-
const logger_1 = require("../logger");
|
|
10
|
+
const query_1 = require("../../testutils/query");
|
|
12
11
|
const test_edge_global_schema_1 = require("../../testutils/test_edge_global_schema");
|
|
13
|
-
const builder_1 = require("../../testutils/builder");
|
|
14
|
-
const action_1 = require("../../action");
|
|
15
12
|
const clause_1 = require("../clause");
|
|
13
|
+
const ent_1 = require("../ent");
|
|
14
|
+
const global_schema_1 = require("../global_schema");
|
|
15
|
+
const logger_1 = require("../logger");
|
|
16
16
|
const query_impl_1 = require("../query_impl");
|
|
17
|
-
const
|
|
17
|
+
const viewer_1 = require("../viewer");
|
|
18
18
|
const commonTests = (opts) => {
|
|
19
19
|
(0, logger_1.setLogLevels)(["query", "error"]);
|
|
20
20
|
const ml = opts.ml;
|
|
@@ -154,7 +154,7 @@ const commonTests = (opts) => {
|
|
|
154
154
|
let expLimit = disablePaginationBump ? limit : limit + 1;
|
|
155
155
|
expect(whereClause, `${i}`).toBe(
|
|
156
156
|
// default limit
|
|
157
|
-
`${opts.clause.clause(1)} ORDER BY ${(0, query_impl_1.getOrderByPhrase)(orderby)}
|
|
157
|
+
`${opts.clause.clause(1)} ORDER BY ${(0, query_impl_1.getOrderByPhrase)(orderby)} LIMIT ${expLimit}`);
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
function verifyCountQuery({ length = 1, numQueries = 1 }) {
|
|
@@ -167,40 +167,47 @@ const commonTests = (opts) => {
|
|
|
167
167
|
function verifyFirstAfterCursorQuery(filter, length = 1, limit = 3) {
|
|
168
168
|
// cache showing up in a few because of cross runs...
|
|
169
169
|
expect(ml.logs.length).toBeGreaterThanOrEqual(length);
|
|
170
|
-
const uniqCol = isCustomQuery(filter) ? "id" : "id2";
|
|
171
170
|
let parts = opts.clause.clause(1).split(" AND ");
|
|
172
|
-
const cmp = (0, clause_1.
|
|
171
|
+
const cmp = (0, clause_1.PaginationUnboundColsQuery)(opts.orderby.map((orderBy) => ({
|
|
172
|
+
direction: orderBy.direction,
|
|
173
|
+
sortCol: orderBy.column,
|
|
174
|
+
sortValue: "_",
|
|
175
|
+
}))).clause(opts.clause.values().length + 1);
|
|
173
176
|
// order of parts is different in custom query seemingly
|
|
174
177
|
const customQuery = isCustomQuery(filter);
|
|
175
178
|
if (!customQuery && parts[parts.length - 1] === "deleted_at IS NULL") {
|
|
176
179
|
parts = parts
|
|
177
180
|
.slice(0, parts.length - 1)
|
|
178
|
-
.concat([cmp
|
|
181
|
+
.concat([`(${cmp})`, "deleted_at IS NULL"]);
|
|
179
182
|
}
|
|
180
183
|
else {
|
|
181
|
-
parts.push(cmp);
|
|
184
|
+
parts.push(`(${cmp})`);
|
|
182
185
|
}
|
|
183
|
-
expect((0, query_1.getWhereClause)(ml.logs[0])).toBe(`${parts.join(" AND ")} ORDER BY ${(0, query_impl_1.getOrderByPhrase)(opts.orderby)}
|
|
186
|
+
expect((0, query_1.getWhereClause)(ml.logs[0])).toBe(`${parts.join(" AND ")} ORDER BY ${(0, query_impl_1.getOrderByPhrase)(opts.orderby)} LIMIT ${limit + 1}`);
|
|
184
187
|
}
|
|
185
188
|
function verifyLastBeforeCursorQuery(filter, { length = 1, limit = 3, orderby = opts.orderby }) {
|
|
186
189
|
// cache showing up in a few because of cross runs...
|
|
187
190
|
expect(ml.logs.length).toBeGreaterThanOrEqual(length);
|
|
188
|
-
const uniqCol = isCustomQuery(filter) ? "id" : "id2";
|
|
189
191
|
let parts = opts.clause.clause(1).split(" AND ");
|
|
190
|
-
const cmp = (0, clause_1.
|
|
192
|
+
const cmp = (0, clause_1.PaginationUnboundColsQuery)(opts.orderby.map((orderBy) => ({
|
|
193
|
+
// Reverse order for "last" cursor
|
|
194
|
+
direction: orderBy.direction === "DESC" ? "ASC" : "DESC",
|
|
195
|
+
sortCol: orderBy.column,
|
|
196
|
+
sortValue: "_",
|
|
197
|
+
}))).clause(opts.clause.values().length + 1);
|
|
191
198
|
// order of parts is different in custom query seemingly
|
|
192
199
|
const customQuery = isCustomQuery(filter);
|
|
193
200
|
if (!customQuery && parts[parts.length - 1] === "deleted_at IS NULL") {
|
|
194
201
|
parts = parts
|
|
195
202
|
.slice(0, parts.length - 1)
|
|
196
|
-
.concat([cmp
|
|
203
|
+
.concat([`(${cmp})`, "deleted_at IS NULL"]);
|
|
197
204
|
}
|
|
198
205
|
else {
|
|
199
|
-
parts.push(cmp);
|
|
206
|
+
parts.push(`(${cmp})`);
|
|
200
207
|
}
|
|
201
208
|
expect((0, query_1.getWhereClause)(ml.logs[0])).toBe(
|
|
202
209
|
// extra fetched for pagination
|
|
203
|
-
`${parts.join(" AND ")} ORDER BY ${(0, query_impl_1.getOrderByPhrase)(orderby)}
|
|
210
|
+
`${parts.join(" AND ")} ORDER BY ${(0, query_impl_1.getOrderByPhrase)(orderby)} LIMIT ${limit + 1}`);
|
|
204
211
|
}
|
|
205
212
|
function getViewer() {
|
|
206
213
|
return new viewer_1.LoggedOutViewer();
|
|
@@ -210,7 +217,8 @@ const commonTests = (opts) => {
|
|
|
210
217
|
if (isCustomQuery(q)) {
|
|
211
218
|
opts = {
|
|
212
219
|
row: contacts[idx],
|
|
213
|
-
|
|
220
|
+
cursorKeys: ["created_at", "id"],
|
|
221
|
+
rowKeys: ["createdAt", "id"],
|
|
214
222
|
};
|
|
215
223
|
}
|
|
216
224
|
else {
|
|
@@ -218,8 +226,8 @@ const commonTests = (opts) => {
|
|
|
218
226
|
// is from assoc_edge table id2 field and so cursor takes it from there
|
|
219
227
|
opts = {
|
|
220
228
|
row: contacts[idx],
|
|
221
|
-
|
|
222
|
-
|
|
229
|
+
cursorKeys: ["time", "id2"],
|
|
230
|
+
rowKeys: ["createdAt", "id"],
|
|
223
231
|
};
|
|
224
232
|
}
|
|
225
233
|
return (0, ent_1.getCursor)(opts);
|
package/core/query_impl.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ export interface OrderByOption {
|
|
|
4
4
|
direction: "ASC" | "DESC";
|
|
5
5
|
alias?: string;
|
|
6
6
|
nullsPlacement?: "first" | "last";
|
|
7
|
-
dateColumn?: boolean;
|
|
8
7
|
}
|
|
9
8
|
export type OrderBy = OrderByOption[];
|
|
10
9
|
export declare function getOrderByPhrase(orderby: OrderBy, alias?: string): string;
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import { EdgeQuery, PaginationInfo } from "../../core/query/query";
|
|
2
1
|
import { Data, Ent, ID, Viewer } from "../../core/base";
|
|
2
|
+
import { EdgeQuery, PaginationInfo } from "../../core/query/query";
|
|
3
3
|
export interface GraphQLEdge<T extends Data> {
|
|
4
4
|
edge: T;
|
|
5
5
|
node: Ent;
|
|
6
6
|
cursor: string;
|
|
7
7
|
}
|
|
8
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
8
9
|
interface edgeQueryCtr<T extends Ent, TEdge extends Data, TViewer extends Viewer> {
|
|
9
|
-
(v: TViewer, src: T): EdgeQuery<T, Ent, TEdge
|
|
10
|
+
(v: TViewer, src: T): MaybePromise<EdgeQuery<T, Ent, TEdge>>;
|
|
10
11
|
}
|
|
11
12
|
interface edgeQueryCtr2<T extends Ent, TEdge extends Data, TViewer extends Viewer> {
|
|
12
|
-
(v: TViewer): EdgeQuery<T, Ent, TEdge
|
|
13
|
+
(v: TViewer): MaybePromise<EdgeQuery<T, Ent, TEdge>>;
|
|
13
14
|
}
|
|
14
15
|
export declare class GraphQLEdgeConnection<TSource extends Ent, TEdge extends Data, TViewer extends Viewer = Viewer> {
|
|
15
|
-
query: EdgeQuery<TSource, Ent, TEdge
|
|
16
|
+
query: Promise<EdgeQuery<TSource, Ent, TEdge>>;
|
|
16
17
|
private results;
|
|
17
18
|
private viewer;
|
|
18
19
|
private source?;
|