@snowtop/ent 0.1.0-alpha152 → 0.1.0-alpha153
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/index.d.ts +1 -0
- package/action/operations.d.ts +3 -2
- package/action/operations.js +8 -6
- package/action/orchestrator.d.ts +5 -0
- package/action/orchestrator.js +37 -5
- package/core/base.d.ts +5 -3
- package/core/context.d.ts +2 -1
- package/core/context.js +2 -1
- package/core/ent.d.ts +3 -3
- package/core/ent.js +10 -4
- package/core/loaders/assoc_count_loader.d.ts +4 -2
- package/core/loaders/assoc_count_loader.js +10 -2
- package/core/loaders/assoc_edge_loader.d.ts +2 -3
- package/core/loaders/assoc_edge_loader.js +14 -4
- package/core/loaders/index_loader.d.ts +2 -1
- package/core/loaders/index_loader.js +1 -1
- package/core/loaders/query_loader.d.ts +3 -3
- package/core/loaders/query_loader.js +11 -16
- package/core/privacy.d.ts +19 -10
- package/core/privacy.js +33 -21
- package/core/query/assoc_query.js +1 -1
- package/core/query/custom_clause_query.d.ts +4 -2
- package/core/query/custom_clause_query.js +32 -15
- package/core/query/custom_query.d.ts +3 -1
- package/core/query/custom_query.js +28 -5
- package/core/query/query.d.ts +6 -4
- package/core/query/query.js +51 -46
- package/core/query/shared_assoc_test.js +142 -1
- package/core/query/shared_test.d.ts +2 -2
- package/core/query/shared_test.js +26 -29
- package/core/query_impl.d.ts +8 -0
- package/core/query_impl.js +28 -0
- package/graphql/index.d.ts +1 -1
- package/graphql/index.js +3 -1
- package/graphql/scalars/orderby_direction.js +2 -2
- package/package.json +1 -1
- package/parse_schema/parse.d.ts +1 -0
- package/parse_schema/parse.js +1 -0
- package/testutils/fake_data/fake_user.js +15 -4
- package/testutils/fake_data/tag_query.js +8 -3
- package/testutils/fake_data/test_helpers.d.ts +3 -2
- package/testutils/fake_data/test_helpers.js +3 -3
- package/testutils/fake_data/user_query.d.ts +5 -2
- package/testutils/fake_data/user_query.js +19 -2
package/core/privacy.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ent, ID, LoadEntOptions, PrivacyError, PrivacyPolicy, PrivacyPolicyRule, PrivacyResult, Viewer } from "./base";
|
|
1
|
+
import { Ent, ID, LoadEntOptions, PrivacyError, PrivacyPolicy, PrivacyPolicyRule, PrivacyResult, Viewer, EdgeQueryableDataOptionsConfigureLoader } from "./base";
|
|
2
2
|
export declare class EntPrivacyError extends Error implements PrivacyError {
|
|
3
3
|
privacyPolicy: PrivacyPolicy;
|
|
4
4
|
privacyRule: PrivacyPolicyRule;
|
|
@@ -115,51 +115,60 @@ export declare class AllowIfEdgeExistsRule implements PrivacyPolicyRule {
|
|
|
115
115
|
private id1;
|
|
116
116
|
private id2;
|
|
117
117
|
private edgeType;
|
|
118
|
-
|
|
118
|
+
private options?;
|
|
119
|
+
constructor(id1: ID, id2: ID, edgeType: string, options?: EdgeQueryableDataOptionsConfigureLoader | undefined);
|
|
119
120
|
apply(v: Viewer, _ent?: Ent): Promise<PrivacyResult>;
|
|
120
121
|
}
|
|
121
122
|
export declare class AllowIfViewerInboundEdgeExistsRule implements PrivacyPolicyRule {
|
|
122
123
|
private edgeType;
|
|
123
|
-
|
|
124
|
+
private options?;
|
|
125
|
+
constructor(edgeType: string, options?: EdgeQueryableDataOptionsConfigureLoader | undefined);
|
|
124
126
|
apply(v: Viewer, ent?: Ent): Promise<PrivacyResult>;
|
|
125
127
|
}
|
|
126
128
|
export declare class AllowIfViewerOutboundEdgeExistsRule implements PrivacyPolicyRule {
|
|
127
129
|
private edgeType;
|
|
128
|
-
|
|
130
|
+
private options?;
|
|
131
|
+
constructor(edgeType: string, options?: EdgeQueryableDataOptionsConfigureLoader | undefined);
|
|
129
132
|
apply(v: Viewer, ent?: Ent): Promise<PrivacyResult>;
|
|
130
133
|
}
|
|
131
134
|
export declare class DenyIfEdgeExistsRule implements PrivacyPolicyRule {
|
|
132
135
|
private id1;
|
|
133
136
|
private id2;
|
|
134
137
|
private edgeType;
|
|
135
|
-
|
|
138
|
+
private options?;
|
|
139
|
+
constructor(id1: ID, id2: ID, edgeType: string, options?: EdgeQueryableDataOptionsConfigureLoader | undefined);
|
|
136
140
|
apply(v: Viewer, _ent?: Ent): Promise<PrivacyResult>;
|
|
137
141
|
}
|
|
138
142
|
export declare class DenyIfViewerInboundEdgeExistsRule implements PrivacyPolicyRule {
|
|
139
143
|
private edgeType;
|
|
140
|
-
|
|
144
|
+
private options?;
|
|
145
|
+
constructor(edgeType: string, options?: EdgeQueryableDataOptionsConfigureLoader | undefined);
|
|
141
146
|
apply(v: Viewer, ent?: Ent): Promise<PrivacyResult>;
|
|
142
147
|
}
|
|
143
148
|
export declare class DenyIfViewerOutboundEdgeExistsRule implements PrivacyPolicyRule {
|
|
144
149
|
private edgeType;
|
|
145
|
-
|
|
150
|
+
private options?;
|
|
151
|
+
constructor(edgeType: string, options?: EdgeQueryableDataOptionsConfigureLoader | undefined);
|
|
146
152
|
apply(v: Viewer, ent?: Ent): Promise<PrivacyResult>;
|
|
147
153
|
}
|
|
148
154
|
export declare class DenyIfEdgeDoesNotExistRule implements PrivacyPolicyRule {
|
|
149
155
|
private id1;
|
|
150
156
|
private id2;
|
|
151
157
|
private edgeType;
|
|
152
|
-
|
|
158
|
+
private options?;
|
|
159
|
+
constructor(id1: ID, id2: ID, edgeType: string, options?: EdgeQueryableDataOptionsConfigureLoader | undefined);
|
|
153
160
|
apply(v: Viewer, _ent?: Ent): Promise<PrivacyResult>;
|
|
154
161
|
}
|
|
155
162
|
export declare class DenyIfViewerInboundEdgeDoesNotExistRule implements PrivacyPolicyRule {
|
|
156
163
|
private edgeType;
|
|
157
|
-
|
|
164
|
+
private options?;
|
|
165
|
+
constructor(edgeType: string, options?: EdgeQueryableDataOptionsConfigureLoader | undefined);
|
|
158
166
|
apply(v: Viewer, ent?: Ent): Promise<PrivacyResult>;
|
|
159
167
|
}
|
|
160
168
|
export declare class DenyIfViewerOutboundEdgeDoesNotExistRule implements PrivacyPolicyRule {
|
|
161
169
|
private edgeType;
|
|
162
|
-
|
|
170
|
+
private options?;
|
|
171
|
+
constructor(edgeType: string, options?: EdgeQueryableDataOptionsConfigureLoader | undefined);
|
|
163
172
|
apply(v: Viewer, ent?: Ent): Promise<PrivacyResult>;
|
|
164
173
|
}
|
|
165
174
|
export declare class AllowIfConditionAppliesRule implements PrivacyPolicyRule {
|
package/core/privacy.js
CHANGED
|
@@ -255,7 +255,7 @@ class DenyIfEntIsNotVisibleRule {
|
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
257
|
exports.DenyIfEntIsNotVisibleRule = DenyIfEntIsNotVisibleRule;
|
|
258
|
-
async function allowIfEdgeExistsRule(id1, id2, edgeType, context) {
|
|
258
|
+
async function allowIfEdgeExistsRule(id1, id2, edgeType, context, options) {
|
|
259
259
|
if (id1 && id2) {
|
|
260
260
|
const edge = await (0, ent_1.loadEdgeForID2)({
|
|
261
261
|
id1,
|
|
@@ -263,6 +263,7 @@ async function allowIfEdgeExistsRule(id1, id2, edgeType, context) {
|
|
|
263
263
|
id2,
|
|
264
264
|
context,
|
|
265
265
|
ctr: ent_1.AssocEdge,
|
|
266
|
+
queryOptions: options,
|
|
266
267
|
});
|
|
267
268
|
if (edge) {
|
|
268
269
|
return (0, base_1.Allow)();
|
|
@@ -271,35 +272,38 @@ async function allowIfEdgeExistsRule(id1, id2, edgeType, context) {
|
|
|
271
272
|
return (0, base_1.Skip)();
|
|
272
273
|
}
|
|
273
274
|
class AllowIfEdgeExistsRule {
|
|
274
|
-
constructor(id1, id2, edgeType) {
|
|
275
|
+
constructor(id1, id2, edgeType, options) {
|
|
275
276
|
this.id1 = id1;
|
|
276
277
|
this.id2 = id2;
|
|
277
278
|
this.edgeType = edgeType;
|
|
279
|
+
this.options = options;
|
|
278
280
|
}
|
|
279
281
|
async apply(v, _ent) {
|
|
280
|
-
return allowIfEdgeExistsRule(this.id1, this.id2, this.edgeType, v.context);
|
|
282
|
+
return allowIfEdgeExistsRule(this.id1, this.id2, this.edgeType, v.context, this.options);
|
|
281
283
|
}
|
|
282
284
|
}
|
|
283
285
|
exports.AllowIfEdgeExistsRule = AllowIfEdgeExistsRule;
|
|
284
286
|
class AllowIfViewerInboundEdgeExistsRule {
|
|
285
|
-
constructor(edgeType) {
|
|
287
|
+
constructor(edgeType, options) {
|
|
286
288
|
this.edgeType = edgeType;
|
|
289
|
+
this.options = options;
|
|
287
290
|
}
|
|
288
291
|
async apply(v, ent) {
|
|
289
|
-
return allowIfEdgeExistsRule(v.viewerID, ent?.id, this.edgeType, v.context);
|
|
292
|
+
return allowIfEdgeExistsRule(v.viewerID, ent?.id, this.edgeType, v.context, this.options);
|
|
290
293
|
}
|
|
291
294
|
}
|
|
292
295
|
exports.AllowIfViewerInboundEdgeExistsRule = AllowIfViewerInboundEdgeExistsRule;
|
|
293
296
|
class AllowIfViewerOutboundEdgeExistsRule {
|
|
294
|
-
constructor(edgeType) {
|
|
297
|
+
constructor(edgeType, options) {
|
|
295
298
|
this.edgeType = edgeType;
|
|
299
|
+
this.options = options;
|
|
296
300
|
}
|
|
297
301
|
async apply(v, ent) {
|
|
298
|
-
return allowIfEdgeExistsRule(ent?.id, v.viewerID, this.edgeType, v.context);
|
|
302
|
+
return allowIfEdgeExistsRule(ent?.id, v.viewerID, this.edgeType, v.context, this.options);
|
|
299
303
|
}
|
|
300
304
|
}
|
|
301
305
|
exports.AllowIfViewerOutboundEdgeExistsRule = AllowIfViewerOutboundEdgeExistsRule;
|
|
302
|
-
async function denyIfEdgeExistsRule(id1, id2, edgeType, context) {
|
|
306
|
+
async function denyIfEdgeExistsRule(id1, id2, edgeType, context, options) {
|
|
303
307
|
// edge doesn't exist if no viewer
|
|
304
308
|
if (id1 && id2) {
|
|
305
309
|
const edge = await (0, ent_1.loadEdgeForID2)({
|
|
@@ -308,6 +312,7 @@ async function denyIfEdgeExistsRule(id1, id2, edgeType, context) {
|
|
|
308
312
|
id2,
|
|
309
313
|
context,
|
|
310
314
|
ctr: ent_1.AssocEdge,
|
|
315
|
+
queryOptions: options,
|
|
311
316
|
});
|
|
312
317
|
if (edge) {
|
|
313
318
|
return (0, base_1.Deny)();
|
|
@@ -315,7 +320,7 @@ async function denyIfEdgeExistsRule(id1, id2, edgeType, context) {
|
|
|
315
320
|
}
|
|
316
321
|
return (0, base_1.Skip)();
|
|
317
322
|
}
|
|
318
|
-
async function denyIfEdgeDoesNotExistRule(id1, id2, edgeType, context) {
|
|
323
|
+
async function denyIfEdgeDoesNotExistRule(id1, id2, edgeType, context, options) {
|
|
319
324
|
// edge doesn't exist if no viewer
|
|
320
325
|
if (!id1 || !id2) {
|
|
321
326
|
return (0, base_1.Deny)();
|
|
@@ -326,6 +331,7 @@ async function denyIfEdgeDoesNotExistRule(id1, id2, edgeType, context) {
|
|
|
326
331
|
id2,
|
|
327
332
|
context,
|
|
328
333
|
ctr: ent_1.AssocEdge,
|
|
334
|
+
queryOptions: options,
|
|
329
335
|
});
|
|
330
336
|
if (!edge) {
|
|
331
337
|
return (0, base_1.Deny)();
|
|
@@ -333,60 +339,66 @@ async function denyIfEdgeDoesNotExistRule(id1, id2, edgeType, context) {
|
|
|
333
339
|
return (0, base_1.Skip)();
|
|
334
340
|
}
|
|
335
341
|
class DenyIfEdgeExistsRule {
|
|
336
|
-
constructor(id1, id2, edgeType) {
|
|
342
|
+
constructor(id1, id2, edgeType, options) {
|
|
337
343
|
this.id1 = id1;
|
|
338
344
|
this.id2 = id2;
|
|
339
345
|
this.edgeType = edgeType;
|
|
346
|
+
this.options = options;
|
|
340
347
|
}
|
|
341
348
|
async apply(v, _ent) {
|
|
342
|
-
return denyIfEdgeExistsRule(this.id1, this.id2, this.edgeType, v.context);
|
|
349
|
+
return denyIfEdgeExistsRule(this.id1, this.id2, this.edgeType, v.context, this.options);
|
|
343
350
|
}
|
|
344
351
|
}
|
|
345
352
|
exports.DenyIfEdgeExistsRule = DenyIfEdgeExistsRule;
|
|
346
353
|
class DenyIfViewerInboundEdgeExistsRule {
|
|
347
|
-
constructor(edgeType) {
|
|
354
|
+
constructor(edgeType, options) {
|
|
348
355
|
this.edgeType = edgeType;
|
|
356
|
+
this.options = options;
|
|
349
357
|
}
|
|
350
358
|
async apply(v, ent) {
|
|
351
|
-
return denyIfEdgeExistsRule(v.viewerID, ent?.id, this.edgeType, v.context);
|
|
359
|
+
return denyIfEdgeExistsRule(v.viewerID, ent?.id, this.edgeType, v.context, this.options);
|
|
352
360
|
}
|
|
353
361
|
}
|
|
354
362
|
exports.DenyIfViewerInboundEdgeExistsRule = DenyIfViewerInboundEdgeExistsRule;
|
|
355
363
|
class DenyIfViewerOutboundEdgeExistsRule {
|
|
356
|
-
constructor(edgeType) {
|
|
364
|
+
constructor(edgeType, options) {
|
|
357
365
|
this.edgeType = edgeType;
|
|
366
|
+
this.options = options;
|
|
358
367
|
}
|
|
359
368
|
async apply(v, ent) {
|
|
360
|
-
return denyIfEdgeExistsRule(ent?.id, v.viewerID, this.edgeType, v.context);
|
|
369
|
+
return denyIfEdgeExistsRule(ent?.id, v.viewerID, this.edgeType, v.context, this.options);
|
|
361
370
|
}
|
|
362
371
|
}
|
|
363
372
|
exports.DenyIfViewerOutboundEdgeExistsRule = DenyIfViewerOutboundEdgeExistsRule;
|
|
364
373
|
class DenyIfEdgeDoesNotExistRule {
|
|
365
|
-
constructor(id1, id2, edgeType) {
|
|
374
|
+
constructor(id1, id2, edgeType, options) {
|
|
366
375
|
this.id1 = id1;
|
|
367
376
|
this.id2 = id2;
|
|
368
377
|
this.edgeType = edgeType;
|
|
378
|
+
this.options = options;
|
|
369
379
|
}
|
|
370
380
|
async apply(v, _ent) {
|
|
371
|
-
return denyIfEdgeDoesNotExistRule(this.id1, this.id2, this.edgeType, v.context);
|
|
381
|
+
return denyIfEdgeDoesNotExistRule(this.id1, this.id2, this.edgeType, v.context, this.options);
|
|
372
382
|
}
|
|
373
383
|
}
|
|
374
384
|
exports.DenyIfEdgeDoesNotExistRule = DenyIfEdgeDoesNotExistRule;
|
|
375
385
|
class DenyIfViewerInboundEdgeDoesNotExistRule {
|
|
376
|
-
constructor(edgeType) {
|
|
386
|
+
constructor(edgeType, options) {
|
|
377
387
|
this.edgeType = edgeType;
|
|
388
|
+
this.options = options;
|
|
378
389
|
}
|
|
379
390
|
async apply(v, ent) {
|
|
380
|
-
return denyIfEdgeDoesNotExistRule(v.viewerID, ent?.id, this.edgeType, v.context);
|
|
391
|
+
return denyIfEdgeDoesNotExistRule(v.viewerID, ent?.id, this.edgeType, v.context, this.options);
|
|
381
392
|
}
|
|
382
393
|
}
|
|
383
394
|
exports.DenyIfViewerInboundEdgeDoesNotExistRule = DenyIfViewerInboundEdgeDoesNotExistRule;
|
|
384
395
|
class DenyIfViewerOutboundEdgeDoesNotExistRule {
|
|
385
|
-
constructor(edgeType) {
|
|
396
|
+
constructor(edgeType, options) {
|
|
386
397
|
this.edgeType = edgeType;
|
|
398
|
+
this.options = options;
|
|
387
399
|
}
|
|
388
400
|
async apply(v, ent) {
|
|
389
|
-
return denyIfEdgeDoesNotExistRule(ent?.id, v.viewerID, this.edgeType, v.context);
|
|
401
|
+
return denyIfEdgeDoesNotExistRule(ent?.id, v.viewerID, this.edgeType, v.context, this.options);
|
|
390
402
|
}
|
|
391
403
|
}
|
|
392
404
|
exports.DenyIfViewerOutboundEdgeDoesNotExistRule = DenyIfViewerOutboundEdgeDoesNotExistRule;
|
|
@@ -43,7 +43,7 @@ class AssocEdgeQueryBase extends query_1.BaseEdgeQuery {
|
|
|
43
43
|
return 0;
|
|
44
44
|
}
|
|
45
45
|
return this.countLoaderFactory
|
|
46
|
-
.
|
|
46
|
+
.createConfigurableLoader(this.getDefaultEdgeQueryOptions() ?? {}, this.viewer.context)
|
|
47
47
|
.load(info.id);
|
|
48
48
|
}
|
|
49
49
|
async queryAllRawCount() {
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { Data, EdgeQueryableDataOptions, Ent, ID, LoadEntOptions, Viewer } from "../base";
|
|
2
2
|
import { Clause } from "../clause";
|
|
3
|
+
import { OrderBy } from "../query_impl";
|
|
3
4
|
import { BaseEdgeQuery, IDInfo } from "./query";
|
|
4
5
|
export interface CustomClauseQueryOptions<TDest extends Ent<TViewer>, TViewer extends Viewer = Viewer> {
|
|
5
6
|
loadEntOptions: LoadEntOptions<TDest, TViewer>;
|
|
6
7
|
clause: Clause;
|
|
7
8
|
name: string;
|
|
9
|
+
primarySortColIsUnique?: boolean;
|
|
10
|
+
orderby?: OrderBy;
|
|
8
11
|
sortColumn?: string;
|
|
9
|
-
|
|
10
|
-
orderByDirection?: "asc" | "desc";
|
|
12
|
+
orderByDirection?: "ASC" | "DESC";
|
|
11
13
|
nullsPlacement?: "first" | "last";
|
|
12
14
|
disableTransformations?: boolean;
|
|
13
15
|
}
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CustomClauseQuery = void 0;
|
|
4
4
|
const clause_1 = require("../clause");
|
|
5
5
|
const ent_1 = require("../ent");
|
|
6
|
-
const query_loader_1 = require("../loaders/query_loader");
|
|
7
6
|
const query_1 = require("./query");
|
|
8
7
|
function getClause(opts) {
|
|
9
8
|
let cls = opts.clause;
|
|
@@ -21,19 +20,32 @@ function getClause(opts) {
|
|
|
21
20
|
}
|
|
22
21
|
class CustomClauseQuery extends query_1.BaseEdgeQuery {
|
|
23
22
|
constructor(viewer, options) {
|
|
24
|
-
let
|
|
25
|
-
let
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (options.
|
|
31
|
-
|
|
23
|
+
let orderby;
|
|
24
|
+
let primarySortCol;
|
|
25
|
+
if (options.orderby &&
|
|
26
|
+
(options.sortColumn || options.orderByDirection || options.nullsPlacement)) {
|
|
27
|
+
throw new Error(`cannot pass orderby and sortColumn|orderByDirection|nullsPlacement`);
|
|
28
|
+
}
|
|
29
|
+
if (options.orderby) {
|
|
30
|
+
primarySortCol = options.orderby[0].column;
|
|
31
|
+
orderby = options.orderby;
|
|
32
32
|
}
|
|
33
|
+
else {
|
|
34
|
+
primarySortCol = options.sortColumn || "id";
|
|
35
|
+
orderby = [
|
|
36
|
+
{
|
|
37
|
+
column: primarySortCol,
|
|
38
|
+
direction: options.orderByDirection ?? "DESC",
|
|
39
|
+
nullsPlacement: options.nullsPlacement,
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
let cursorCol = options.primarySortColIsUnique
|
|
44
|
+
? primarySortCol
|
|
45
|
+
: options.loadEntOptions.loaderFactory.options?.key || "id";
|
|
33
46
|
super(viewer, {
|
|
34
|
-
|
|
35
|
-
cursorCol
|
|
36
|
-
nullsPlacement: options.nullsPlacement,
|
|
47
|
+
orderby,
|
|
48
|
+
cursorCol,
|
|
37
49
|
});
|
|
38
50
|
this.viewer = viewer;
|
|
39
51
|
this.options = options;
|
|
@@ -62,8 +74,13 @@ class CustomClauseQuery extends query_1.BaseEdgeQuery {
|
|
|
62
74
|
async loadRawIDs(_addID) { }
|
|
63
75
|
async loadRawData(_infos, options) {
|
|
64
76
|
if (!options.orderby) {
|
|
65
|
-
|
|
66
|
-
|
|
77
|
+
options.orderby = [
|
|
78
|
+
{
|
|
79
|
+
column: this.getSortCol(),
|
|
80
|
+
direction: this.options.orderByDirection ?? "DESC",
|
|
81
|
+
nullsPlacement: this.options.nullsPlacement,
|
|
82
|
+
},
|
|
83
|
+
];
|
|
67
84
|
}
|
|
68
85
|
if (!options.limit) {
|
|
69
86
|
options.limit = (0, ent_1.getDefaultLimit)();
|
|
@@ -72,7 +89,7 @@ class CustomClauseQuery extends query_1.BaseEdgeQuery {
|
|
|
72
89
|
tableName: this.options.loadEntOptions.tableName,
|
|
73
90
|
fields: this.options.loadEntOptions.fields,
|
|
74
91
|
clause: (0, clause_1.AndOptional)(this.clause, options.clause),
|
|
75
|
-
orderby:
|
|
92
|
+
orderby: options.orderby,
|
|
76
93
|
limit: options?.limit || (0, ent_1.getDefaultLimit)(),
|
|
77
94
|
context: this.viewer.context,
|
|
78
95
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Data, Ent, ID, EdgeQueryableDataOptions, LoadEntOptions, Viewer, LoaderFactory, ConfigurableLoaderFactory } from "../base";
|
|
2
2
|
import { Clause } from "../clause";
|
|
3
|
+
import { OrderBy } from "../query_impl";
|
|
3
4
|
import { BaseEdgeQuery, IDInfo, EdgeQuery } from "./query";
|
|
4
5
|
export interface CustomEdgeQueryOptionsDeprecated<TSource extends Ent<TViewer>, TDest extends Ent<TViewer>, TViewer extends Viewer = Viewer> {
|
|
5
6
|
src: TSource | ID;
|
|
@@ -15,7 +16,8 @@ export interface CustomEdgeQueryOptions<TSource extends Ent<TViewer>, TDest exte
|
|
|
15
16
|
clause?: Clause;
|
|
16
17
|
name: string;
|
|
17
18
|
sortColumn?: string;
|
|
18
|
-
|
|
19
|
+
orderby?: OrderBy;
|
|
20
|
+
primarySortColIsUnique?: boolean;
|
|
19
21
|
disableTransformations?: boolean;
|
|
20
22
|
}
|
|
21
23
|
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> {
|
|
@@ -54,21 +54,39 @@ class CustomEdgeQueryBase extends query_1.BaseEdgeQuery {
|
|
|
54
54
|
let opts;
|
|
55
55
|
let defaultSort = "id";
|
|
56
56
|
let uniqueColIsSort = false;
|
|
57
|
+
let orderby;
|
|
58
|
+
let sortCol;
|
|
57
59
|
if (isDeprecatedOptions(options)) {
|
|
58
60
|
opts = options.options;
|
|
59
61
|
}
|
|
60
62
|
else {
|
|
61
63
|
opts = options.loadEntOptions;
|
|
62
|
-
if (options.
|
|
64
|
+
if (options.primarySortColIsUnique) {
|
|
63
65
|
uniqueColIsSort = true;
|
|
64
66
|
}
|
|
67
|
+
if (options.orderby) {
|
|
68
|
+
orderby = options.orderby;
|
|
69
|
+
sortCol = options.orderby[0].column;
|
|
70
|
+
}
|
|
65
71
|
}
|
|
66
72
|
let uniqueCol = opts.loaderFactory.options?.key || "id";
|
|
73
|
+
if (!orderby) {
|
|
74
|
+
options.sortColumn = options.sortColumn || defaultSort;
|
|
75
|
+
sortCol = options.sortColumn;
|
|
76
|
+
orderby = [
|
|
77
|
+
{
|
|
78
|
+
column: options.sortColumn,
|
|
79
|
+
direction: "DESC",
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
}
|
|
67
83
|
if (uniqueColIsSort) {
|
|
68
|
-
uniqueCol =
|
|
84
|
+
uniqueCol = sortCol || defaultSort;
|
|
69
85
|
}
|
|
70
|
-
|
|
71
|
-
|
|
86
|
+
super(viewer, {
|
|
87
|
+
cursorCol: uniqueCol,
|
|
88
|
+
orderby,
|
|
89
|
+
});
|
|
72
90
|
this.viewer = viewer;
|
|
73
91
|
this.options = options;
|
|
74
92
|
if (typeof options.src === "object") {
|
|
@@ -124,7 +142,12 @@ class CustomEdgeQueryBase extends query_1.BaseEdgeQuery {
|
|
|
124
142
|
throw new Error(`expected 1 info passed to loadRawData. ${infos.length} passed`);
|
|
125
143
|
}
|
|
126
144
|
if (!options.orderby) {
|
|
127
|
-
options.orderby =
|
|
145
|
+
options.orderby = [
|
|
146
|
+
{
|
|
147
|
+
column: this.getSortCol(),
|
|
148
|
+
direction: "DESC",
|
|
149
|
+
},
|
|
150
|
+
];
|
|
128
151
|
}
|
|
129
152
|
if (!options.limit) {
|
|
130
153
|
options.limit = (0, ent_1.getDefaultLimit)();
|
package/core/query/query.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ID, Ent, Viewer, EdgeQueryableDataOptions, Data, PrivacyPolicy } from "../base";
|
|
1
|
+
import { ID, Ent, Viewer, EdgeQueryableDataOptions, Data, PrivacyPolicy, EdgeQueryableDataOptionsConfigureLoader } from "../base";
|
|
2
|
+
import { OrderBy } from "../query_impl";
|
|
2
3
|
export interface EdgeQuery<TSource extends Ent, TDest extends Ent, TEdge extends Data> {
|
|
3
4
|
queryEdges(): Promise<TEdge[]>;
|
|
4
5
|
queryAllEdges(): Promise<Map<ID, TEdge[]>>;
|
|
@@ -31,8 +32,7 @@ export interface PaginationInfo {
|
|
|
31
32
|
}
|
|
32
33
|
interface EdgeQueryOptions {
|
|
33
34
|
cursorCol: string;
|
|
34
|
-
|
|
35
|
-
nullsPlacement?: "first" | "last";
|
|
35
|
+
orderby: OrderBy;
|
|
36
36
|
}
|
|
37
37
|
export declare abstract class BaseEdgeQuery<TSource extends Ent, TDest extends Ent, TEdge extends Data> implements EdgeQuery<TSource, TDest, TEdge> {
|
|
38
38
|
viewer: Viewer;
|
|
@@ -46,7 +46,6 @@ export declare abstract class BaseEdgeQuery<TSource extends Ent, TDest extends E
|
|
|
46
46
|
private idsToFetch;
|
|
47
47
|
private sortCol;
|
|
48
48
|
private cursorCol;
|
|
49
|
-
private defaultDirection?;
|
|
50
49
|
private edgeQueryOptions;
|
|
51
50
|
constructor(viewer: Viewer, sortCol: string, cursorCol: string);
|
|
52
51
|
constructor(viewer: Viewer, options: EdgeQueryOptions);
|
|
@@ -76,6 +75,9 @@ export declare abstract class BaseEdgeQuery<TSource extends Ent, TDest extends E
|
|
|
76
75
|
private addID;
|
|
77
76
|
abstract getTableName(): string | Promise<string>;
|
|
78
77
|
protected genIDInfosToFetchImpl(): Promise<IDInfo[]>;
|
|
78
|
+
private _defaultEdgeQueryableOptions;
|
|
79
|
+
protected configureEdgeQueryableDataOptions(opts: EdgeQueryableDataOptionsConfigureLoader): void;
|
|
80
|
+
protected getDefaultEdgeQueryOptions(): Partial<Pick<import("../base").QueryableDataOptions, "clause" | "limit" | "orderby" | "disableTransformations">> | undefined;
|
|
79
81
|
private loadEdges;
|
|
80
82
|
getCursor(row: TEdge): string;
|
|
81
83
|
}
|
package/core/query/query.js
CHANGED
|
@@ -33,6 +33,7 @@ const memoizee_1 = __importDefault(require("memoizee"));
|
|
|
33
33
|
const privacy_1 = require("../privacy");
|
|
34
34
|
const uuid_1 = require("uuid");
|
|
35
35
|
const util_1 = require("util");
|
|
36
|
+
const query_impl_1 = require("../query_impl");
|
|
36
37
|
// TODO can we generalize EdgeQuery to support any clause
|
|
37
38
|
function assertPositive(n) {
|
|
38
39
|
if (n < 0) {
|
|
@@ -95,23 +96,16 @@ class FirstFilter {
|
|
|
95
96
|
// we fetch an extra one to see if we're at the end
|
|
96
97
|
const limit = this.options.limit + 1;
|
|
97
98
|
options.limit = limit;
|
|
98
|
-
let orderby = this.options.defaultDirection || "DESC";
|
|
99
99
|
// we sort by most recent first
|
|
100
100
|
// so when paging, we fetch afterCursor X
|
|
101
|
-
const less = orderby === "DESC";
|
|
102
|
-
|
|
103
|
-
if (this.options.nullsPlacement) {
|
|
104
|
-
if (this.options.nullsPlacement === "first") {
|
|
105
|
-
nullsPlacement = " NULLS FIRST";
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
nullsPlacement = " NULLS LAST";
|
|
109
|
-
}
|
|
110
|
-
}
|
|
101
|
+
const less = this.options.orderby[0].direction === "DESC";
|
|
102
|
+
const orderby = this.options.orderby;
|
|
111
103
|
if (this.options.cursorCol !== this.sortCol) {
|
|
112
|
-
// we also sort
|
|
113
|
-
|
|
114
|
-
|
|
104
|
+
// we also sort cursor col in same direction. (direction doesn't matter)
|
|
105
|
+
orderby.push({
|
|
106
|
+
column: this.options.cursorCol,
|
|
107
|
+
direction: orderby[0].direction,
|
|
108
|
+
});
|
|
115
109
|
if (this.offset) {
|
|
116
110
|
const res = this.edgeQuery.getTableName();
|
|
117
111
|
const tableName = util_1.types.isPromise(res) ? await res : res;
|
|
@@ -120,7 +114,6 @@ class FirstFilter {
|
|
|
120
114
|
}
|
|
121
115
|
}
|
|
122
116
|
else {
|
|
123
|
-
options.orderby = `${this.sortCol} ${orderby}${nullsPlacement}`;
|
|
124
117
|
if (this.offset) {
|
|
125
118
|
let clauseFn = less ? clause.Less : clause.Greater;
|
|
126
119
|
let val = this.options.sortColTime
|
|
@@ -129,6 +122,7 @@ class FirstFilter {
|
|
|
129
122
|
options.clause = clauseFn(this.sortCol, val);
|
|
130
123
|
}
|
|
131
124
|
}
|
|
125
|
+
options.orderby = orderby;
|
|
132
126
|
return options;
|
|
133
127
|
}
|
|
134
128
|
// TODO?
|
|
@@ -136,6 +130,7 @@ class FirstFilter {
|
|
|
136
130
|
return this.pageMap.get(id);
|
|
137
131
|
}
|
|
138
132
|
}
|
|
133
|
+
// TODO need some last tests to test all these cases. clearly don't have the tests
|
|
139
134
|
class LastFilter {
|
|
140
135
|
constructor(options) {
|
|
141
136
|
this.options = options;
|
|
@@ -175,19 +170,10 @@ class LastFilter {
|
|
|
175
170
|
return ret;
|
|
176
171
|
}
|
|
177
172
|
async query(options) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
// reverse sort col shown...
|
|
183
|
-
if (this.options.defaultDirection === "DESC") {
|
|
184
|
-
orderby = "ASC";
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
orderby = "DESC";
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
const greater = orderby === "ASC";
|
|
173
|
+
const orderby = (0, query_impl_1.reverseOrderBy)(this.options.orderby);
|
|
174
|
+
const greater = orderby[0].direction === "ASC";
|
|
175
|
+
// TODO verify that this greater still makes sense. tests pass
|
|
176
|
+
// but wanna confirm
|
|
191
177
|
options.limit = this.options.limit + 1; // fetch an extra so we know if previous pag
|
|
192
178
|
if (this.options.cursorCol !== this.sortCol) {
|
|
193
179
|
const res = this.edgeQuery.getTableName();
|
|
@@ -196,10 +182,13 @@ class LastFilter {
|
|
|
196
182
|
// inner col time
|
|
197
183
|
options.clause = clause.PaginationMultipleColsSubQuery(this.sortCol, greater ? ">" : "<", tableName, this.options.cursorCol, this.offset);
|
|
198
184
|
}
|
|
199
|
-
|
|
185
|
+
// we also sort cursor col in same direction. (direction doesn't matter)
|
|
186
|
+
orderby.push({
|
|
187
|
+
column: this.options.cursorCol,
|
|
188
|
+
direction: orderby[0].direction,
|
|
189
|
+
});
|
|
200
190
|
}
|
|
201
191
|
else {
|
|
202
|
-
options.orderby = `${this.sortCol} ${orderby}`;
|
|
203
192
|
if (this.offset) {
|
|
204
193
|
let clauseFn = greater ? clause.Greater : clause.Less;
|
|
205
194
|
let val = this.options.sortColTime
|
|
@@ -208,6 +197,7 @@ class LastFilter {
|
|
|
208
197
|
options.clause = clauseFn(this.sortCol, val);
|
|
209
198
|
}
|
|
210
199
|
}
|
|
200
|
+
options.orderby = orderby;
|
|
211
201
|
return options;
|
|
212
202
|
}
|
|
213
203
|
paginationInfo(id) {
|
|
@@ -227,7 +217,7 @@ class BaseEdgeQuery {
|
|
|
227
217
|
return await this.querySingleEdge("queryEdges");
|
|
228
218
|
};
|
|
229
219
|
this.queryAllEdges = async () => {
|
|
230
|
-
return
|
|
220
|
+
return this.memoizedloadEdges();
|
|
231
221
|
};
|
|
232
222
|
this.queryIDs = async () => {
|
|
233
223
|
const edges = await this.querySingleEdge("queryIDs");
|
|
@@ -255,7 +245,7 @@ class BaseEdgeQuery {
|
|
|
255
245
|
};
|
|
256
246
|
this.queryEnts = async () => {
|
|
257
247
|
const edges = await this.querySingleEdge("queryEnts");
|
|
258
|
-
return
|
|
248
|
+
return this.loadEntsFromEdges("id", edges);
|
|
259
249
|
};
|
|
260
250
|
this.queryAllEnts = async () => {
|
|
261
251
|
// applies filters and then gets things after
|
|
@@ -279,28 +269,35 @@ class BaseEdgeQuery {
|
|
|
279
269
|
cursorCol = cursorColMaybe;
|
|
280
270
|
this.edgeQueryOptions = {
|
|
281
271
|
cursorCol,
|
|
282
|
-
|
|
272
|
+
orderby: [
|
|
273
|
+
{
|
|
274
|
+
column: sortCol,
|
|
275
|
+
direction: "DESC",
|
|
276
|
+
},
|
|
277
|
+
],
|
|
283
278
|
};
|
|
284
279
|
}
|
|
285
280
|
else {
|
|
286
|
-
|
|
281
|
+
if (typeof sortColOrOptions.orderby === "string") {
|
|
282
|
+
sortCol = sortColOrOptions.orderby;
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
// TODO this orderby isn't consistent and this logic needs to be changed anywhere that's using this and this.getSortCol()
|
|
286
|
+
sortCol = sortColOrOptions.orderby[0].column;
|
|
287
|
+
}
|
|
287
288
|
cursorCol = sortColOrOptions.cursorCol;
|
|
288
289
|
this.edgeQueryOptions = sortColOrOptions;
|
|
289
290
|
}
|
|
291
|
+
this.sortCol = sortCol;
|
|
290
292
|
let m = orderbyRegex.exec(sortCol);
|
|
291
293
|
if (!m) {
|
|
292
294
|
throw new Error(`invalid sort column ${sortCol}`);
|
|
293
295
|
}
|
|
294
296
|
this.sortCol = m[1];
|
|
295
297
|
if (m[2]) {
|
|
296
|
-
|
|
297
|
-
this.defaultDirection = m[2].toUpperCase();
|
|
298
|
-
}
|
|
299
|
-
let m2 = orderbyRegex.exec(cursorCol);
|
|
300
|
-
if (!m2) {
|
|
301
|
-
throw new Error(`invalid sort column ${cursorCol}`);
|
|
298
|
+
throw new Error(`passing direction in sort column is not supproted. use orderby`);
|
|
302
299
|
}
|
|
303
|
-
this.cursorCol =
|
|
300
|
+
this.cursorCol = cursorCol;
|
|
304
301
|
this.memoizedloadEdges = (0, memoizee_1.default)(this.loadEdges.bind(this));
|
|
305
302
|
this.genIDInfosToFetch = (0, memoizee_1.default)(this.genIDInfosToFetchImpl.bind(this));
|
|
306
303
|
}
|
|
@@ -318,8 +315,7 @@ class BaseEdgeQuery {
|
|
|
318
315
|
after,
|
|
319
316
|
sortCol: this.sortCol,
|
|
320
317
|
cursorCol: this.cursorCol,
|
|
321
|
-
|
|
322
|
-
nullsPlacement: this.edgeQueryOptions.nullsPlacement,
|
|
318
|
+
orderby: this.edgeQueryOptions.orderby,
|
|
323
319
|
query: this,
|
|
324
320
|
}));
|
|
325
321
|
return this;
|
|
@@ -331,8 +327,7 @@ class BaseEdgeQuery {
|
|
|
331
327
|
before,
|
|
332
328
|
sortCol: this.sortCol,
|
|
333
329
|
cursorCol: this.cursorCol,
|
|
334
|
-
|
|
335
|
-
nullsPlacement: this.edgeQueryOptions.nullsPlacement,
|
|
330
|
+
orderby: this.edgeQueryOptions.orderby,
|
|
336
331
|
query: this,
|
|
337
332
|
}));
|
|
338
333
|
return this;
|
|
@@ -374,13 +369,23 @@ class BaseEdgeQuery {
|
|
|
374
369
|
await this.loadRawIDs(this.addID.bind(this));
|
|
375
370
|
return applyPrivacyPolicyForEdgeQ(this.viewer, this, this.idsToFetch, this.idMap);
|
|
376
371
|
}
|
|
372
|
+
// FYI: this should be used sparingly.
|
|
373
|
+
// currently only exists so that disableTransformations can be configured by the developer
|
|
374
|
+
// so we're only exposing a partial API for now but maybe in the future we can expose
|
|
375
|
+
// the full API if there's a reason to use this that's not via filters
|
|
376
|
+
configureEdgeQueryableDataOptions(opts) {
|
|
377
|
+
this._defaultEdgeQueryableOptions = opts;
|
|
378
|
+
}
|
|
379
|
+
getDefaultEdgeQueryOptions() {
|
|
380
|
+
return this._defaultEdgeQueryableOptions;
|
|
381
|
+
}
|
|
377
382
|
async loadEdges() {
|
|
378
383
|
const idsInfo = await this.genIDInfosToFetch();
|
|
379
384
|
if (!this.filters.length) {
|
|
380
385
|
// if no filter, we add the firstN filter to ensure we get pagination info
|
|
381
386
|
this.first((0, ent_1.getDefaultLimit)());
|
|
382
387
|
}
|
|
383
|
-
let options = {};
|
|
388
|
+
let options = this._defaultEdgeQueryableOptions ?? {};
|
|
384
389
|
// TODO once we add a lot of complex filters, this needs to be more complicated
|
|
385
390
|
// e.g. commutative filters. what can be done in sql or combined together etc
|
|
386
391
|
// may need to bring sql mode or something back
|