@snowtop/ent 0.1.0 → 0.1.2
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/action/operations.js +2 -1
- package/core/query/assoc_query.d.ts +3 -0
- package/core/query/assoc_query.js +71 -0
- package/core/query/query.d.ts +3 -0
- package/core/query/query.js +20 -6
- package/core/query/shared_assoc_test.js +297 -12
- package/core/query/shared_test.js +2 -2
- package/package.json +1 -1
- package/testutils/fake_data/test_helpers.js +2 -1
package/action/operations.js
CHANGED
|
@@ -390,7 +390,8 @@ class EdgeOperation {
|
|
|
390
390
|
// maybe when actions exist?
|
|
391
391
|
fields["time"] = new Date().toISOString();
|
|
392
392
|
}
|
|
393
|
-
|
|
393
|
+
// update time if we're trying to insert a row with the same id1, edge_type, id2
|
|
394
|
+
const onConflictFields = ["data", "time"];
|
|
394
395
|
const extraEdgeFields = (0, global_schema_1.__getGlobalSchema)()?.extraEdgeFields;
|
|
395
396
|
if (extraEdgeFields) {
|
|
396
397
|
for (const name in extraEdgeFields) {
|
|
@@ -24,6 +24,9 @@ export declare abstract class AssocEdgeQueryBase<TSource extends Ent<TViewer>, T
|
|
|
24
24
|
protected loadRawData(infos: IDInfo[], options: EdgeQueryableDataOptions): Promise<void>;
|
|
25
25
|
queryID2(id2: ID): Promise<TEdge | undefined>;
|
|
26
26
|
queryAllID2(id2: ID): Promise<Map<ID, TEdge>>;
|
|
27
|
+
__beforeBETA(time: Date): this;
|
|
28
|
+
__afterBETA(time: Date): this;
|
|
29
|
+
__withinBeta(start: Date, end: Date): this;
|
|
27
30
|
}
|
|
28
31
|
export interface EdgeQueryCtr<TSource extends Ent, TDest extends Ent, TEdge extends AssocEdge> {
|
|
29
32
|
new (viewer: Viewer, src: EdgeQuerySource<TSource>): EdgeQuery<TSource, TDest, TEdge>;
|
|
@@ -1,8 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
26
|
exports.AssocEdgeQueryBase = void 0;
|
|
4
27
|
const ent_1 = require("../ent");
|
|
5
28
|
const query_1 = require("./query");
|
|
29
|
+
const clause = __importStar(require("../clause"));
|
|
6
30
|
// would be nice to someday have an API for assoc asc
|
|
7
31
|
class AssocEdgeQueryBase extends query_1.BaseEdgeQuery {
|
|
8
32
|
constructor(viewer, src, countLoaderFactory, dataLoaderFactory,
|
|
@@ -150,5 +174,52 @@ class AssocEdgeQueryBase extends query_1.BaseEdgeQuery {
|
|
|
150
174
|
}));
|
|
151
175
|
return m;
|
|
152
176
|
}
|
|
177
|
+
__beforeBETA(time) {
|
|
178
|
+
this.__assertNoFiltersBETA("before");
|
|
179
|
+
this.__addCustomFilterBETA(new BeforeFilter(time));
|
|
180
|
+
return this;
|
|
181
|
+
}
|
|
182
|
+
__afterBETA(time) {
|
|
183
|
+
this.__assertNoFiltersBETA("after");
|
|
184
|
+
this.__addCustomFilterBETA(new AfterFilter(time));
|
|
185
|
+
return this;
|
|
186
|
+
}
|
|
187
|
+
// start is inclusive, end is exclusive
|
|
188
|
+
__withinBeta(start, end) {
|
|
189
|
+
this.__assertNoFiltersBETA("after");
|
|
190
|
+
this.__addCustomFilterBETA(new WithinFilter(start, end));
|
|
191
|
+
return this;
|
|
192
|
+
}
|
|
153
193
|
}
|
|
154
194
|
exports.AssocEdgeQueryBase = AssocEdgeQueryBase;
|
|
195
|
+
class BeforeFilter {
|
|
196
|
+
constructor(time) {
|
|
197
|
+
this.time = time;
|
|
198
|
+
}
|
|
199
|
+
query(options) {
|
|
200
|
+
const cls = clause.Less("time", this.time);
|
|
201
|
+
options.clause = clause.AndOptional(options.clause, cls);
|
|
202
|
+
return options;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
class AfterFilter {
|
|
206
|
+
constructor(time) {
|
|
207
|
+
this.time = time;
|
|
208
|
+
}
|
|
209
|
+
query(options) {
|
|
210
|
+
const cls = clause.Greater("time", this.time);
|
|
211
|
+
options.clause = clause.AndOptional(options.clause, cls);
|
|
212
|
+
return options;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
class WithinFilter {
|
|
216
|
+
constructor(start, end) {
|
|
217
|
+
this.start = start;
|
|
218
|
+
this.end = end;
|
|
219
|
+
}
|
|
220
|
+
query(options) {
|
|
221
|
+
const cls = clause.And(clause.GreaterEq("time", this.start), clause.Less("time", this.end));
|
|
222
|
+
options.clause = clause.AndOptional(options.clause, cls);
|
|
223
|
+
return options;
|
|
224
|
+
}
|
|
225
|
+
}
|
package/core/query/query.d.ts
CHANGED
|
@@ -47,12 +47,15 @@ export declare abstract class BaseEdgeQuery<TSource extends Ent, TDest extends E
|
|
|
47
47
|
private sortCol;
|
|
48
48
|
private cursorCol;
|
|
49
49
|
private edgeQueryOptions;
|
|
50
|
+
private limitAdded;
|
|
50
51
|
constructor(viewer: Viewer, sortCol: string, cursorCol: string);
|
|
51
52
|
constructor(viewer: Viewer, options: EdgeQueryOptions);
|
|
52
53
|
protected getSortCol(): string;
|
|
53
54
|
getPrivacyPolicy(): PrivacyPolicy<Ent<Viewer<Ent<any> | null, ID | null>>, Viewer<Ent<any> | null, ID | null>>;
|
|
54
55
|
abstract sourceEnt(id: ID): Promise<Ent | null>;
|
|
55
56
|
first(n: number, after?: string): this;
|
|
57
|
+
__addCustomFilterBETA(filter: EdgeQueryFilter<TEdge>): void;
|
|
58
|
+
protected __assertNoFiltersBETA(name: string): void;
|
|
56
59
|
last(n: number, before?: string): this;
|
|
57
60
|
private querySingleEdge;
|
|
58
61
|
readonly queryEdges: () => Promise<TEdge[]>;
|
package/core/query/query.js
CHANGED
|
@@ -110,7 +110,7 @@ class FirstFilter {
|
|
|
110
110
|
const res = this.edgeQuery.getTableName();
|
|
111
111
|
const tableName = (0, types_1.isPromise)(res) ? await res : res;
|
|
112
112
|
// inner col time
|
|
113
|
-
options.clause = clause.PaginationMultipleColsSubQuery(this.sortCol, less ? "<" : ">", tableName, this.options.cursorCol, this.offset);
|
|
113
|
+
options.clause = clause.AndOptional(options.clause, clause.PaginationMultipleColsSubQuery(this.sortCol, less ? "<" : ">", tableName, this.options.cursorCol, this.offset));
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
else {
|
|
@@ -119,7 +119,7 @@ class FirstFilter {
|
|
|
119
119
|
let val = this.options.sortColTime
|
|
120
120
|
? new Date(this.offset).toISOString()
|
|
121
121
|
: this.offset;
|
|
122
|
-
options.clause = clauseFn(this.sortCol, val);
|
|
122
|
+
options.clause = clause.AndOptional(options.clause, clauseFn(this.sortCol, val));
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
options.orderby = orderby;
|
|
@@ -177,7 +177,7 @@ class LastFilter {
|
|
|
177
177
|
const tableName = (0, types_1.isPromise)(res) ? await res : res;
|
|
178
178
|
if (this.offset) {
|
|
179
179
|
// inner col time
|
|
180
|
-
options.clause = clause.PaginationMultipleColsSubQuery(this.sortCol, greater ? ">" : "<", tableName, this.options.cursorCol, this.offset);
|
|
180
|
+
options.clause = clause.AndOptional(options.clause, clause.PaginationMultipleColsSubQuery(this.sortCol, greater ? ">" : "<", tableName, this.options.cursorCol, this.offset));
|
|
181
181
|
}
|
|
182
182
|
// we also sort cursor col in same direction. (direction doesn't matter)
|
|
183
183
|
orderby.push({
|
|
@@ -191,7 +191,7 @@ class LastFilter {
|
|
|
191
191
|
let val = this.options.sortColTime
|
|
192
192
|
? new Date(this.offset).toISOString()
|
|
193
193
|
: this.offset;
|
|
194
|
-
options.clause = clauseFn(this.sortCol, val);
|
|
194
|
+
options.clause = clause.AndOptional(options.clause, clauseFn(this.sortCol, val));
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
options.orderby = orderby;
|
|
@@ -209,6 +209,7 @@ class BaseEdgeQuery {
|
|
|
209
209
|
this.pagination = new Map();
|
|
210
210
|
this.idMap = new Map();
|
|
211
211
|
this.idsToFetch = [];
|
|
212
|
+
this.limitAdded = false;
|
|
212
213
|
// this is basically just raw rows
|
|
213
214
|
this.queryEdges = async () => {
|
|
214
215
|
return await this.querySingleEdge("queryEdges");
|
|
@@ -306,6 +307,7 @@ class BaseEdgeQuery {
|
|
|
306
307
|
return privacy_1.AlwaysAllowPrivacyPolicy;
|
|
307
308
|
}
|
|
308
309
|
first(n, after) {
|
|
310
|
+
this.limitAdded = true;
|
|
309
311
|
this.assertQueryNotDispatched("first");
|
|
310
312
|
this.filters.push(new FirstFilter({
|
|
311
313
|
limit: n,
|
|
@@ -317,7 +319,18 @@ class BaseEdgeQuery {
|
|
|
317
319
|
}));
|
|
318
320
|
return this;
|
|
319
321
|
}
|
|
322
|
+
// API subject to change
|
|
323
|
+
// TODO https://github.com/lolopinto/ent/issues/685
|
|
324
|
+
__addCustomFilterBETA(filter) {
|
|
325
|
+
this.filters.push(filter);
|
|
326
|
+
}
|
|
327
|
+
__assertNoFiltersBETA(name) {
|
|
328
|
+
if (this.filters.length !== 0) {
|
|
329
|
+
throw new Error(`${name} must be the first filter called`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
320
332
|
last(n, before) {
|
|
333
|
+
this.limitAdded = true;
|
|
321
334
|
this.assertQueryNotDispatched("last");
|
|
322
335
|
this.filters.push(new LastFilter({
|
|
323
336
|
limit: n,
|
|
@@ -378,8 +391,9 @@ class BaseEdgeQuery {
|
|
|
378
391
|
}
|
|
379
392
|
async loadEdges() {
|
|
380
393
|
const idsInfo = await this.genIDInfosToFetch();
|
|
381
|
-
if
|
|
382
|
-
|
|
394
|
+
// we need first even if other filters are included...
|
|
395
|
+
if (!this.limitAdded) {
|
|
396
|
+
// if no limit filter, we add the firstN filter to ensure we get pagination info
|
|
383
397
|
this.first((0, ent_1.getDefaultLimit)());
|
|
384
398
|
}
|
|
385
399
|
let options = this._defaultEdgeQueryableOptions ?? {};
|
|
@@ -11,6 +11,7 @@ const clause_1 = require("../clause");
|
|
|
11
11
|
const builder_1 = require("../../testutils/builder");
|
|
12
12
|
const luxon_1 = require("luxon");
|
|
13
13
|
const convert_1 = require("../convert");
|
|
14
|
+
const test_context_1 = require("../../testutils/context/test_context");
|
|
14
15
|
function assocTests(ml, global = false) {
|
|
15
16
|
ml.mock();
|
|
16
17
|
describe("custom edge", () => {
|
|
@@ -56,21 +57,21 @@ function assocTests(ml, global = false) {
|
|
|
56
57
|
let execArray = /^SELECT (.+) FROM (.+) WHERE (.+)?/.exec(query.query);
|
|
57
58
|
return execArray?.[3];
|
|
58
59
|
}
|
|
59
|
-
function verifyQuery({ length = 1, numQueries = 1, limit = (0, ent_1.getDefaultLimit)(), disablePaginationBump = false, }) {
|
|
60
|
+
function verifyQuery({ length = 1, numQueries = 1, limit = (0, ent_1.getDefaultLimit)(), extraClause = undefined, disablePaginationBump = false, logsStart = 0, direction = "DESC", }) {
|
|
61
|
+
const clses = [(0, clause_1.Eq)("id1", ""), (0, clause_1.Eq)("edge_type", "")];
|
|
62
|
+
if (extraClause) {
|
|
63
|
+
clses.push(extraClause);
|
|
64
|
+
}
|
|
65
|
+
if (global) {
|
|
66
|
+
clses.push((0, clause_1.Eq)("deleted_at", null));
|
|
67
|
+
}
|
|
60
68
|
expect(ml.logs.length).toBe(length);
|
|
61
|
-
for (let i =
|
|
69
|
+
for (let i = logsStart; i < numQueries; i++) {
|
|
62
70
|
const whereClause = getWhereClause(ml.logs[i]);
|
|
63
71
|
let expLimit = disablePaginationBump ? limit : limit + 1;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
`${(0, clause_1.And)((0, clause_1.Eq)("id1", ""), (0, clause_1.Eq)("edge_type", ""), (0, clause_1.Eq)("deleted_at", null)).clause(1)} ORDER BY time DESC, id2 DESC LIMIT ${expLimit}`);
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
expect(whereClause, `${i}`).toBe(
|
|
71
|
-
// default limit
|
|
72
|
-
`${(0, clause_1.And)((0, clause_1.Eq)("id1", ""), (0, clause_1.Eq)("edge_type", "")).clause(1)} ORDER BY time DESC, id2 DESC LIMIT ${expLimit}`);
|
|
73
|
-
}
|
|
72
|
+
expect(whereClause, `${i}`).toBe(
|
|
73
|
+
// default limit
|
|
74
|
+
`${(0, clause_1.And)(...clses).clause(1)} ORDER BY time ${direction}, id2 ${direction} LIMIT ${expLimit}`);
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
function verifyCountQuery({ length = 1, numQueries = 1 }) {
|
|
@@ -663,6 +664,290 @@ function assocTests(ml, global = false) {
|
|
|
663
664
|
expect(entsMapFromUserVCToken.get(user2.id)?.length).toBe(0);
|
|
664
665
|
});
|
|
665
666
|
});
|
|
667
|
+
describe("time based queries", () => {
|
|
668
|
+
let user;
|
|
669
|
+
let contacts;
|
|
670
|
+
let ctx;
|
|
671
|
+
beforeEach(async () => {
|
|
672
|
+
ctx = new test_context_1.TestContext();
|
|
673
|
+
[user, contacts] = await (0, test_helpers_1.createAllContacts)({ ctx });
|
|
674
|
+
const sortedTimes = contacts.map((c) => c.createdAt.getTime());
|
|
675
|
+
expect(sortedTimes[1] - sortedTimes[0]).toBe(86400);
|
|
676
|
+
expect(contacts.length).toBe(5);
|
|
677
|
+
ml.clear();
|
|
678
|
+
// to prime the cache
|
|
679
|
+
await index_1.FakeUser.load(user.viewer, user.id);
|
|
680
|
+
});
|
|
681
|
+
function getQuery(viewer) {
|
|
682
|
+
return index_1.UserToContactsQuery.query(viewer, user.id);
|
|
683
|
+
}
|
|
684
|
+
test("ids", async () => {
|
|
685
|
+
const ids = await getQuery(user.viewer).queryIDs();
|
|
686
|
+
expect(ids.length).toBe(contacts.length);
|
|
687
|
+
});
|
|
688
|
+
test("before", async () => {
|
|
689
|
+
const ids = await getQuery(user.viewer)
|
|
690
|
+
.__beforeBETA(contacts[2].createdAt)
|
|
691
|
+
.queryIDs();
|
|
692
|
+
expect(ids.length).toBe(2);
|
|
693
|
+
expect(ids).toEqual([contacts[1].id, contacts[0].id]);
|
|
694
|
+
verifyQuery({
|
|
695
|
+
length: 2,
|
|
696
|
+
extraClause: (0, clause_1.Less)("time", contacts[2].createdAt),
|
|
697
|
+
// there's a load for user we don't care about here so just skip it...
|
|
698
|
+
logsStart: 1,
|
|
699
|
+
numQueries: 2,
|
|
700
|
+
});
|
|
701
|
+
});
|
|
702
|
+
test("before + first", async () => {
|
|
703
|
+
const ids = await getQuery(user.viewer)
|
|
704
|
+
.__beforeBETA(contacts[2].createdAt)
|
|
705
|
+
.first(1)
|
|
706
|
+
.queryIDs();
|
|
707
|
+
expect(ids.length).toBe(1);
|
|
708
|
+
expect(ids).toEqual([contacts[1].id]);
|
|
709
|
+
verifyQuery({
|
|
710
|
+
length: 2,
|
|
711
|
+
limit: 1,
|
|
712
|
+
extraClause: (0, clause_1.Less)("time", contacts[2].createdAt),
|
|
713
|
+
// there's a load for user we don't care about here so just skip it...
|
|
714
|
+
logsStart: 1,
|
|
715
|
+
numQueries: 2,
|
|
716
|
+
});
|
|
717
|
+
});
|
|
718
|
+
test("before + last", async () => {
|
|
719
|
+
const ids = await getQuery(user.viewer)
|
|
720
|
+
.__beforeBETA(contacts[2].createdAt)
|
|
721
|
+
.last(1)
|
|
722
|
+
.queryIDs();
|
|
723
|
+
expect(ids.length).toBe(1);
|
|
724
|
+
expect(ids).toEqual([contacts[0].id]);
|
|
725
|
+
verifyQuery({
|
|
726
|
+
length: 2,
|
|
727
|
+
limit: 1,
|
|
728
|
+
extraClause: (0, clause_1.Less)("time", contacts[2].createdAt),
|
|
729
|
+
// there's a load for user we don't care about here so just skip it...
|
|
730
|
+
logsStart: 1,
|
|
731
|
+
numQueries: 2,
|
|
732
|
+
direction: "ASC",
|
|
733
|
+
});
|
|
734
|
+
});
|
|
735
|
+
test("before + first with pagination", async () => {
|
|
736
|
+
const query = getQuery(user.viewer);
|
|
737
|
+
const edges = await query
|
|
738
|
+
.__beforeBETA(contacts[2].createdAt)
|
|
739
|
+
.first(1)
|
|
740
|
+
.queryEdges();
|
|
741
|
+
const pagination = query.paginationInfo();
|
|
742
|
+
expect(edges.length).toBe(1);
|
|
743
|
+
expect(edges.map((edge) => edge.id2)).toEqual([contacts[1].id]);
|
|
744
|
+
verifyQuery({
|
|
745
|
+
length: 2,
|
|
746
|
+
limit: 1,
|
|
747
|
+
extraClause: (0, clause_1.Less)("time", contacts[2].createdAt),
|
|
748
|
+
// there's a load for user we don't care about here so just skip it...
|
|
749
|
+
logsStart: 1,
|
|
750
|
+
numQueries: 2,
|
|
751
|
+
});
|
|
752
|
+
const info = pagination.get(user.id);
|
|
753
|
+
expect(info.hasNextPage).toBe(true);
|
|
754
|
+
expect(info.hasPreviousPage).toBe(false);
|
|
755
|
+
ml.clear();
|
|
756
|
+
const query2 = getQuery(user.viewer);
|
|
757
|
+
const edges2 = await query2
|
|
758
|
+
.__beforeBETA(contacts[2].createdAt)
|
|
759
|
+
.first(1, info.endCursor)
|
|
760
|
+
.queryEdges();
|
|
761
|
+
const pagination2 = query2.paginationInfo();
|
|
762
|
+
expect(edges2.length).toBe(1);
|
|
763
|
+
expect(edges2.map((edge) => edge.id2)).toEqual([contacts[0].id]);
|
|
764
|
+
// complicated pagination query. ignore verifying for now
|
|
765
|
+
// verifyFirstAfterCursorQuery in shared_test handles this...
|
|
766
|
+
const info2 = pagination2.get(user.id) ?? {
|
|
767
|
+
hasNextPage: false,
|
|
768
|
+
hasPreviousPage: false,
|
|
769
|
+
};
|
|
770
|
+
expect(info2.hasNextPage).toBe(false);
|
|
771
|
+
expect(info2.hasPreviousPage).toBe(false);
|
|
772
|
+
});
|
|
773
|
+
test("after", async () => {
|
|
774
|
+
const ids = await getQuery(user.viewer)
|
|
775
|
+
.__afterBETA(contacts[2].createdAt)
|
|
776
|
+
.queryIDs();
|
|
777
|
+
expect(ids.length).toBe(2);
|
|
778
|
+
expect(ids).toEqual([contacts[4].id, contacts[3].id]);
|
|
779
|
+
verifyQuery({
|
|
780
|
+
length: 2,
|
|
781
|
+
extraClause: (0, clause_1.Greater)("time", contacts[2].createdAt),
|
|
782
|
+
// there's a load for user we don't care about here so just skip it...
|
|
783
|
+
logsStart: 1,
|
|
784
|
+
numQueries: 2,
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
test("after + first", async () => {
|
|
788
|
+
const ids = await getQuery(user.viewer)
|
|
789
|
+
.__afterBETA(contacts[2].createdAt)
|
|
790
|
+
.first(1)
|
|
791
|
+
.queryIDs();
|
|
792
|
+
expect(ids.length).toBe(1);
|
|
793
|
+
expect(ids).toEqual([contacts[4].id]);
|
|
794
|
+
verifyQuery({
|
|
795
|
+
length: 2,
|
|
796
|
+
limit: 1,
|
|
797
|
+
extraClause: (0, clause_1.Greater)("time", contacts[2].createdAt),
|
|
798
|
+
// there's a load for user we don't care about here so just skip it...
|
|
799
|
+
logsStart: 1,
|
|
800
|
+
numQueries: 2,
|
|
801
|
+
});
|
|
802
|
+
});
|
|
803
|
+
test("after + last", async () => {
|
|
804
|
+
const ids = await getQuery(user.viewer)
|
|
805
|
+
.__afterBETA(contacts[2].createdAt)
|
|
806
|
+
.last(1)
|
|
807
|
+
.queryIDs();
|
|
808
|
+
expect(ids.length).toBe(1);
|
|
809
|
+
expect(ids).toEqual([contacts[3].id]);
|
|
810
|
+
verifyQuery({
|
|
811
|
+
length: 2,
|
|
812
|
+
limit: 1,
|
|
813
|
+
extraClause: (0, clause_1.Greater)("time", contacts[2].createdAt),
|
|
814
|
+
// there's a load for user we don't care about here so just skip it...
|
|
815
|
+
logsStart: 1,
|
|
816
|
+
numQueries: 2,
|
|
817
|
+
direction: "ASC",
|
|
818
|
+
});
|
|
819
|
+
});
|
|
820
|
+
test("within", async () => {
|
|
821
|
+
const ids = await getQuery(user.viewer)
|
|
822
|
+
.__withinBeta(contacts[1].createdAt, contacts[4].createdAt)
|
|
823
|
+
.queryIDs();
|
|
824
|
+
expect(ids.length).toBe(3);
|
|
825
|
+
expect(ids).toEqual([contacts[3].id, contacts[2].id, contacts[1].id]);
|
|
826
|
+
verifyQuery({
|
|
827
|
+
length: 2,
|
|
828
|
+
extraClause: (0, clause_1.And)((0, clause_1.GreaterEq)("time", contacts[1].createdAt), (0, clause_1.Less)("time", contacts[4].createdAt)),
|
|
829
|
+
// there's a load for user we don't care about here so just skip it...
|
|
830
|
+
logsStart: 1,
|
|
831
|
+
numQueries: 2,
|
|
832
|
+
});
|
|
833
|
+
});
|
|
834
|
+
test("within + first", async () => {
|
|
835
|
+
const ids = await getQuery(user.viewer)
|
|
836
|
+
.__withinBeta(contacts[1].createdAt, contacts[4].createdAt)
|
|
837
|
+
.first(2)
|
|
838
|
+
.queryIDs();
|
|
839
|
+
expect(ids.length).toBe(2);
|
|
840
|
+
expect(ids).toEqual([contacts[3].id, contacts[2].id]);
|
|
841
|
+
verifyQuery({
|
|
842
|
+
length: 2,
|
|
843
|
+
limit: 2,
|
|
844
|
+
extraClause: (0, clause_1.And)((0, clause_1.GreaterEq)("time", contacts[1].createdAt), (0, clause_1.Less)("time", contacts[4].createdAt)),
|
|
845
|
+
// there's a load for user we don't care about here so just skip it...
|
|
846
|
+
logsStart: 1,
|
|
847
|
+
numQueries: 2,
|
|
848
|
+
});
|
|
849
|
+
});
|
|
850
|
+
test("within + last", async () => {
|
|
851
|
+
const ids = await getQuery(user.viewer)
|
|
852
|
+
.__withinBeta(contacts[1].createdAt, contacts[4].createdAt)
|
|
853
|
+
.last(2)
|
|
854
|
+
.queryIDs();
|
|
855
|
+
expect(ids.length).toBe(2);
|
|
856
|
+
expect(ids).toEqual([contacts[1].id, contacts[2].id]);
|
|
857
|
+
verifyQuery({
|
|
858
|
+
length: 2,
|
|
859
|
+
limit: 2,
|
|
860
|
+
extraClause: (0, clause_1.And)((0, clause_1.GreaterEq)("time", contacts[1].createdAt), (0, clause_1.Less)("time", contacts[4].createdAt)),
|
|
861
|
+
// there's a load for user we don't care about here so just skip it...
|
|
862
|
+
logsStart: 1,
|
|
863
|
+
numQueries: 2,
|
|
864
|
+
direction: "ASC",
|
|
865
|
+
});
|
|
866
|
+
});
|
|
867
|
+
test("within + first with pagination", async () => {
|
|
868
|
+
const query = getQuery(user.viewer);
|
|
869
|
+
const edges = await query
|
|
870
|
+
.__withinBeta(contacts[1].createdAt, contacts[4].createdAt)
|
|
871
|
+
.first(2)
|
|
872
|
+
.queryEdges();
|
|
873
|
+
const pagination = query.paginationInfo();
|
|
874
|
+
expect(edges.length).toBe(2);
|
|
875
|
+
expect(edges.map((edge) => edge.id2)).toEqual([
|
|
876
|
+
contacts[3].id,
|
|
877
|
+
contacts[2].id,
|
|
878
|
+
]);
|
|
879
|
+
verifyQuery({
|
|
880
|
+
length: 2,
|
|
881
|
+
limit: 2,
|
|
882
|
+
extraClause: (0, clause_1.And)((0, clause_1.GreaterEq)("time", contacts[1].createdAt), (0, clause_1.Less)("time", contacts[4].createdAt)),
|
|
883
|
+
// there's a load for user we don't care about here so just skip it...
|
|
884
|
+
logsStart: 1,
|
|
885
|
+
numQueries: 2,
|
|
886
|
+
});
|
|
887
|
+
const info = pagination.get(user.id);
|
|
888
|
+
expect(info.hasNextPage).toBe(true);
|
|
889
|
+
expect(info.hasPreviousPage).toBe(false);
|
|
890
|
+
ml.clear();
|
|
891
|
+
const query2 = getQuery(user.viewer);
|
|
892
|
+
const edges2 = await query2
|
|
893
|
+
.__withinBeta(contacts[1].createdAt, contacts[4].createdAt)
|
|
894
|
+
.first(2, info.endCursor)
|
|
895
|
+
.queryEdges();
|
|
896
|
+
const pagination2 = query2.paginationInfo();
|
|
897
|
+
expect(edges2.length).toBe(1);
|
|
898
|
+
expect(edges2.map((edge) => edge.id2)).toEqual([contacts[1].id]);
|
|
899
|
+
// complicated pagination query. ignore verifying for now
|
|
900
|
+
// verifyFirstAfterCursorQuery in shared_test handles this...
|
|
901
|
+
const info2 = pagination2.get(user.id) ?? {
|
|
902
|
+
hasNextPage: false,
|
|
903
|
+
hasPreviousPage: false,
|
|
904
|
+
};
|
|
905
|
+
expect(info2.hasNextPage).toBe(false);
|
|
906
|
+
expect(info2.hasPreviousPage).toBe(false);
|
|
907
|
+
});
|
|
908
|
+
test("within + last with pagination", async () => {
|
|
909
|
+
const query = getQuery(user.viewer);
|
|
910
|
+
const edges = await query
|
|
911
|
+
.__withinBeta(contacts[1].createdAt, contacts[4].createdAt)
|
|
912
|
+
.last(2)
|
|
913
|
+
.queryEdges();
|
|
914
|
+
const pagination = query.paginationInfo();
|
|
915
|
+
expect(edges.length).toBe(2);
|
|
916
|
+
expect(edges.map((edge) => edge.id2)).toEqual([
|
|
917
|
+
contacts[1].id,
|
|
918
|
+
contacts[2].id,
|
|
919
|
+
]);
|
|
920
|
+
verifyQuery({
|
|
921
|
+
length: 2,
|
|
922
|
+
limit: 2,
|
|
923
|
+
extraClause: (0, clause_1.And)((0, clause_1.GreaterEq)("time", contacts[1].createdAt), (0, clause_1.Less)("time", contacts[4].createdAt)),
|
|
924
|
+
// there's a load for user we don't care about here so just skip it...
|
|
925
|
+
logsStart: 1,
|
|
926
|
+
numQueries: 2,
|
|
927
|
+
direction: "ASC",
|
|
928
|
+
});
|
|
929
|
+
const info = pagination.get(user.id);
|
|
930
|
+
expect(info.hasNextPage).toBe(false);
|
|
931
|
+
expect(info.hasPreviousPage).toBe(true);
|
|
932
|
+
ml.clear();
|
|
933
|
+
const query2 = getQuery(user.viewer);
|
|
934
|
+
const edges2 = await query2
|
|
935
|
+
.__withinBeta(contacts[1].createdAt, contacts[4].createdAt)
|
|
936
|
+
.last(2, info.endCursor)
|
|
937
|
+
.queryEdges();
|
|
938
|
+
const pagination2 = query2.paginationInfo();
|
|
939
|
+
expect(edges2.length).toBe(1);
|
|
940
|
+
expect(edges2.map((edge) => edge.id2)).toEqual([contacts[3].id]);
|
|
941
|
+
// complicated pagination query. ignore verifying for now
|
|
942
|
+
// verifyFirstAfterCursorQuery in shared_test handles this...
|
|
943
|
+
const info2 = pagination2.get(user.id) ?? {
|
|
944
|
+
hasNextPage: false,
|
|
945
|
+
hasPreviousPage: false,
|
|
946
|
+
};
|
|
947
|
+
expect(info2.hasNextPage).toBe(false);
|
|
948
|
+
expect(info2.hasPreviousPage).toBe(false);
|
|
949
|
+
});
|
|
950
|
+
});
|
|
666
951
|
if (!global) {
|
|
667
952
|
return;
|
|
668
953
|
}
|
|
@@ -697,11 +697,11 @@ const commonTests = (opts) => {
|
|
|
697
697
|
user = u;
|
|
698
698
|
const [_, contacts2] = await (0, test_helpers_1.createAllContacts)({
|
|
699
699
|
user,
|
|
700
|
-
start: contacts[contacts.length - 1].createdAt.getTime() -
|
|
700
|
+
start: contacts[contacts.length - 1].createdAt.getTime() - 86400,
|
|
701
701
|
});
|
|
702
702
|
await (0, test_helpers_1.createAllContacts)({
|
|
703
703
|
user,
|
|
704
|
-
start: contacts2[contacts.length - 1].createdAt.getTime() -
|
|
704
|
+
start: contacts2[contacts.length - 1].createdAt.getTime() - 86400,
|
|
705
705
|
});
|
|
706
706
|
const edges = await opts.newQuery(getViewer(), user).queryEdges();
|
|
707
707
|
// confirm there are duplicates...
|
package/package.json
CHANGED
|
@@ -102,7 +102,8 @@ async function createAllContacts(opts) {
|
|
|
102
102
|
let userInputs = exports.inputs.slice(0, slice || exports.inputs.length);
|
|
103
103
|
const contacts = await Promise.all(userInputs.map(async (input) => {
|
|
104
104
|
// just to make times deterministic so that tests can consistently work
|
|
105
|
-
|
|
105
|
+
// doing a day so we can do time range queries more easily
|
|
106
|
+
(0, jest_date_mock_1.advanceBy)(86400);
|
|
106
107
|
const builder = (0, _1.getContactBuilder)(userr.viewer, getContactInput(userr, input));
|
|
107
108
|
// add edge from user to contact
|
|
108
109
|
builder.orchestrator.addInboundEdge(userr.id, _1.EdgeType.UserToContacts, const_1.NodeType.FakeUser, {
|