@snowtop/ent 0.1.0-alpha152 → 0.1.0-alpha154

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.
Files changed (46) hide show
  1. package/action/index.d.ts +1 -0
  2. package/action/operations.d.ts +3 -2
  3. package/action/operations.js +8 -6
  4. package/action/orchestrator.d.ts +5 -0
  5. package/action/orchestrator.js +37 -5
  6. package/core/base.d.ts +5 -3
  7. package/core/context.d.ts +2 -1
  8. package/core/context.js +2 -1
  9. package/core/ent.d.ts +3 -3
  10. package/core/ent.js +10 -4
  11. package/core/loaders/assoc_count_loader.d.ts +4 -2
  12. package/core/loaders/assoc_count_loader.js +10 -2
  13. package/core/loaders/assoc_edge_loader.d.ts +2 -3
  14. package/core/loaders/assoc_edge_loader.js +14 -4
  15. package/core/loaders/index_loader.d.ts +2 -1
  16. package/core/loaders/index_loader.js +1 -1
  17. package/core/loaders/query_loader.d.ts +3 -3
  18. package/core/loaders/query_loader.js +11 -16
  19. package/core/privacy.d.ts +19 -10
  20. package/core/privacy.js +33 -21
  21. package/core/query/assoc_query.js +1 -1
  22. package/core/query/custom_clause_query.d.ts +4 -2
  23. package/core/query/custom_clause_query.js +32 -15
  24. package/core/query/custom_query.d.ts +3 -1
  25. package/core/query/custom_query.js +28 -5
  26. package/core/query/query.d.ts +6 -4
  27. package/core/query/query.js +51 -46
  28. package/core/query/shared_assoc_test.js +142 -1
  29. package/core/query/shared_test.d.ts +2 -2
  30. package/core/query/shared_test.js +26 -29
  31. package/core/query_impl.d.ts +8 -0
  32. package/core/query_impl.js +28 -0
  33. package/graphql/index.d.ts +1 -1
  34. package/graphql/index.js +3 -1
  35. package/graphql/scalars/orderby_direction.js +2 -2
  36. package/index.d.ts +1 -0
  37. package/index.js +1 -0
  38. package/package.json +1 -1
  39. package/parse_schema/parse.d.ts +1 -0
  40. package/parse_schema/parse.js +1 -0
  41. package/testutils/fake_data/fake_user.js +15 -4
  42. package/testutils/fake_data/tag_query.js +8 -3
  43. package/testutils/fake_data/test_helpers.d.ts +3 -2
  44. package/testutils/fake_data/test_helpers.js +3 -3
  45. package/testutils/fake_data/user_query.d.ts +5 -2
  46. 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
- constructor(id1: ID, id2: ID, edgeType: string);
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
- constructor(edgeType: string);
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
- constructor(edgeType: string);
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
- constructor(id1: ID, id2: ID, edgeType: string);
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
- constructor(edgeType: string);
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
- constructor(edgeType: string);
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
- constructor(id1: ID, id2: ID, edgeType: string);
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
- constructor(edgeType: string);
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
- constructor(edgeType: string);
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
- .createLoader(this.viewer.context)
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
- sortColumnUnique?: boolean;
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 sortCol = options.sortColumn || "id";
25
- let unique = options.sortColumnUnique
26
- ? sortCol
27
- : options.loadEntOptions.loaderFactory.options?.key || "id";
28
- // pass direction to base class since it uses it
29
- // this API needs to be cleaned up...
30
- if (options.orderByDirection) {
31
- sortCol = `${sortCol} ${options.orderByDirection}`;
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
- sortCol,
35
- cursorCol: unique,
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
- const direction = this.options.orderByDirection ?? "desc";
66
- options.orderby = `${this.options.sortColumn} ${direction}`;
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: (0, query_loader_1.getOrderBy)(this.getSortCol(), options?.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
- sortColumnUnique?: boolean;
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.sortColumnUnique) {
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 = options.sortColumn || defaultSort;
84
+ uniqueCol = sortCol || defaultSort;
69
85
  }
70
- options.sortColumn = options.sortColumn || defaultSort;
71
- super(viewer, options.sortColumn, uniqueCol);
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 = `${this.getSortCol()} DESC`;
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)();
@@ -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
- sortCol: string;
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
  }
@@ -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
- let nullsPlacement = "";
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 unique col in same direction since it doesn't matter...
113
- // nulls placement only affects sortCol. assumption is cursorCol will not be null and no need for that
114
- options.orderby = `${this.sortCol} ${orderby}${nullsPlacement}, ${this.options.cursorCol} ${orderby}`;
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
- // assume desc by default
179
- // so last is reverse
180
- let orderby = "ASC";
181
- if (this.options.defaultDirection) {
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
- options.orderby = `${this.sortCol} ${orderby}, ${this.options.cursorCol} ${orderby}`;
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 await this.memoizedloadEdges();
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 await this.loadEntsFromEdges("id", edges);
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
- sortCol,
272
+ orderby: [
273
+ {
274
+ column: sortCol,
275
+ direction: "DESC",
276
+ },
277
+ ],
283
278
  };
284
279
  }
285
280
  else {
286
- sortCol = sortColOrOptions.sortCol;
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
- // @ts-ignore
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 = m2[1];
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
- defaultDirection: this.defaultDirection,
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
- defaultDirection: this.defaultDirection,
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