@snowtop/ent 0.1.0-alpha89 → 0.1.0-alpha89-16dc3410-33c4-11ed-9fc4-5d4a927887be
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/db.d.ts +1 -0
- package/core/ent.d.ts +1 -1
- package/core/ent.js +20 -19
- package/core/loaders/index.d.ts +1 -1
- package/core/loaders/index.js +1 -3
- package/core/loaders/index_loader.d.ts +2 -2
- package/core/loaders/query_loader.d.ts +5 -11
- package/core/loaders/query_loader.js +47 -10
- package/core/query/custom_clause_query.d.ts +24 -0
- package/core/query/custom_clause_query.js +72 -0
- package/core/query/custom_query.d.ts +17 -2
- package/core/query/custom_query.js +77 -10
- package/core/query/index.d.ts +1 -0
- package/core/query/index.js +3 -1
- package/core/query/query.js +8 -1
- package/core/query/shared_test.js +118 -15
- package/package.json +1 -1
- package/scripts/custom_graphql.js +1 -0
- package/testutils/db/temp_db.js +7 -1
- package/testutils/fake_data/fake_contact.d.ts +2 -1
- package/testutils/fake_data/fake_contact.js +5 -0
- package/testutils/fake_data/fake_event.d.ts +2 -0
- package/testutils/fake_data/test_helpers.d.ts +1 -0
- package/testutils/fake_data/test_helpers.js +3 -1
- package/testutils/fake_data/user_query.d.ts +6 -4
- package/testutils/fake_data/user_query.js +28 -21
package/core/db.d.ts
CHANGED
package/core/ent.d.ts
CHANGED
|
@@ -229,6 +229,6 @@ interface loadEdgeForIDOptions<T extends AssocEdge> extends loadCustomEdgesOptio
|
|
|
229
229
|
export declare function loadEdgeForID2<T extends AssocEdge>(options: loadEdgeForIDOptions<T>): Promise<T | undefined>;
|
|
230
230
|
export declare function loadNodesByEdge<T extends Ent>(viewer: Viewer, id1: ID, edgeType: string, options: LoadEntOptions<T>): Promise<T[]>;
|
|
231
231
|
export declare function applyPrivacyPolicyForRow<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadEntOptions<TEnt, TViewer>, row: Data): Promise<TEnt | null>;
|
|
232
|
-
export declare function applyPrivacyPolicyForRows<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, rows: Data[], options: LoadEntOptions<TEnt, TViewer>): Promise<
|
|
232
|
+
export declare function applyPrivacyPolicyForRows<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, rows: Data[], options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt[]>;
|
|
233
233
|
export declare function getEdgeTypeInGroup<T extends string>(viewer: Viewer, id1: ID, id2: ID, m: Map<T, string>): Promise<[T, AssocEdge] | undefined>;
|
|
234
234
|
export {};
|
package/core/ent.js
CHANGED
|
@@ -348,25 +348,12 @@ async function loadEntsFromClause(viewer, clause, options) {
|
|
|
348
348
|
context: viewer.context,
|
|
349
349
|
};
|
|
350
350
|
const rows = await loadRows(rowOptions);
|
|
351
|
-
return
|
|
351
|
+
return applyPrivacyPolicyForRowsDeprecated(viewer, rows, options);
|
|
352
352
|
}
|
|
353
353
|
exports.loadEntsFromClause = loadEntsFromClause;
|
|
354
354
|
async function loadCustomEnts(viewer, options, query) {
|
|
355
355
|
const rows = await loadCustomData(options, query, viewer.context);
|
|
356
|
-
|
|
357
|
-
if (!rows.length) {
|
|
358
|
-
return [];
|
|
359
|
-
}
|
|
360
|
-
const entLoader = getEntLoader(viewer, options);
|
|
361
|
-
await Promise.all(rows.map(async (row, idx) => {
|
|
362
|
-
const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options, entLoader);
|
|
363
|
-
if (r instanceof ErrorWrapper) {
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
result[idx] = r;
|
|
367
|
-
}));
|
|
368
|
-
// filter ents that aren't visible because of privacy
|
|
369
|
-
return result.filter((r) => r !== undefined);
|
|
356
|
+
return applyPrivacyPolicyForRows(viewer, rows, options);
|
|
370
357
|
}
|
|
371
358
|
exports.loadCustomEnts = loadCustomEnts;
|
|
372
359
|
function isClause(opts) {
|
|
@@ -1550,10 +1537,8 @@ async function applyPrivacyPolicyForRowX(viewer, options, row) {
|
|
|
1550
1537
|
const ent = new options.ent(viewer, row);
|
|
1551
1538
|
return await applyPrivacyPolicyForEntX(viewer, ent, row, options);
|
|
1552
1539
|
}
|
|
1553
|
-
//
|
|
1554
|
-
|
|
1555
|
-
// custom_query is one that should be updated
|
|
1556
|
-
async function applyPrivacyPolicyForRows(viewer, rows, options) {
|
|
1540
|
+
// deprecated. doesn't use entcache
|
|
1541
|
+
async function applyPrivacyPolicyForRowsDeprecated(viewer, rows, options) {
|
|
1557
1542
|
let m = new Map();
|
|
1558
1543
|
// apply privacy logic
|
|
1559
1544
|
await Promise.all(rows.map(async (row) => {
|
|
@@ -1564,6 +1549,22 @@ async function applyPrivacyPolicyForRows(viewer, rows, options) {
|
|
|
1564
1549
|
}));
|
|
1565
1550
|
return m;
|
|
1566
1551
|
}
|
|
1552
|
+
async function applyPrivacyPolicyForRows(viewer, rows, options) {
|
|
1553
|
+
const result = new Array(rows.length);
|
|
1554
|
+
if (!rows.length) {
|
|
1555
|
+
return [];
|
|
1556
|
+
}
|
|
1557
|
+
const entLoader = getEntLoader(viewer, options);
|
|
1558
|
+
await Promise.all(rows.map(async (row, idx) => {
|
|
1559
|
+
const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options, entLoader);
|
|
1560
|
+
if (r instanceof ErrorWrapper) {
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
result[idx] = r;
|
|
1564
|
+
}));
|
|
1565
|
+
// filter ents that aren't visible because of privacy
|
|
1566
|
+
return result.filter((r) => r !== undefined);
|
|
1567
|
+
}
|
|
1567
1568
|
exports.applyPrivacyPolicyForRows = applyPrivacyPolicyForRows;
|
|
1568
1569
|
async function loadEdgeWithConst(viewer, id1, id2, edgeEnum, edgeType) {
|
|
1569
1570
|
const edge = await loadEdgeForID2({
|
package/core/loaders/index.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export { RawCountLoader, RawCountLoaderFactory } from "./raw_count_loader";
|
|
|
3
3
|
export { AssocEdgeCountLoader, AssocEdgeCountLoaderFactory, } from "./assoc_count_loader";
|
|
4
4
|
export { AssocDirectEdgeLoader, AssocEdgeLoader, AssocEdgeLoaderFactory, } from "./assoc_edge_loader";
|
|
5
5
|
export { IndexLoaderFactory } from "./index_loader";
|
|
6
|
-
export {
|
|
6
|
+
export { QueryLoaderFactory } from "./query_loader";
|
package/core/loaders/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.QueryLoaderFactory = exports.
|
|
3
|
+
exports.QueryLoaderFactory = exports.IndexLoaderFactory = exports.AssocEdgeLoaderFactory = exports.AssocEdgeLoader = exports.AssocDirectEdgeLoader = exports.AssocEdgeCountLoaderFactory = exports.AssocEdgeCountLoader = exports.RawCountLoaderFactory = exports.RawCountLoader = exports.ObjectLoaderFactory = exports.ObjectLoader = void 0;
|
|
4
4
|
var object_loader_1 = require("./object_loader");
|
|
5
5
|
Object.defineProperty(exports, "ObjectLoader", { enumerable: true, get: function () { return object_loader_1.ObjectLoader; } });
|
|
6
6
|
Object.defineProperty(exports, "ObjectLoaderFactory", { enumerable: true, get: function () { return object_loader_1.ObjectLoaderFactory; } });
|
|
@@ -17,6 +17,4 @@ Object.defineProperty(exports, "AssocEdgeLoaderFactory", { enumerable: true, get
|
|
|
17
17
|
var index_loader_1 = require("./index_loader");
|
|
18
18
|
Object.defineProperty(exports, "IndexLoaderFactory", { enumerable: true, get: function () { return index_loader_1.IndexLoaderFactory; } });
|
|
19
19
|
var query_loader_1 = require("./query_loader");
|
|
20
|
-
Object.defineProperty(exports, "QueryLoader", { enumerable: true, get: function () { return query_loader_1.QueryLoader; } });
|
|
21
|
-
Object.defineProperty(exports, "QueryDirectLoader", { enumerable: true, get: function () { return query_loader_1.QueryDirectLoader; } });
|
|
22
20
|
Object.defineProperty(exports, "QueryLoaderFactory", { enumerable: true, get: function () { return query_loader_1.QueryLoaderFactory; } });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ID, SelectBaseDataOptions, Context, Data, LoaderFactory, EdgeQueryableDataOptions } from "../base";
|
|
1
|
+
import { ID, SelectBaseDataOptions, Context, Data, LoaderFactory, EdgeQueryableDataOptions, Loader } from "../base";
|
|
2
2
|
import * as clause from "../clause";
|
|
3
3
|
import { ObjectLoaderFactory } from "./object_loader";
|
|
4
4
|
export declare class IndexLoaderFactory implements LoaderFactory<ID, Data[]> {
|
|
@@ -10,5 +10,5 @@ export declare class IndexLoaderFactory implements LoaderFactory<ID, Data[]> {
|
|
|
10
10
|
toPrime?: ObjectLoaderFactory<ID>[];
|
|
11
11
|
});
|
|
12
12
|
createLoader(context?: Context): any;
|
|
13
|
-
createConfigurableLoader(options: EdgeQueryableDataOptions, context?: Context):
|
|
13
|
+
createConfigurableLoader(options: EdgeQueryableDataOptions, context?: Context): Loader<ID, Data[]>;
|
|
14
14
|
}
|
|
@@ -1,21 +1,14 @@
|
|
|
1
1
|
import { Context, ID, EdgeQueryableDataOptions, Loader, LoaderFactory, Data } from "../base";
|
|
2
2
|
import * as clause from "../clause";
|
|
3
3
|
import { ObjectLoaderFactory } from "./object_loader";
|
|
4
|
-
export declare
|
|
4
|
+
export declare function getOrderBy(sortCol: string, orderby?: string): string;
|
|
5
|
+
declare class QueryDirectLoader<K extends any> implements Loader<K, Data[]> {
|
|
5
6
|
private options;
|
|
6
7
|
private queryOptions?;
|
|
7
|
-
constructor(options: QueryOptions, queryOptions?: Partial<Pick<import("../base").QueryableDataOptions, "limit" | "orderby" | "clause">> | undefined);
|
|
8
|
-
load(id: K): Promise<Data[]>;
|
|
9
|
-
clearAll(): void;
|
|
10
|
-
}
|
|
11
|
-
export declare class QueryLoader<K extends any> implements Loader<K, Data[]> {
|
|
12
|
-
private options;
|
|
13
8
|
context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined;
|
|
14
|
-
private queryOptions?;
|
|
15
|
-
private loader;
|
|
16
|
-
private primedLoaders;
|
|
17
9
|
private memoizedInitPrime;
|
|
18
|
-
|
|
10
|
+
private primedLoaders;
|
|
11
|
+
constructor(options: QueryOptions, queryOptions?: Partial<Pick<import("../base").QueryableDataOptions, "limit" | "orderby" | "clause">> | undefined, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined);
|
|
19
12
|
private initPrime;
|
|
20
13
|
load(id: K): Promise<Data[]>;
|
|
21
14
|
clearAll(): void;
|
|
@@ -34,5 +27,6 @@ export declare class QueryLoaderFactory<K extends any> implements LoaderFactory<
|
|
|
34
27
|
constructor(options: QueryOptions);
|
|
35
28
|
createLoader(context?: Context): any;
|
|
36
29
|
createConfigurableLoader(options: EdgeQueryableDataOptions, context?: Context): Loader<unknown, Data[]> | QueryDirectLoader<unknown>;
|
|
30
|
+
static createConfigurableLoader(name: string, queryOptions: QueryOptions, options: EdgeQueryableDataOptions, context?: Context): Loader<unknown, Data[]> | QueryDirectLoader<unknown>;
|
|
37
31
|
}
|
|
38
32
|
export {};
|
|
@@ -22,7 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
22
22
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
23
|
};
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.QueryLoaderFactory = exports.
|
|
25
|
+
exports.QueryLoaderFactory = exports.getOrderBy = void 0;
|
|
26
26
|
const dataloader_1 = __importDefault(require("dataloader"));
|
|
27
27
|
const ent_1 = require("../ent");
|
|
28
28
|
const clause = __importStar(require("../clause"));
|
|
@@ -30,13 +30,17 @@ const logger_1 = require("../logger");
|
|
|
30
30
|
const loader_1 = require("./loader");
|
|
31
31
|
const memoizee_1 = __importDefault(require("memoizee"));
|
|
32
32
|
function getOrderBy(sortCol, orderby) {
|
|
33
|
+
if (orderby) {
|
|
34
|
+
return orderby;
|
|
35
|
+
}
|
|
33
36
|
let sortColLower = sortCol.toLowerCase();
|
|
34
37
|
let orderbyDirection = " DESC";
|
|
35
38
|
if (sortColLower.endsWith("asc") || sortCol.endsWith("desc")) {
|
|
36
39
|
orderbyDirection = "";
|
|
37
40
|
}
|
|
38
|
-
return
|
|
41
|
+
return `${sortCol}${orderbyDirection}`;
|
|
39
42
|
}
|
|
43
|
+
exports.getOrderBy = getOrderBy;
|
|
40
44
|
async function simpleCase(options, id, queryOptions) {
|
|
41
45
|
let cls;
|
|
42
46
|
if (options.groupCol) {
|
|
@@ -120,16 +124,47 @@ function createLoader(options, queryOptions) {
|
|
|
120
124
|
}, loaderOptions);
|
|
121
125
|
}
|
|
122
126
|
class QueryDirectLoader {
|
|
123
|
-
constructor(options, queryOptions) {
|
|
127
|
+
constructor(options, queryOptions, context) {
|
|
124
128
|
this.options = options;
|
|
125
129
|
this.queryOptions = queryOptions;
|
|
130
|
+
this.context = context;
|
|
131
|
+
this.memoizedInitPrime = (0, memoizee_1.default)(this.initPrime.bind(this));
|
|
132
|
+
}
|
|
133
|
+
initPrime() {
|
|
134
|
+
if (!this.context || !this.options?.toPrime) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
let primedLoaders = new Map();
|
|
138
|
+
this.options.toPrime.forEach((prime) => {
|
|
139
|
+
const l2 = prime.createLoader(this.context);
|
|
140
|
+
if (l2.prime === undefined) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
primedLoaders.set(prime.options.key, l2);
|
|
144
|
+
});
|
|
145
|
+
this.primedLoaders = primedLoaders;
|
|
126
146
|
}
|
|
127
147
|
async load(id) {
|
|
128
|
-
|
|
148
|
+
const rows = await simpleCase(this.options, id, this.queryOptions);
|
|
149
|
+
if (this.context) {
|
|
150
|
+
this.memoizedInitPrime();
|
|
151
|
+
if (this.primedLoaders) {
|
|
152
|
+
for (const row of rows) {
|
|
153
|
+
for (const [key, loader] of this.primedLoaders) {
|
|
154
|
+
const value = row[key];
|
|
155
|
+
if (value !== undefined) {
|
|
156
|
+
loader.prime(row);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return rows;
|
|
129
163
|
}
|
|
130
164
|
clearAll() { }
|
|
131
165
|
}
|
|
132
|
-
|
|
166
|
+
// note, you should never call this directly
|
|
167
|
+
// there's scenarios where QueryDirectLoader is needed instead of this...
|
|
133
168
|
class QueryLoader {
|
|
134
169
|
constructor(options, context, queryOptions) {
|
|
135
170
|
this.options = options;
|
|
@@ -176,7 +211,6 @@ class QueryLoader {
|
|
|
176
211
|
this.loader && this.loader.clearAll();
|
|
177
212
|
}
|
|
178
213
|
}
|
|
179
|
-
exports.QueryLoader = QueryLoader;
|
|
180
214
|
class QueryLoaderFactory {
|
|
181
215
|
constructor(options) {
|
|
182
216
|
this.options = options;
|
|
@@ -194,11 +228,14 @@ class QueryLoaderFactory {
|
|
|
194
228
|
return (0, loader_1.getLoader)(this, () => new QueryLoader(this.options, context), context);
|
|
195
229
|
}
|
|
196
230
|
createConfigurableLoader(options, context) {
|
|
197
|
-
|
|
198
|
-
|
|
231
|
+
return QueryLoaderFactory.createConfigurableLoader(this.name, this.options, options, context);
|
|
232
|
+
}
|
|
233
|
+
static createConfigurableLoader(name, queryOptions, options, context) {
|
|
234
|
+
if (options.clause || !context) {
|
|
235
|
+
return new QueryDirectLoader(queryOptions, options, context);
|
|
199
236
|
}
|
|
200
|
-
const key = `${
|
|
201
|
-
return (0, loader_1.getCustomLoader)(key, () => new QueryLoader(
|
|
237
|
+
const key = `${name}:limit:${options.limit}:orderby:${options.orderby}`;
|
|
238
|
+
return (0, loader_1.getCustomLoader)(key, () => new QueryLoader(queryOptions, context, options), context);
|
|
202
239
|
}
|
|
203
240
|
}
|
|
204
241
|
exports.QueryLoaderFactory = QueryLoaderFactory;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Data, EdgeQueryableDataOptions, Ent, ID, LoadEntOptions, Viewer } from "../base";
|
|
2
|
+
import { Clause } from "../clause";
|
|
3
|
+
import { BaseEdgeQuery, IDInfo } from "./query";
|
|
4
|
+
interface CustomClauseQueryOptions<TDest extends Ent<TViewer>, TViewer extends Viewer = Viewer> {
|
|
5
|
+
loadEntOptions: LoadEntOptions<TDest, TViewer>;
|
|
6
|
+
clause: Clause;
|
|
7
|
+
name: string;
|
|
8
|
+
sortColumn?: string;
|
|
9
|
+
disableTransformations?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare class CustomClauseQuery<TDest extends Ent<TViewer>, TViewer extends Viewer = Viewer> extends BaseEdgeQuery<any, TDest, Data> {
|
|
12
|
+
viewer: TViewer;
|
|
13
|
+
private options;
|
|
14
|
+
private clause;
|
|
15
|
+
constructor(viewer: TViewer, options: CustomClauseQueryOptions<TDest, TViewer>);
|
|
16
|
+
sourceEnt(_id: ID): Promise<null>;
|
|
17
|
+
queryRawCount(): Promise<number>;
|
|
18
|
+
queryAllRawCount(): Promise<Map<ID, number>>;
|
|
19
|
+
protected loadRawIDs(_addID: (src: ID) => void): Promise<void>;
|
|
20
|
+
protected loadRawData(_infos: IDInfo[], options: EdgeQueryableDataOptions): Promise<void>;
|
|
21
|
+
dataToID(edge: Data): ID;
|
|
22
|
+
protected loadEntsFromEdges(id: ID, rows: Data[]): Promise<TDest[]>;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CustomClauseQuery = void 0;
|
|
4
|
+
const clause_1 = require("../clause");
|
|
5
|
+
const ent_1 = require("../ent");
|
|
6
|
+
const query_loader_1 = require("../loaders/query_loader");
|
|
7
|
+
const query_1 = require("./query");
|
|
8
|
+
function getClause(opts) {
|
|
9
|
+
let cls = opts.clause;
|
|
10
|
+
if (opts.disableTransformations) {
|
|
11
|
+
return cls;
|
|
12
|
+
}
|
|
13
|
+
let optClause = opts.loadEntOptions.loaderFactory?.options?.clause;
|
|
14
|
+
if (typeof optClause === "function") {
|
|
15
|
+
optClause = optClause();
|
|
16
|
+
}
|
|
17
|
+
if (!optClause) {
|
|
18
|
+
return cls;
|
|
19
|
+
}
|
|
20
|
+
return (0, clause_1.AndOptional)(cls, optClause);
|
|
21
|
+
}
|
|
22
|
+
class CustomClauseQuery extends query_1.BaseEdgeQuery {
|
|
23
|
+
constructor(viewer, options) {
|
|
24
|
+
super(viewer, options.sortColumn || "created_at");
|
|
25
|
+
this.viewer = viewer;
|
|
26
|
+
this.options = options;
|
|
27
|
+
this.clause = getClause(options);
|
|
28
|
+
}
|
|
29
|
+
async sourceEnt(_id) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
async queryRawCount() {
|
|
33
|
+
const row = await (0, ent_1.loadRow)({
|
|
34
|
+
tableName: this.options.loadEntOptions.tableName,
|
|
35
|
+
// sqlite needs as count otherwise it returns count(1)
|
|
36
|
+
fields: ["count(1) as count"],
|
|
37
|
+
clause: this.clause,
|
|
38
|
+
context: this.viewer.context,
|
|
39
|
+
});
|
|
40
|
+
return parseInt(row?.count, 10) || 0;
|
|
41
|
+
}
|
|
42
|
+
async queryAllRawCount() {
|
|
43
|
+
throw new Error(`queryAllRawCount doesn't make sense in CustomClauseQuery`);
|
|
44
|
+
}
|
|
45
|
+
// nothing to do here
|
|
46
|
+
async loadRawIDs(_addID) { }
|
|
47
|
+
async loadRawData(_infos, options) {
|
|
48
|
+
if (!options.orderby) {
|
|
49
|
+
options.orderby = `${this.options.sortColumn} DESC`;
|
|
50
|
+
}
|
|
51
|
+
if (!options.limit) {
|
|
52
|
+
options.limit = ent_1.DefaultLimit;
|
|
53
|
+
}
|
|
54
|
+
let sortCol = this.options.sortColumn || "created_at";
|
|
55
|
+
const rows = await (0, ent_1.loadRows)({
|
|
56
|
+
tableName: this.options.loadEntOptions.tableName,
|
|
57
|
+
fields: this.options.loadEntOptions.fields,
|
|
58
|
+
clause: (0, clause_1.AndOptional)(this.clause, options.clause),
|
|
59
|
+
orderby: (0, query_loader_1.getOrderBy)(sortCol, options?.orderby),
|
|
60
|
+
limit: options?.limit || ent_1.DefaultLimit,
|
|
61
|
+
context: this.viewer.context,
|
|
62
|
+
});
|
|
63
|
+
this.edges.set(1, rows);
|
|
64
|
+
}
|
|
65
|
+
dataToID(edge) {
|
|
66
|
+
return edge.id;
|
|
67
|
+
}
|
|
68
|
+
async loadEntsFromEdges(id, rows) {
|
|
69
|
+
return (0, ent_1.applyPrivacyPolicyForRows)(this.viewer, rows, this.options.loadEntOptions);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.CustomClauseQuery = CustomClauseQuery;
|
|
@@ -1,22 +1,37 @@
|
|
|
1
1
|
import { Data, Ent, ID, EdgeQueryableDataOptions, LoadEntOptions, Viewer, LoaderFactory, ConfigurableLoaderFactory } from "../base";
|
|
2
|
+
import { Clause } from "../clause";
|
|
2
3
|
import { BaseEdgeQuery, IDInfo, EdgeQuery } from "./query";
|
|
3
|
-
export interface
|
|
4
|
+
export interface CustomEdgeQueryOptionsDeprecated<TSource extends Ent<TViewer>, TDest extends Ent<TViewer>, TViewer extends Viewer = Viewer> {
|
|
4
5
|
src: TSource | ID;
|
|
5
6
|
countLoaderFactory: LoaderFactory<ID, number>;
|
|
6
7
|
dataLoaderFactory: ConfigurableLoaderFactory<ID, Data[]>;
|
|
7
8
|
options: LoadEntOptions<TDest, TViewer>;
|
|
8
9
|
sortColumn?: string;
|
|
9
10
|
}
|
|
11
|
+
export interface CustomEdgeQueryOptions<TSource extends Ent<TViewer>, TDest extends Ent<TViewer>, TViewer extends Viewer = Viewer> {
|
|
12
|
+
src: TSource | ID;
|
|
13
|
+
loadEntOptions: LoadEntOptions<TDest, TViewer>;
|
|
14
|
+
groupCol?: string;
|
|
15
|
+
clause?: Clause;
|
|
16
|
+
name: string;
|
|
17
|
+
sortColumn?: string;
|
|
18
|
+
disableTransformations?: boolean;
|
|
19
|
+
}
|
|
10
20
|
export declare abstract class CustomEdgeQueryBase<TSource extends Ent<TViewer>, TDest extends Ent<TViewer>, TViewer extends Viewer = Viewer> extends BaseEdgeQuery<TSource, TDest, Data> implements EdgeQuery<TSource, TDest, Data> {
|
|
11
21
|
viewer: TViewer;
|
|
12
22
|
private options;
|
|
13
23
|
private id;
|
|
14
|
-
|
|
24
|
+
private opts;
|
|
25
|
+
constructor(viewer: TViewer, options: CustomEdgeQueryOptionsDeprecated<TSource, TDest, TViewer> | CustomEdgeQueryOptions<TSource, TDest, TViewer>);
|
|
15
26
|
abstract sourceEnt(id: ID): Promise<Ent | null>;
|
|
16
27
|
private idVisible;
|
|
28
|
+
private isDeprecatedOptions;
|
|
29
|
+
private getCountLoader;
|
|
30
|
+
private getQueryLoader;
|
|
17
31
|
queryRawCount(): Promise<number>;
|
|
18
32
|
queryAllRawCount(): Promise<Map<ID, number>>;
|
|
19
33
|
protected loadRawIDs(addID: (src: ID | TSource) => void): Promise<void>;
|
|
34
|
+
private getLoadEntOptions;
|
|
20
35
|
protected loadRawData(infos: IDInfo[], options: EdgeQueryableDataOptions): Promise<void>;
|
|
21
36
|
dataToID(edge: Data): ID;
|
|
22
37
|
protected loadEntsFromEdges(id: ID, rows: Data[]): Promise<TDest[]>;
|
|
@@ -1,11 +1,54 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CustomEdgeQueryBase = void 0;
|
|
4
|
+
const clause_1 = require("../clause");
|
|
4
5
|
const ent_1 = require("../ent");
|
|
6
|
+
const loaders_1 = require("../loaders");
|
|
5
7
|
const query_1 = require("./query");
|
|
8
|
+
function getClause(opts) {
|
|
9
|
+
let cls = opts.clause;
|
|
10
|
+
if (opts.disableTransformations) {
|
|
11
|
+
return cls;
|
|
12
|
+
}
|
|
13
|
+
let optClause = opts.loadEntOptions.loaderFactory?.options?.clause;
|
|
14
|
+
if (typeof optClause === "function") {
|
|
15
|
+
optClause = optClause();
|
|
16
|
+
}
|
|
17
|
+
if (!optClause) {
|
|
18
|
+
return cls;
|
|
19
|
+
}
|
|
20
|
+
return (0, clause_1.AndOptional)(cls, optClause);
|
|
21
|
+
}
|
|
22
|
+
function getRawCountLoader(viewer, opts) {
|
|
23
|
+
if (!viewer.context?.cache) {
|
|
24
|
+
return new loaders_1.RawCountLoader({
|
|
25
|
+
tableName: opts.loadEntOptions.tableName,
|
|
26
|
+
groupCol: opts.groupCol,
|
|
27
|
+
clause: getClause(opts),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const name = `custom_query_count_loader:${opts.name}`;
|
|
31
|
+
return viewer.context.cache.getLoader(name, () => new loaders_1.RawCountLoader({
|
|
32
|
+
tableName: opts.loadEntOptions.tableName,
|
|
33
|
+
groupCol: opts.groupCol,
|
|
34
|
+
clause: getClause(opts),
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
function getQueryLoader(viewer, opts, options) {
|
|
38
|
+
const loader = opts.loadEntOptions.loaderFactory;
|
|
39
|
+
const name = `custom_query_loader:${opts.name}`;
|
|
40
|
+
return loaders_1.QueryLoaderFactory.createConfigurableLoader(name, {
|
|
41
|
+
tableName: opts.loadEntOptions.tableName,
|
|
42
|
+
fields: opts.loadEntOptions.fields,
|
|
43
|
+
groupCol: opts.groupCol,
|
|
44
|
+
clause: getClause(opts),
|
|
45
|
+
toPrime: [loader],
|
|
46
|
+
}, options, viewer.context);
|
|
47
|
+
}
|
|
6
48
|
class CustomEdgeQueryBase extends query_1.BaseEdgeQuery {
|
|
7
49
|
constructor(viewer, options) {
|
|
8
|
-
|
|
50
|
+
// @ts-ignore
|
|
51
|
+
super(viewer, options?.sortColumn || "created_at");
|
|
9
52
|
this.viewer = viewer;
|
|
10
53
|
this.options = options;
|
|
11
54
|
options.sortColumn = options.sortColumn || "created_at";
|
|
@@ -15,6 +58,7 @@ class CustomEdgeQueryBase extends query_1.BaseEdgeQuery {
|
|
|
15
58
|
else {
|
|
16
59
|
this.id = options.src;
|
|
17
60
|
}
|
|
61
|
+
this.opts = this.getLoadEntOptions();
|
|
18
62
|
}
|
|
19
63
|
async idVisible() {
|
|
20
64
|
const ids = await this.genIDInfosToFetch();
|
|
@@ -23,14 +67,28 @@ class CustomEdgeQueryBase extends query_1.BaseEdgeQuery {
|
|
|
23
67
|
}
|
|
24
68
|
return !ids[0].invalidated;
|
|
25
69
|
}
|
|
70
|
+
isDeprecatedOptions(options) {
|
|
71
|
+
return (options
|
|
72
|
+
.countLoaderFactory !== undefined);
|
|
73
|
+
}
|
|
74
|
+
getCountLoader() {
|
|
75
|
+
if (this.isDeprecatedOptions(this.options)) {
|
|
76
|
+
return this.options.countLoaderFactory.createLoader(this.viewer.context);
|
|
77
|
+
}
|
|
78
|
+
return getRawCountLoader(this.viewer, this.options);
|
|
79
|
+
}
|
|
80
|
+
getQueryLoader(options) {
|
|
81
|
+
if (this.isDeprecatedOptions(this.options)) {
|
|
82
|
+
return this.options.dataLoaderFactory.createConfigurableLoader(options, this.viewer.context);
|
|
83
|
+
}
|
|
84
|
+
return getQueryLoader(this.viewer, this.options, options);
|
|
85
|
+
}
|
|
26
86
|
async queryRawCount() {
|
|
27
87
|
const idVisible = await this.idVisible();
|
|
28
88
|
if (!idVisible) {
|
|
29
89
|
return 0;
|
|
30
90
|
}
|
|
31
|
-
return
|
|
32
|
-
.createLoader(this.viewer.context)
|
|
33
|
-
.load(this.id);
|
|
91
|
+
return this.getCountLoader().load(this.id);
|
|
34
92
|
}
|
|
35
93
|
async queryAllRawCount() {
|
|
36
94
|
let count = 0;
|
|
@@ -43,17 +101,27 @@ class CustomEdgeQueryBase extends query_1.BaseEdgeQuery {
|
|
|
43
101
|
async loadRawIDs(addID) {
|
|
44
102
|
addID(this.options.src);
|
|
45
103
|
}
|
|
104
|
+
getLoadEntOptions() {
|
|
105
|
+
let opts;
|
|
106
|
+
if (this.isDeprecatedOptions(this.options)) {
|
|
107
|
+
opts = this.options.options;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
opts = this.options.loadEntOptions;
|
|
111
|
+
}
|
|
112
|
+
return opts;
|
|
113
|
+
}
|
|
46
114
|
async loadRawData(infos, options) {
|
|
47
|
-
|
|
115
|
+
if (infos.length !== 1) {
|
|
116
|
+
throw new Error(`expected 1 info passed to loadRawData. ${infos.length} passed`);
|
|
117
|
+
}
|
|
48
118
|
if (!options.orderby) {
|
|
49
119
|
options.orderby = `${this.options.sortColumn} DESC`;
|
|
50
120
|
}
|
|
51
121
|
if (!options.limit) {
|
|
52
122
|
options.limit = ent_1.DefaultLimit;
|
|
53
123
|
}
|
|
54
|
-
|
|
55
|
-
throw new Error(`expected 1 info passed to loadRawData. ${infos.length} passed`);
|
|
56
|
-
}
|
|
124
|
+
const loader = this.getQueryLoader(options);
|
|
57
125
|
const info = infos[0];
|
|
58
126
|
if (info.invalidated) {
|
|
59
127
|
this.edges.set(this.id, []);
|
|
@@ -66,8 +134,7 @@ class CustomEdgeQueryBase extends query_1.BaseEdgeQuery {
|
|
|
66
134
|
return edge.id;
|
|
67
135
|
}
|
|
68
136
|
async loadEntsFromEdges(id, rows) {
|
|
69
|
-
|
|
70
|
-
return Array.from(ents.values());
|
|
137
|
+
return (0, ent_1.applyPrivacyPolicyForRows)(this.viewer, rows, this.opts);
|
|
71
138
|
}
|
|
72
139
|
}
|
|
73
140
|
exports.CustomEdgeQueryBase = CustomEdgeQueryBase;
|
package/core/query/index.d.ts
CHANGED
package/core/query/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CustomEdgeQueryBase = exports.AssocEdgeQueryBase = exports.BaseEdgeQuery = void 0;
|
|
3
|
+
exports.CustomClauseQuery = exports.CustomEdgeQueryBase = exports.AssocEdgeQueryBase = exports.BaseEdgeQuery = void 0;
|
|
4
4
|
var query_1 = require("./query");
|
|
5
5
|
Object.defineProperty(exports, "BaseEdgeQuery", { enumerable: true, get: function () { return query_1.BaseEdgeQuery; } });
|
|
6
6
|
var assoc_query_1 = require("./assoc_query");
|
|
7
7
|
Object.defineProperty(exports, "AssocEdgeQueryBase", { enumerable: true, get: function () { return assoc_query_1.AssocEdgeQueryBase; } });
|
|
8
8
|
var custom_query_1 = require("./custom_query");
|
|
9
9
|
Object.defineProperty(exports, "CustomEdgeQueryBase", { enumerable: true, get: function () { return custom_query_1.CustomEdgeQueryBase; } });
|
|
10
|
+
var custom_clause_query_1 = require("./custom_clause_query");
|
|
11
|
+
Object.defineProperty(exports, "CustomClauseQuery", { enumerable: true, get: function () { return custom_clause_query_1.CustomClauseQuery; } });
|
package/core/query/query.js
CHANGED
|
@@ -89,7 +89,13 @@ class FirstFilter {
|
|
|
89
89
|
// todo may not be desc
|
|
90
90
|
// and if asc
|
|
91
91
|
// clause below should switch to greater...
|
|
92
|
-
|
|
92
|
+
const sortCol = this.sortCol.toLowerCase();
|
|
93
|
+
if (sortCol.endsWith("desc") || sortCol.endsWith("asc")) {
|
|
94
|
+
options.orderby = `${this.sortCol}`;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
options.orderby = `${this.sortCol} DESC`;
|
|
98
|
+
}
|
|
93
99
|
// we sort by most recent first
|
|
94
100
|
// so when paging, we fetch afterCursor X
|
|
95
101
|
if (this.offset) {
|
|
@@ -100,6 +106,7 @@ class FirstFilter {
|
|
|
100
106
|
options.clause = clause.Less(this.sortCol, new Date(this.offset).toISOString());
|
|
101
107
|
}
|
|
102
108
|
}
|
|
109
|
+
// console.debug("filter opts", options, this.options);
|
|
103
110
|
return options;
|
|
104
111
|
}
|
|
105
112
|
// TODO?
|
|
@@ -12,6 +12,7 @@ const logger_1 = require("../logger");
|
|
|
12
12
|
const test_edge_global_schema_1 = require("../../testutils/test_edge_global_schema");
|
|
13
13
|
const builder_1 = require("../../testutils/builder");
|
|
14
14
|
const action_1 = require("../../action");
|
|
15
|
+
const mock_log_1 = require("../../testutils/mock_log");
|
|
15
16
|
class TestQueryFilter {
|
|
16
17
|
constructor(filter, newQuery, ents, defaultViewer) {
|
|
17
18
|
this.filter = filter;
|
|
@@ -20,11 +21,15 @@ class TestQueryFilter {
|
|
|
20
21
|
this.defaultViewer = defaultViewer;
|
|
21
22
|
this.allContacts = [];
|
|
22
23
|
this.filteredContacts = [];
|
|
24
|
+
// @ts-ignore
|
|
25
|
+
const q = this.newQuery(this.defaultViewer);
|
|
26
|
+
// TODO sad not generic enough
|
|
27
|
+
this.customQuery =
|
|
28
|
+
q instanceof index_1.UserToContactsFkeyQuery ||
|
|
29
|
+
q instanceof index_1.UserToContactsFkeyQueryDeprecated;
|
|
23
30
|
}
|
|
24
|
-
async
|
|
25
|
-
// console.log("sss");
|
|
31
|
+
async createData() {
|
|
26
32
|
[this.user, this.allContacts] = await (0, test_helpers_1.createAllContacts)();
|
|
27
|
-
// console.log(this.user, this.contacts);
|
|
28
33
|
// this.allContacts = this.allContacts.reverse();
|
|
29
34
|
this.filteredContacts = this.ents(this.allContacts);
|
|
30
35
|
db_mock_1.QueryRecorder.clearQueries();
|
|
@@ -61,16 +66,55 @@ class TestQueryFilter {
|
|
|
61
66
|
verifyEdges(edges) {
|
|
62
67
|
const q = this.getQuery();
|
|
63
68
|
// TODO sad not generic enough
|
|
64
|
-
if (
|
|
69
|
+
if (this.customQuery) {
|
|
65
70
|
(0, test_helpers_1.verifyUserToContactRawData)(this.user, edges, this.filteredContacts);
|
|
66
71
|
}
|
|
67
72
|
else {
|
|
68
73
|
(0, test_helpers_1.verifyUserToContactEdges)(this.user, edges, this.filteredContacts);
|
|
69
74
|
}
|
|
70
75
|
}
|
|
71
|
-
async testEnts() {
|
|
72
|
-
const
|
|
73
|
-
this.verifyEnts(
|
|
76
|
+
async testEnts(v) {
|
|
77
|
+
const ents = await this.getQuery(v || new viewer_1.IDViewer(this.user.id)).queryEnts();
|
|
78
|
+
this.verifyEnts(ents);
|
|
79
|
+
return ents;
|
|
80
|
+
}
|
|
81
|
+
async testEntsCache() {
|
|
82
|
+
if (!this.customQuery) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
(0, logger_1.setLogLevels)(["query", "cache"]);
|
|
86
|
+
const ml = new mock_log_1.MockLogs();
|
|
87
|
+
ml.mock();
|
|
88
|
+
const v = new test_context_1.TestContext(new viewer_1.IDViewer(this.user.id)).getViewer();
|
|
89
|
+
const ents = await this.testEnts(v);
|
|
90
|
+
expect(ml.logs.length).toBe(1);
|
|
91
|
+
expect(ml.logs[0].query).toMatch(/SELECT (.+) FROM /);
|
|
92
|
+
await Promise.all(ents.map((ent) => index_1.FakeContact.loadX(v, ent.id)));
|
|
93
|
+
expect(ml.logs.length).toBe(this.filteredContacts.length + 1);
|
|
94
|
+
for (const log of ml.logs.slice(1)) {
|
|
95
|
+
expect(log["ent-cache-hit"]).toBeDefined();
|
|
96
|
+
}
|
|
97
|
+
ml.restore();
|
|
98
|
+
(0, logger_1.clearLogLevels)();
|
|
99
|
+
}
|
|
100
|
+
async testDataCache() {
|
|
101
|
+
if (!this.customQuery) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
(0, logger_1.setLogLevels)(["query", "cache"]);
|
|
105
|
+
const ml = new mock_log_1.MockLogs();
|
|
106
|
+
ml.mock();
|
|
107
|
+
const v = new test_context_1.TestContext(new viewer_1.IDViewer(this.user.id)).getViewer();
|
|
108
|
+
const ents = await this.testEnts(v);
|
|
109
|
+
expect(ml.logs.length).toBe(1);
|
|
110
|
+
expect(ml.logs[0].query).toMatch(/SELECT (.+) FROM /);
|
|
111
|
+
await Promise.all(ents.map((ent) => index_1.FakeContact.loadRawData(ent.id, v.context)));
|
|
112
|
+
expect(ml.logs.length).toBe(this.filteredContacts.length + 1);
|
|
113
|
+
for (const log of ml.logs.slice(1)) {
|
|
114
|
+
expect(log["dataloader-cache-hit"]).toBeDefined();
|
|
115
|
+
}
|
|
116
|
+
ml.restore();
|
|
117
|
+
(0, logger_1.clearLogLevels)();
|
|
74
118
|
}
|
|
75
119
|
verifyEnts(ents) {
|
|
76
120
|
(0, test_helpers_1.verifyUserToContacts)(this.user, ents, this.filteredContacts);
|
|
@@ -203,17 +247,21 @@ const commonTests = (opts) => {
|
|
|
203
247
|
}
|
|
204
248
|
let tdb;
|
|
205
249
|
if (opts.sqlite) {
|
|
206
|
-
(0, temp_db_1.setupSqlite)(`sqlite:///shared_test+${opts.uniqKey}.db`, () => (0, test_helpers_1.tempDBTables)(opts.globalSchema)
|
|
250
|
+
(0, temp_db_1.setupSqlite)(`sqlite:///shared_test+${opts.uniqKey}.db`, () => (0, test_helpers_1.tempDBTables)(opts.globalSchema), {
|
|
251
|
+
disableDeleteAfterEachTest: true,
|
|
252
|
+
});
|
|
207
253
|
}
|
|
208
254
|
beforeAll(async () => {
|
|
209
255
|
// want error on by default in tests?
|
|
210
256
|
(0, logger_1.setLogLevels)(["error", "warn", "info"]);
|
|
211
257
|
if (opts.livePostgresDB) {
|
|
212
258
|
tdb = await (0, test_helpers_1.setupTempDB)();
|
|
259
|
+
return;
|
|
213
260
|
}
|
|
261
|
+
await (0, test_helpers_1.createEdges)();
|
|
214
262
|
});
|
|
215
263
|
beforeEach(async () => {
|
|
216
|
-
if (opts.livePostgresDB) {
|
|
264
|
+
if (opts.livePostgresDB || opts.sqlite) {
|
|
217
265
|
return;
|
|
218
266
|
}
|
|
219
267
|
await (0, test_helpers_1.createEdges)();
|
|
@@ -233,7 +281,7 @@ const commonTests = (opts) => {
|
|
|
233
281
|
return contacts.reverse();
|
|
234
282
|
}, getViewer());
|
|
235
283
|
beforeEach(async () => {
|
|
236
|
-
await filter.
|
|
284
|
+
await filter.createData();
|
|
237
285
|
});
|
|
238
286
|
test("ids", async () => {
|
|
239
287
|
await filter.testIDs();
|
|
@@ -258,6 +306,12 @@ const commonTests = (opts) => {
|
|
|
258
306
|
test("all", async () => {
|
|
259
307
|
await filter.testAll();
|
|
260
308
|
});
|
|
309
|
+
test("ents cache", async () => {
|
|
310
|
+
await filter.testEntsCache();
|
|
311
|
+
});
|
|
312
|
+
test("data cache", async () => {
|
|
313
|
+
await filter.testDataCache();
|
|
314
|
+
});
|
|
261
315
|
});
|
|
262
316
|
describe("after delete", () => {
|
|
263
317
|
const filter = new TestQueryFilter((q) => {
|
|
@@ -268,7 +322,7 @@ const commonTests = (opts) => {
|
|
|
268
322
|
return [];
|
|
269
323
|
}, getViewer());
|
|
270
324
|
beforeEach(async () => {
|
|
271
|
-
await filter.
|
|
325
|
+
await filter.createData();
|
|
272
326
|
const action = new builder_1.SimpleAction(filter.user.viewer, index_1.FakeUserSchema, new Map(), action_1.WriteOperation.Edit, filter.user);
|
|
273
327
|
await Promise.all(filter.allContacts.map(async (contact) => {
|
|
274
328
|
action.builder.orchestrator.removeOutboundEdge(contact.id, index_1.EdgeType.UserToContacts);
|
|
@@ -307,6 +361,12 @@ const commonTests = (opts) => {
|
|
|
307
361
|
await opts.rawDataVerify(filter.user);
|
|
308
362
|
}
|
|
309
363
|
});
|
|
364
|
+
test("ents cache", async () => {
|
|
365
|
+
await filter.testEntsCache();
|
|
366
|
+
});
|
|
367
|
+
test("data cache", async () => {
|
|
368
|
+
await filter.testDataCache();
|
|
369
|
+
});
|
|
310
370
|
});
|
|
311
371
|
describe("first. no cursor", () => {
|
|
312
372
|
const N = 2;
|
|
@@ -317,7 +377,7 @@ const commonTests = (opts) => {
|
|
|
317
377
|
return contacts.reverse().slice(0, N);
|
|
318
378
|
}, getViewer());
|
|
319
379
|
beforeEach(async () => {
|
|
320
|
-
await filter.
|
|
380
|
+
await filter.createData();
|
|
321
381
|
});
|
|
322
382
|
test("ids", async () => {
|
|
323
383
|
await filter.testIDs();
|
|
@@ -342,6 +402,12 @@ const commonTests = (opts) => {
|
|
|
342
402
|
test("all", async () => {
|
|
343
403
|
await filter.testAll();
|
|
344
404
|
});
|
|
405
|
+
test("ents cache", async () => {
|
|
406
|
+
await filter.testEntsCache();
|
|
407
|
+
});
|
|
408
|
+
test("data cache", async () => {
|
|
409
|
+
await filter.testDataCache();
|
|
410
|
+
});
|
|
345
411
|
});
|
|
346
412
|
describe("last", () => {
|
|
347
413
|
const N = 2;
|
|
@@ -353,7 +419,7 @@ const commonTests = (opts) => {
|
|
|
353
419
|
return contacts.slice(0, N).reverse();
|
|
354
420
|
}, getViewer());
|
|
355
421
|
beforeEach(async () => {
|
|
356
|
-
await filter.
|
|
422
|
+
await filter.createData();
|
|
357
423
|
});
|
|
358
424
|
test("ids", async () => {
|
|
359
425
|
await filter.testIDs();
|
|
@@ -378,6 +444,12 @@ const commonTests = (opts) => {
|
|
|
378
444
|
test("all", async () => {
|
|
379
445
|
await filter.testAll();
|
|
380
446
|
});
|
|
447
|
+
test("ents cache", async () => {
|
|
448
|
+
await filter.testEntsCache();
|
|
449
|
+
});
|
|
450
|
+
test("data cache", async () => {
|
|
451
|
+
await filter.testDataCache();
|
|
452
|
+
});
|
|
381
453
|
});
|
|
382
454
|
describe("first after cursor", () => {
|
|
383
455
|
const idx = 2;
|
|
@@ -388,8 +460,18 @@ const commonTests = (opts) => {
|
|
|
388
460
|
// < check so we shouldn't get that index
|
|
389
461
|
return contacts.reverse().slice(idx + 1, idx + N);
|
|
390
462
|
}, getViewer());
|
|
463
|
+
beforeAll(async () => {
|
|
464
|
+
if (opts.livePostgresDB || opts.sqlite) {
|
|
465
|
+
await filter.createData();
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
// TODO do we still need QueryRecorder?
|
|
469
|
+
// should just delete this...
|
|
391
470
|
beforeEach(async () => {
|
|
392
|
-
|
|
471
|
+
if (opts.livePostgresDB || opts.sqlite) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
await filter.createData();
|
|
393
475
|
});
|
|
394
476
|
test("ids", async () => {
|
|
395
477
|
await filter.testIDs();
|
|
@@ -414,6 +496,12 @@ const commonTests = (opts) => {
|
|
|
414
496
|
test("all", async () => {
|
|
415
497
|
await filter.testAll();
|
|
416
498
|
});
|
|
499
|
+
test("ents cache", async () => {
|
|
500
|
+
await filter.testEntsCache();
|
|
501
|
+
});
|
|
502
|
+
test("data cache", async () => {
|
|
503
|
+
await filter.testDataCache();
|
|
504
|
+
});
|
|
417
505
|
});
|
|
418
506
|
test("first. after each cursor", async () => {
|
|
419
507
|
let [user, contacts] = await (0, test_helpers_1.createAllContacts)();
|
|
@@ -456,8 +544,17 @@ const commonTests = (opts) => {
|
|
|
456
544
|
// > check so we don't want that index
|
|
457
545
|
return contacts.reverse().slice(0, idx).reverse(); // because of order returned
|
|
458
546
|
}, getViewer());
|
|
547
|
+
beforeAll(async () => {
|
|
548
|
+
if (opts.livePostgresDB || opts.sqlite) {
|
|
549
|
+
await filter.createData();
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
// same TODO above
|
|
459
553
|
beforeEach(async () => {
|
|
460
|
-
|
|
554
|
+
if (opts.livePostgresDB || opts.sqlite) {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
await filter.createData();
|
|
461
558
|
});
|
|
462
559
|
test("ids", async () => {
|
|
463
560
|
await filter.testIDs();
|
|
@@ -482,6 +579,12 @@ const commonTests = (opts) => {
|
|
|
482
579
|
test("all", async () => {
|
|
483
580
|
await filter.testAll();
|
|
484
581
|
});
|
|
582
|
+
test("ents cache", async () => {
|
|
583
|
+
await filter.testEntsCache();
|
|
584
|
+
});
|
|
585
|
+
test("data cache", async () => {
|
|
586
|
+
await filter.testDataCache();
|
|
587
|
+
});
|
|
485
588
|
});
|
|
486
589
|
test("last. before each cursor", async () => {
|
|
487
590
|
let [user, contacts] = await (0, test_helpers_1.createAllContacts)();
|
package/package.json
CHANGED
package/testutils/db/temp_db.js
CHANGED
|
@@ -304,7 +304,12 @@ function table(name, ...items) {
|
|
|
304
304
|
parts.push("PRIMARY KEY");
|
|
305
305
|
}
|
|
306
306
|
if (col.default !== undefined) {
|
|
307
|
-
|
|
307
|
+
if (db_1.Dialect.SQLite === db_1.default.getDialect()) {
|
|
308
|
+
parts.push(`DEFAULT "${col.default}"`);
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
parts.push(`DEFAULT ${col.default}`);
|
|
312
|
+
}
|
|
308
313
|
}
|
|
309
314
|
if (col.unique) {
|
|
310
315
|
parts.push("UNIQUE");
|
|
@@ -384,6 +389,7 @@ class TempDB {
|
|
|
384
389
|
else {
|
|
385
390
|
process.env.DB_CONNECTION_STRING = `postgres://localhost/${this.db}?`;
|
|
386
391
|
}
|
|
392
|
+
db_1.default.initDB();
|
|
387
393
|
}
|
|
388
394
|
else {
|
|
389
395
|
// will probably be setup via loadConfig
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ID, Ent, Viewer, Data, LoadEntOptions, PrivacyPolicy } from "../../core/base";
|
|
1
|
+
import { ID, Ent, Viewer, Data, LoadEntOptions, PrivacyPolicy, Context } from "../../core/base";
|
|
2
2
|
import { SimpleBuilder } from "../builder";
|
|
3
3
|
import { NodeType } from "./const";
|
|
4
4
|
import { ObjectLoaderFactory } from "../../core/loaders";
|
|
@@ -20,6 +20,7 @@ export declare class FakeContact implements Ent {
|
|
|
20
20
|
static loaderOptions(): LoadEntOptions<FakeContact>;
|
|
21
21
|
static load(v: Viewer, id: ID): Promise<FakeContact | null>;
|
|
22
22
|
static loadX(v: Viewer, id: ID): Promise<FakeContact>;
|
|
23
|
+
static loadRawData(id: ID, context?: Context): Promise<Data | null>;
|
|
23
24
|
}
|
|
24
25
|
export declare const FakeContactSchema: import("../builder").BuilderSchema<FakeContact>;
|
|
25
26
|
export interface ContactCreateInput {
|
|
@@ -60,6 +60,11 @@ class FakeContact {
|
|
|
60
60
|
static async loadX(v, id) {
|
|
61
61
|
return (0, ent_1.loadEntX)(v, id, FakeContact.loaderOptions());
|
|
62
62
|
}
|
|
63
|
+
static async loadRawData(id, context) {
|
|
64
|
+
return FakeContact.loaderOptions()
|
|
65
|
+
.loaderFactory.createLoader(context)
|
|
66
|
+
.load(id);
|
|
67
|
+
}
|
|
63
68
|
}
|
|
64
69
|
exports.FakeContact = FakeContact;
|
|
65
70
|
exports.FakeContactSchema = (0, builder_1.getBuilderSchemaFromFields)({
|
|
@@ -24,6 +24,8 @@ export declare class FakeEvent implements Ent {
|
|
|
24
24
|
}
|
|
25
25
|
export declare const FakeEventSchema: import("../builder").BuilderSchema<FakeEvent>;
|
|
26
26
|
export interface EventCreateInput {
|
|
27
|
+
createdAt?: Date;
|
|
28
|
+
updatedAt?: Date;
|
|
27
29
|
startTime: Date;
|
|
28
30
|
endTime?: Date | null;
|
|
29
31
|
location: string;
|
|
@@ -26,6 +26,7 @@ interface options {
|
|
|
26
26
|
interval: number;
|
|
27
27
|
userInput?: Partial<UserCreateInput>;
|
|
28
28
|
eventInputs?: Partial<EventCreateInput>[];
|
|
29
|
+
startTime?: number | Date;
|
|
29
30
|
}
|
|
30
31
|
export declare function createAllEvents(opts: options): Promise<[FakeUser, FakeEvent[]]>;
|
|
31
32
|
export {};
|
|
@@ -41,6 +41,8 @@ function getEventInput(user, input) {
|
|
|
41
41
|
title: "title",
|
|
42
42
|
description: "fun event",
|
|
43
43
|
userID: user.id,
|
|
44
|
+
createdAt: new Date(),
|
|
45
|
+
updatedAt: new Date(),
|
|
44
46
|
...input,
|
|
45
47
|
};
|
|
46
48
|
}
|
|
@@ -232,7 +234,7 @@ async function createAllEvents(opts) {
|
|
|
232
234
|
let arr = new Array(opts.howMany);
|
|
233
235
|
arr.fill(1);
|
|
234
236
|
// start at date in case something else has used a date already
|
|
235
|
-
(0, jest_date_mock_1.advanceTo)(mock_date_1.MockDate.getDate());
|
|
237
|
+
(0, jest_date_mock_1.advanceTo)(opts.startTime || mock_date_1.MockDate.getDate());
|
|
236
238
|
const events = await Promise.all(arr.map(async (v, idx) => {
|
|
237
239
|
// just to make times deterministic so that tests can consistently work
|
|
238
240
|
if (opts.interval > 0) {
|
|
@@ -5,7 +5,6 @@ import * as clause from "../../core/clause";
|
|
|
5
5
|
import { AssocEdgeQueryBase, EdgeQuerySource } from "../../core/query/assoc_query";
|
|
6
6
|
import { FakeUser, FakeEvent, FakeContact, EventToAttendeesQuery, EventToDeclinedQuery, EventToHostsQuery, EventToInvitedQuery, EventToMaybeQuery } from "./internal";
|
|
7
7
|
import { RawCountLoaderFactory } from "../../core/loaders/raw_count_loader";
|
|
8
|
-
import { IndexLoaderFactory } from "../../core/loaders/index_loader";
|
|
9
8
|
import { QueryLoaderFactory } from "../../core/loaders/query_loader";
|
|
10
9
|
export declare class UserToContactsQuery extends AssocEdgeQueryBase<FakeUser, FakeContact, AssocEdge> {
|
|
11
10
|
constructor(viewer: Viewer, src: EdgeQuerySource<FakeUser>);
|
|
@@ -13,7 +12,12 @@ export declare class UserToContactsQuery extends AssocEdgeQueryBase<FakeUser, Fa
|
|
|
13
12
|
sourceEnt(id: ID): Promise<FakeUser | null>;
|
|
14
13
|
}
|
|
15
14
|
export declare const userToContactsCountLoaderFactory: RawCountLoaderFactory;
|
|
16
|
-
export declare const userToContactsDataLoaderFactory:
|
|
15
|
+
export declare const userToContactsDataLoaderFactory: QueryLoaderFactory<unknown>;
|
|
16
|
+
export declare class UserToContactsFkeyQueryDeprecated extends CustomEdgeQueryBase<FakeUser, FakeContact> {
|
|
17
|
+
constructor(viewer: Viewer, src: ID | FakeUser);
|
|
18
|
+
static query(viewer: Viewer, src: FakeUser | ID): UserToContactsFkeyQueryDeprecated;
|
|
19
|
+
sourceEnt(id: ID): Promise<FakeUser | null>;
|
|
20
|
+
}
|
|
17
21
|
export declare class UserToContactsFkeyQuery extends CustomEdgeQueryBase<FakeUser, FakeContact> {
|
|
18
22
|
constructor(viewer: Viewer, src: ID | FakeUser);
|
|
19
23
|
static query(viewer: Viewer, src: FakeUser | ID): UserToContactsFkeyQuery;
|
|
@@ -84,8 +88,6 @@ export declare class UserToHostedEventsQuery extends AssocEdgeQueryBase<FakeUser
|
|
|
84
88
|
}
|
|
85
89
|
export declare const getNextWeekClause: () => clause.Clause;
|
|
86
90
|
export declare function getCompleteClause(id: ID): clause.Clause;
|
|
87
|
-
export declare const userToEventsInNextWeekCountLoaderFactory: RawCountLoaderFactory;
|
|
88
|
-
export declare const userToEventsInNextWeekDataLoaderFactory: QueryLoaderFactory<unknown>;
|
|
89
91
|
export declare class UserToEventsInNextWeekQuery extends CustomEdgeQueryBase<FakeUser, FakeEvent> {
|
|
90
92
|
constructor(viewer: Viewer, src: ID | FakeUser);
|
|
91
93
|
static query(viewer: Viewer, src: FakeUser | ID): UserToEventsInNextWeekQuery;
|
|
@@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
19
19
|
return result;
|
|
20
20
|
};
|
|
21
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.UserToFollowingQuery = exports.UserToEventsInNextWeekQuery = exports.
|
|
22
|
+
exports.UserToFollowingQuery = exports.UserToEventsInNextWeekQuery = exports.getCompleteClause = exports.getNextWeekClause = exports.UserToHostedEventsQuery = exports.UserToEventsAttendingQuery = exports.UserToIncomingFriendRequestsQuery = exports.UserToFriendRequestsQuery = exports.UserToCustomEdgeQuery = exports.CustomEdge = exports.UserToFriendsQuery = exports.UserToContactsFkeyQuery = exports.UserToContactsFkeyQueryDeprecated = exports.userToContactsDataLoaderFactory = exports.userToContactsCountLoaderFactory = exports.UserToContactsQuery = void 0;
|
|
23
23
|
const custom_query_1 = require("../../core/query/custom_query");
|
|
24
24
|
const ent_1 = require("../../core/ent");
|
|
25
25
|
const clause = __importStar(require("../../core/clause"));
|
|
@@ -28,7 +28,6 @@ const internal_1 = require("./internal");
|
|
|
28
28
|
const raw_count_loader_1 = require("../../core/loaders/raw_count_loader");
|
|
29
29
|
const assoc_count_loader_1 = require("../../core/loaders/assoc_count_loader");
|
|
30
30
|
const assoc_edge_loader_1 = require("../../core/loaders/assoc_edge_loader");
|
|
31
|
-
const index_loader_1 = require("../../core/loaders/index_loader");
|
|
32
31
|
const fake_contact_1 = require("./fake_contact");
|
|
33
32
|
const jest_date_mock_1 = require("jest-date-mock");
|
|
34
33
|
const luxon_1 = require("luxon");
|
|
@@ -52,10 +51,12 @@ exports.userToContactsCountLoaderFactory = new raw_count_loader_1.RawCountLoader
|
|
|
52
51
|
...internal_1.FakeContact.loaderOptions(),
|
|
53
52
|
groupCol: "user_id",
|
|
54
53
|
});
|
|
55
|
-
exports.userToContactsDataLoaderFactory = new
|
|
54
|
+
exports.userToContactsDataLoaderFactory = new query_loader_1.QueryLoaderFactory({
|
|
55
|
+
...internal_1.FakeContact.loaderOptions(),
|
|
56
|
+
groupCol: "user_id",
|
|
56
57
|
toPrime: [fake_contact_1.contactLoader],
|
|
57
58
|
});
|
|
58
|
-
class
|
|
59
|
+
class UserToContactsFkeyQueryDeprecated extends custom_query_1.CustomEdgeQueryBase {
|
|
59
60
|
constructor(viewer, src) {
|
|
60
61
|
super(viewer, {
|
|
61
62
|
src,
|
|
@@ -65,6 +66,24 @@ class UserToContactsFkeyQuery extends custom_query_1.CustomEdgeQueryBase {
|
|
|
65
66
|
options: internal_1.FakeContact.loaderOptions(),
|
|
66
67
|
});
|
|
67
68
|
}
|
|
69
|
+
static query(viewer, src) {
|
|
70
|
+
return new UserToContactsFkeyQueryDeprecated(viewer, src);
|
|
71
|
+
}
|
|
72
|
+
sourceEnt(id) {
|
|
73
|
+
return internal_1.FakeUser.load(this.viewer, id);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.UserToContactsFkeyQueryDeprecated = UserToContactsFkeyQueryDeprecated;
|
|
77
|
+
// this replaces UserToContactsFkeyQueryDeprecated
|
|
78
|
+
class UserToContactsFkeyQuery extends custom_query_1.CustomEdgeQueryBase {
|
|
79
|
+
constructor(viewer, src) {
|
|
80
|
+
super(viewer, {
|
|
81
|
+
src,
|
|
82
|
+
loadEntOptions: internal_1.FakeContact.loaderOptions(),
|
|
83
|
+
groupCol: "user_id",
|
|
84
|
+
name: "user_to_contacts",
|
|
85
|
+
});
|
|
86
|
+
}
|
|
68
87
|
static query(viewer, src) {
|
|
69
88
|
return new UserToContactsFkeyQuery(viewer, src);
|
|
70
89
|
}
|
|
@@ -257,27 +276,15 @@ function getCompleteClause(id) {
|
|
|
257
276
|
return clause.And(clause.Eq("user_id", id), (0, exports.getNextWeekClause)());
|
|
258
277
|
}
|
|
259
278
|
exports.getCompleteClause = getCompleteClause;
|
|
260
|
-
exports.userToEventsInNextWeekCountLoaderFactory = new raw_count_loader_1.RawCountLoaderFactory({
|
|
261
|
-
...internal_1.FakeEvent.loaderOptions(),
|
|
262
|
-
groupCol: "user_id",
|
|
263
|
-
clause: (0, exports.getNextWeekClause)(),
|
|
264
|
-
});
|
|
265
|
-
exports.userToEventsInNextWeekDataLoaderFactory = new query_loader_1.QueryLoaderFactory({
|
|
266
|
-
...internal_1.FakeEvent.loaderOptions(),
|
|
267
|
-
groupCol: "user_id",
|
|
268
|
-
clause: (0, exports.getNextWeekClause)(),
|
|
269
|
-
toPrime: [fake_contact_1.contactLoader],
|
|
270
|
-
sortColumn: "start_time",
|
|
271
|
-
});
|
|
272
279
|
class UserToEventsInNextWeekQuery extends custom_query_1.CustomEdgeQueryBase {
|
|
273
280
|
constructor(viewer, src) {
|
|
274
281
|
super(viewer, {
|
|
275
282
|
src,
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
283
|
+
...internal_1.FakeEvent.loaderOptions(),
|
|
284
|
+
groupCol: "user_id",
|
|
285
|
+
clause: (0, exports.getNextWeekClause)(),
|
|
286
|
+
loadEntOptions: internal_1.FakeEvent.loaderOptions(),
|
|
287
|
+
name: "events_in_next_week",
|
|
281
288
|
sortColumn: "start_time",
|
|
282
289
|
});
|
|
283
290
|
}
|