@snowtop/ent 0.0.22 → 0.0.28-alpha

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.
@@ -1,4 +1,4 @@
1
- import { ID, Ent, Viewer, EntConstructor, Context } from "../core/base";
1
+ import { ID, Data, Ent, Viewer, EntConstructor, Context } from "../core/base";
2
2
  import { DataOperation } from "../core/ent";
3
3
  import { Changeset, Executor } from "../action";
4
4
  import { Builder } from "../action";
@@ -11,7 +11,7 @@ export declare class ListBasedExecutor<T extends Ent> implements Executor {
11
11
  private operations;
12
12
  private options?;
13
13
  private idx;
14
- constructor(viewer: Viewer, placeholderID: ID, ent: EntConstructor<T>, operations: DataOperation[], options?: OrchestratorOptions<T> | undefined);
14
+ constructor(viewer: Viewer, placeholderID: ID, ent: EntConstructor<T>, operations: DataOperation[], options?: OrchestratorOptions<T, Data> | undefined);
15
15
  private lastOp;
16
16
  private createdEnt;
17
17
  resolveValue(val: ID): Ent | null;
@@ -34,7 +34,7 @@ export declare class ComplexExecutor<T extends Ent> implements Executor {
34
34
  private changesetMap;
35
35
  private nodeOpMap;
36
36
  private executors;
37
- constructor(viewer: Viewer, placeholderID: ID, ent: EntConstructor<T>, operations: DataOperation[], dependencies: Map<ID, Builder<T>>, changesets: Changeset<T>[], options?: OrchestratorOptions<T> | undefined);
37
+ constructor(viewer: Viewer, placeholderID: ID, ent: EntConstructor<T>, operations: DataOperation[], dependencies: Map<ID, Builder<T>>, changesets: Changeset<T>[], options?: OrchestratorOptions<T, Data> | undefined);
38
38
  [Symbol.iterator](): this;
39
39
  private handleCreatedEnt;
40
40
  next(): IteratorResult<DataOperation>;
@@ -3,7 +3,7 @@ import { AssocEdgeInputOptions, DataOperation } from "../core/ent";
3
3
  import { SchemaInputType } from "../schema/schema";
4
4
  import { Changeset, Executor } from "../action";
5
5
  import { WriteOperation, Builder, Action } from "../action";
6
- export interface OrchestratorOptions<T extends Ent> {
6
+ export interface OrchestratorOptions<T extends Ent, TData extends Data> {
7
7
  viewer: Viewer;
8
8
  operation: WriteOperation;
9
9
  tableName: string;
@@ -13,6 +13,7 @@ export interface OrchestratorOptions<T extends Ent> {
13
13
  action?: Action<T>;
14
14
  schema: SchemaInputType;
15
15
  editedFields(): Map<string, any>;
16
+ updateInput?: (data: TData) => void;
16
17
  }
17
18
  interface edgeInputDataOpts {
18
19
  edgeType: string;
@@ -38,7 +39,9 @@ export declare class Orchestrator<T extends Ent> {
38
39
  private fieldsToResolve;
39
40
  private mainOp;
40
41
  viewer: Viewer;
41
- constructor(options: OrchestratorOptions<T>);
42
+ private defaultFieldsByFieldName;
43
+ private defaultFieldsByTSName;
44
+ constructor(options: OrchestratorOptions<T, Data>);
42
45
  private addEdge;
43
46
  addInboundEdge<T2 extends Ent>(id1: ID | Builder<T2>, edgeType: string, nodeType: string, options?: AssocEdgeInputOptions): void;
44
47
  addOutboundEdge<T2 extends Ent>(id2: ID | Builder<T2>, edgeType: string, nodeType: string, options?: AssocEdgeInputOptions): void;
@@ -50,11 +53,13 @@ export declare class Orchestrator<T extends Ent> {
50
53
  private getEdgeOperation;
51
54
  private buildEdgeOps;
52
55
  private throwError;
56
+ private getEntForPrivacyPolicy;
53
57
  private validate;
54
58
  private triggers;
55
59
  private validators;
56
60
  private isBuilder;
57
- private validateFields;
61
+ private getFieldsWithDefaultValues;
62
+ private formatAndValidateFields;
58
63
  valid(): Promise<boolean>;
59
64
  validX(): Promise<void>;
60
65
  build(): Promise<EntChangeset<T>>;
@@ -71,7 +76,7 @@ export declare class EntChangeset<T extends Ent> implements Changeset<T> {
71
76
  dependencies?: Map<ID, Builder<T>> | undefined;
72
77
  changesets?: Changeset<Ent>[] | undefined;
73
78
  private options?;
74
- constructor(viewer: Viewer, placeholderID: ID, ent: EntConstructor<T>, operations: DataOperation[], dependencies?: Map<ID, Builder<T>> | undefined, changesets?: Changeset<Ent>[] | undefined, options?: OrchestratorOptions<T> | undefined);
79
+ constructor(viewer: Viewer, placeholderID: ID, ent: EntConstructor<T>, operations: DataOperation[], dependencies?: Map<ID, Builder<T>> | undefined, changesets?: Changeset<Ent>[] | undefined, options?: OrchestratorOptions<T, Data> | undefined);
75
80
  executor(): Executor;
76
81
  }
77
82
  export {};
@@ -5,6 +5,7 @@ const ent_1 = require("../core/ent");
5
5
  const schema_1 = require("../schema/schema");
6
6
  const action_1 = require("../action");
7
7
  const snake_case_1 = require("snake-case");
8
+ const camel_case_1 = require("camel-case");
8
9
  const privacy_1 = require("../core/privacy");
9
10
  const executor_1 = require("./executor");
10
11
  const logger_1 = require("../core/logger");
@@ -58,6 +59,8 @@ class Orchestrator {
58
59
  this.changesets = [];
59
60
  this.dependencies = new Map();
60
61
  this.fieldsToResolve = [];
62
+ this.defaultFieldsByFieldName = {};
63
+ this.defaultFieldsByTSName = {};
61
64
  this.viewer = options.viewer;
62
65
  }
63
66
  addEdge(edge, op) {
@@ -222,16 +225,35 @@ class Orchestrator {
222
225
  }
223
226
  return new EntCannotDeleteEntError(privacyPolicy, action, this.options.builder.existingEnt);
224
227
  }
228
+ getEntForPrivacyPolicy(editedData) {
229
+ if (this.options.operation !== action_1.WriteOperation.Insert) {
230
+ return this.options.builder.existingEnt;
231
+ }
232
+ // we create an unsafe ent to be used for privacy policies
233
+ return new this.options.builder.ent(this.options.builder.viewer, editedData);
234
+ }
225
235
  async validate() {
236
+ // existing ent required for edit or delete operations
237
+ switch (this.options.operation) {
238
+ case action_1.WriteOperation.Delete:
239
+ case action_1.WriteOperation.Edit:
240
+ if (!this.options.builder.existingEnt) {
241
+ throw new Error("existing ent required with operation");
242
+ }
243
+ }
226
244
  const action = this.options.action;
227
245
  const builder = this.options.builder;
228
- // this runs in 3 phases:
229
- // * privacy policy
246
+ // future optimization: can get schemaFields to memoize based on different values
247
+ const schemaFields = (0, schema_1.getFields)(this.options.schema);
248
+ let editedData = this.getFieldsWithDefaultValues(builder, schemaFields, action);
249
+ // this runs in following phases:
250
+ // * set default fields and pass to builder so the value can be checked by triggers/observers/validators
251
+ // * privacy policy (use unsafe ent if we have it)
230
252
  // * triggers
231
253
  // * validators
232
254
  let privacyPolicy = action?.getPrivacyPolicy();
233
255
  if (privacyPolicy) {
234
- await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, builder.existingEnt, this.throwError.bind(this));
256
+ await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, this.getEntForPrivacyPolicy(editedData), this.throwError.bind(this));
235
257
  }
236
258
  // have to run triggers which update fields first before field and other validators
237
259
  // so running this first to build things up
@@ -241,7 +263,7 @@ class Orchestrator {
241
263
  }
242
264
  let validators = action?.validators || [];
243
265
  await Promise.all([
244
- this.validateFields(builder, action),
266
+ this.formatAndValidateFields(schemaFields),
245
267
  this.validators(validators, action, builder),
246
268
  ]);
247
269
  }
@@ -276,29 +298,14 @@ class Orchestrator {
276
298
  isBuilder(val) {
277
299
  return val.placeholderID !== undefined;
278
300
  }
279
- async validateFields(builder, action) {
280
- // existing ent required for edit or delete operations
281
- switch (this.options.operation) {
282
- case action_1.WriteOperation.Delete:
283
- case action_1.WriteOperation.Edit:
284
- if (!this.options.builder.existingEnt) {
285
- throw new Error("existing ent required with operation");
286
- }
287
- }
288
- if (this.options.operation == action_1.WriteOperation.Delete) {
289
- return;
290
- }
301
+ getFieldsWithDefaultValues(builder, schemaFields, action) {
291
302
  const editedFields = this.options.editedFields();
292
- // build up data to be saved...
293
303
  let data = {};
294
- let logValues = {};
295
- const schemaFields = (0, schema_1.getFields)(this.options.schema);
296
- let input = {};
297
- if (action !== undefined) {
298
- input = action.getInput();
299
- }
304
+ let input = action?.getInput() || {};
305
+ let updateInput = false;
300
306
  for (const [fieldName, field] of schemaFields) {
301
307
  let value = editedFields.get(fieldName);
308
+ let defaultValue = undefined;
302
309
  let dbKey = field.storageKey || (0, snake_case_1.snakeCase)(field.name);
303
310
  if (value === undefined) {
304
311
  if (this.options.operation === action_1.WriteOperation.Insert) {
@@ -306,18 +313,52 @@ class Orchestrator {
306
313
  throw new Error(`cannot set both defaultToViewerOnCreate and defaultValueOnCreate`);
307
314
  }
308
315
  if (field.defaultToViewerOnCreate) {
309
- value = builder.viewer.viewerID;
316
+ defaultValue = builder.viewer.viewerID;
310
317
  }
311
318
  if (field.defaultValueOnCreate) {
312
- value = field.defaultValueOnCreate(builder, input);
319
+ defaultValue = field.defaultValueOnCreate(builder, input);
320
+ if (defaultValue === undefined) {
321
+ throw new Error(`defaultValueOnCreate() returned undefined for field ${field.name}`);
322
+ }
313
323
  }
314
324
  }
315
325
  if (field.defaultValueOnEdit &&
316
326
  this.options.operation === action_1.WriteOperation.Edit) {
317
- value = field.defaultValueOnEdit(builder, input);
318
- // TODO special case this if this is the onlything changing and don't do the write.
327
+ defaultValue = field.defaultValueOnEdit(builder, input);
328
+ // TODO special case this if this is the only thing changing and don't do the write.
319
329
  }
320
330
  }
331
+ data[dbKey] = value;
332
+ if (defaultValue !== undefined) {
333
+ updateInput = true;
334
+ data[dbKey] = defaultValue;
335
+ this.defaultFieldsByFieldName[fieldName] = defaultValue;
336
+ // TODO related to #510. we need this logic to be consistent so do this all in TypeScript or get it from go somehow
337
+ this.defaultFieldsByTSName[(0, camel_case_1.camelCase)(fieldName)] = defaultValue;
338
+ }
339
+ }
340
+ if (updateInput && this.options.updateInput) {
341
+ // this basically fixes #605. just needs to be exposed correctly
342
+ this.options.updateInput(this.defaultFieldsByTSName);
343
+ }
344
+ return data;
345
+ }
346
+ async formatAndValidateFields(schemaFields) {
347
+ if (this.options.operation == action_1.WriteOperation.Delete) {
348
+ return;
349
+ }
350
+ const editedFields = this.options.editedFields();
351
+ // build up data to be saved...
352
+ let data = {};
353
+ let logValues = {};
354
+ for (const [fieldName, field] of schemaFields) {
355
+ let value = editedFields.get(fieldName);
356
+ if (value === undefined) {
357
+ // null allowed
358
+ value = this.defaultFieldsByFieldName[fieldName];
359
+ }
360
+ let dbKey = field.storageKey || (0, snake_case_1.snakeCase)(field.name);
361
+ // now format and validate...
321
362
  if (value === null) {
322
363
  if (!field.nullable) {
323
364
  throw new Error(`field ${field.name} set to null for non-nullable field`);
@@ -361,7 +402,6 @@ class Orchestrator {
361
402
  }
362
403
  this.validatedFields = data;
363
404
  this.logValues = logValues;
364
- // console.log(this.validatedFields);
365
405
  }
366
406
  async valid() {
367
407
  try {
@@ -389,7 +429,7 @@ class Orchestrator {
389
429
  if (!action || !action.viewerForEntLoad) {
390
430
  return this.options.viewer;
391
431
  }
392
- return await action.viewerForEntLoad(data);
432
+ return action.viewerForEntLoad(data);
393
433
  }
394
434
  async returnedRow() {
395
435
  if (this.mainOp && this.mainOp.returnedEntRow) {
@@ -403,7 +443,7 @@ class Orchestrator {
403
443
  return null;
404
444
  }
405
445
  const viewer = await this.viewerForEntLoad(row);
406
- return await (0, ent_1.applyPrivacyPolicyForRow)(viewer, this.options.loaderOptions, row);
446
+ return (0, ent_1.applyPrivacyPolicyForRow)(viewer, this.options.loaderOptions, row);
407
447
  }
408
448
  async editedEntX() {
409
449
  const row = await this.returnedRow();
package/core/base.js CHANGED
@@ -9,22 +9,25 @@ var privacyResult;
9
9
  privacyResult[privacyResult["Deny"] = 401] = "Deny";
10
10
  privacyResult[privacyResult["Skip"] = 307] = "Skip";
11
11
  })(privacyResult || (privacyResult = {}));
12
+ const allow = {
13
+ result: privacyResult.Allow,
14
+ };
12
15
  function Allow() {
13
- return {
14
- result: privacyResult.Allow,
15
- };
16
+ return allow;
16
17
  }
17
18
  exports.Allow = Allow;
19
+ const skip = {
20
+ result: privacyResult.Skip,
21
+ };
18
22
  function Skip() {
19
- return {
20
- result: privacyResult.Skip,
21
- };
23
+ return skip;
22
24
  }
23
25
  exports.Skip = Skip;
26
+ const deny = {
27
+ result: privacyResult.Deny,
28
+ };
24
29
  function Deny() {
25
- return {
26
- result: privacyResult.Deny,
27
- };
30
+ return deny;
28
31
  }
29
32
  exports.Deny = Deny;
30
33
  function DenyWithReason(e) {
package/core/config.d.ts CHANGED
@@ -15,6 +15,8 @@ interface CodegenConfig {
15
15
  relativeImports?: boolean;
16
16
  disableGraphQLRoot?: boolean;
17
17
  generatedHeader?: string;
18
+ disableBase64Encoding?: boolean;
19
+ generateRootResolvers?: boolean;
18
20
  }
19
21
  interface PrettierConfig {
20
22
  custom?: boolean;
package/core/convert.js CHANGED
@@ -59,7 +59,8 @@ function convertList(val, conv) {
59
59
  }
60
60
  exports.convertList = convertList;
61
61
  function convertNullableList(val, conv) {
62
- if (val === null) {
62
+ // undefined can happen with unsafe ents
63
+ if (val === null || val === undefined) {
63
64
  return null;
64
65
  }
65
66
  return convertList(val, conv);
package/core/privacy.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Viewer, ID, Ent, LoadEntOptions, PrivacyError, PrivacyPolicy, PrivacyPolicyRule, PrivacyResult } from "./base";
1
+ import { Ent, ID, LoadEntOptions, PrivacyError, PrivacyPolicy, PrivacyPolicyRule, PrivacyResult, Viewer } from "./base";
2
2
  export declare class EntPrivacyError extends Error implements PrivacyError {
3
3
  privacyPolicy: PrivacyPolicy;
4
4
  privacyRule: PrivacyPolicyRule;
@@ -46,11 +46,31 @@ export declare class DenyIfFuncRule implements PrivacyPolicyRule {
46
46
  constructor(fn: FuncRule);
47
47
  apply(v: Viewer, ent?: Ent): Promise<PrivacyResult>;
48
48
  }
49
+ /**
50
+ * @deprecated use AllowIfViewerIsEntPropertyRule
51
+ */
49
52
  export declare class AllowIfViewerIsRule implements PrivacyPolicyRule {
50
53
  private property;
51
54
  constructor(property: string);
52
55
  apply(v: Viewer, ent?: Ent): Promise<PrivacyResult>;
53
56
  }
57
+ export declare class AllowIfViewerIsEntPropertyRule<T extends Ent> implements PrivacyPolicyRule {
58
+ private property;
59
+ constructor(property: keyof T);
60
+ apply(v: Viewer, ent?: T): Promise<PrivacyResult>;
61
+ }
62
+ export declare class AllowIfEntPropertyIsRule<T extends Ent> implements PrivacyPolicyRule {
63
+ private property;
64
+ private val;
65
+ constructor(property: keyof T, val: any);
66
+ apply(v: Viewer, ent?: T): Promise<PrivacyResult>;
67
+ }
68
+ export declare class DenyIfEntPropertyIsRule<T extends Ent> implements PrivacyPolicyRule {
69
+ private property;
70
+ private val;
71
+ constructor(property: keyof T, val: any);
72
+ apply(v: Viewer, ent?: T): Promise<PrivacyResult>;
73
+ }
54
74
  export declare class AllowIfEntIsVisibleRule<T extends Ent> implements PrivacyPolicyRule {
55
75
  private id;
56
76
  private options;
package/core/privacy.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AllowIfViewerHasIdentityPrivacyPolicy = exports.AllowIfViewerPrivacyPolicy = exports.AlwaysDenyPrivacyPolicy = exports.AlwaysAllowPrivacyPolicy = exports.applyPrivacyPolicyX = exports.applyPrivacyPolicy = exports.AllowIfSubPolicyAllowsRule = exports.DelayedResultRule = exports.AllowIfConditionAppliesRule = exports.DenyIfViewerOutboundEdgeDoesNotExistRule = exports.DenyIfViewerInboundEdgeDoesNotExistRule = exports.DenyIfEdgeDoesNotExistRule = exports.DenyIfViewerOutboundEdgeExistsRule = exports.DenyIfViewerInboundEdgeExistsRule = exports.DenyIfEdgeExistsRule = exports.AllowIfViewerOutboundEdgeExistsRule = exports.AllowIfViewerInboundEdgeExistsRule = exports.AllowIfEdgeExistsRule = exports.DenyIfEntIsNotVisibleRule = exports.DenyIfEntIsVisibleRule = exports.DenyIfEntIsVisiblePolicy = exports.AllowIfEntIsVisiblePolicy = exports.AllowIfEntIsNotVisibleRule = exports.AllowIfEntIsVisibleRule = exports.AllowIfViewerIsRule = exports.DenyIfFuncRule = exports.AllowIfFuncRule = exports.DenyIfViewerEqualsRule = exports.AllowIfViewerEqualsRule = exports.AllowIfViewerRule = exports.AllowIfHasIdentity = exports.DenyIfLoggedInRule = exports.DenyIfLoggedOutRule = exports.AlwaysDenyRule = exports.AlwaysAllowRule = exports.EntPrivacyError = void 0;
3
+ exports.AllowIfViewerHasIdentityPrivacyPolicy = exports.AllowIfViewerPrivacyPolicy = exports.AlwaysDenyPrivacyPolicy = exports.AlwaysAllowPrivacyPolicy = exports.applyPrivacyPolicyX = exports.applyPrivacyPolicy = exports.AllowIfSubPolicyAllowsRule = exports.DelayedResultRule = exports.AllowIfConditionAppliesRule = exports.DenyIfViewerOutboundEdgeDoesNotExistRule = exports.DenyIfViewerInboundEdgeDoesNotExistRule = exports.DenyIfEdgeDoesNotExistRule = exports.DenyIfViewerOutboundEdgeExistsRule = exports.DenyIfViewerInboundEdgeExistsRule = exports.DenyIfEdgeExistsRule = exports.AllowIfViewerOutboundEdgeExistsRule = exports.AllowIfViewerInboundEdgeExistsRule = exports.AllowIfEdgeExistsRule = exports.DenyIfEntIsNotVisibleRule = exports.DenyIfEntIsVisibleRule = exports.DenyIfEntIsVisiblePolicy = exports.AllowIfEntIsVisiblePolicy = exports.AllowIfEntIsNotVisibleRule = exports.AllowIfEntIsVisibleRule = exports.DenyIfEntPropertyIsRule = exports.AllowIfEntPropertyIsRule = exports.AllowIfViewerIsEntPropertyRule = exports.AllowIfViewerIsRule = exports.DenyIfFuncRule = exports.AllowIfFuncRule = exports.DenyIfViewerEqualsRule = exports.AllowIfViewerEqualsRule = exports.AllowIfViewerRule = exports.AllowIfHasIdentity = exports.DenyIfLoggedInRule = exports.DenyIfLoggedOutRule = exports.AlwaysDenyRule = exports.AlwaysAllowRule = exports.EntPrivacyError = void 0;
4
4
  const base_1 = require("./base");
5
5
  const ent_1 = require("./ent");
6
6
  const logger_1 = require("./logger");
@@ -124,6 +124,9 @@ class DenyIfFuncRule {
124
124
  }
125
125
  }
126
126
  exports.DenyIfFuncRule = DenyIfFuncRule;
127
+ /**
128
+ * @deprecated use AllowIfViewerIsEntPropertyRule
129
+ */
127
130
  class AllowIfViewerIsRule {
128
131
  constructor(property) {
129
132
  this.property = property;
@@ -140,6 +143,47 @@ class AllowIfViewerIsRule {
140
143
  }
141
144
  }
142
145
  exports.AllowIfViewerIsRule = AllowIfViewerIsRule;
146
+ class AllowIfViewerIsEntPropertyRule {
147
+ constructor(property) {
148
+ this.property = property;
149
+ }
150
+ async apply(v, ent) {
151
+ const result = ent && ent[this.property];
152
+ if (result === v.viewerID) {
153
+ return (0, base_1.Allow)();
154
+ }
155
+ return (0, base_1.Skip)();
156
+ }
157
+ }
158
+ exports.AllowIfViewerIsEntPropertyRule = AllowIfViewerIsEntPropertyRule;
159
+ class AllowIfEntPropertyIsRule {
160
+ constructor(property, val) {
161
+ this.property = property;
162
+ this.val = val;
163
+ }
164
+ async apply(v, ent) {
165
+ const result = ent && ent[this.property];
166
+ if (result === this.val) {
167
+ return (0, base_1.Allow)();
168
+ }
169
+ return (0, base_1.Skip)();
170
+ }
171
+ }
172
+ exports.AllowIfEntPropertyIsRule = AllowIfEntPropertyIsRule;
173
+ class DenyIfEntPropertyIsRule {
174
+ constructor(property, val) {
175
+ this.property = property;
176
+ this.val = val;
177
+ }
178
+ async apply(v, ent) {
179
+ const result = ent && ent[this.property];
180
+ if (result === this.val) {
181
+ return (0, base_1.Deny)();
182
+ }
183
+ return (0, base_1.Skip)();
184
+ }
185
+ }
186
+ exports.DenyIfEntPropertyIsRule = DenyIfEntPropertyIsRule;
143
187
  class AllowIfEntIsVisibleRule {
144
188
  constructor(id, options) {
145
189
  this.id = id;
@@ -2,31 +2,29 @@ import { ID, Ent, Viewer, LoadEntOptions, EdgeQueryableDataOptions } from "../ba
2
2
  import { AssocEdge } from "../ent";
3
3
  import { AssocEdgeCountLoaderFactory } from "../loaders/assoc_count_loader";
4
4
  import { AssocEdgeLoaderFactory } from "../loaders/assoc_edge_loader";
5
- import { EdgeQuery, BaseEdgeQuery } from "./query";
6
- export declare type EdgeQuerySource<T extends Ent> = T | T[] | ID | ID[] | EdgeQuery<T, Ent, AssocEdge>;
5
+ import { EdgeQuery, BaseEdgeQuery, IDInfo } from "./query";
6
+ export declare type EdgeQuerySource<TSource extends Ent, TDest extends Ent = Ent> = TSource | TSource[] | ID | ID[] | EdgeQuery<TDest, Ent, AssocEdge>;
7
7
  declare type loaderOptionsFunc = (type: string) => LoadEntOptions<Ent>;
8
- export declare class AssocEdgeQueryBase<TSource extends Ent, TDest extends Ent, TEdge extends AssocEdge> extends BaseEdgeQuery<TDest, TEdge> {
8
+ export declare abstract class AssocEdgeQueryBase<TSource extends Ent, TDest extends Ent, TEdge extends AssocEdge> extends BaseEdgeQuery<TSource, TDest, TEdge> implements EdgeQuery<TSource, TDest, TEdge> {
9
9
  viewer: Viewer;
10
- src: EdgeQuerySource<TSource>;
10
+ src: EdgeQuerySource<TSource, TDest>;
11
11
  private countLoaderFactory;
12
12
  private dataLoaderFactory;
13
13
  private options;
14
- private idsResolved;
15
- private resolvedIDs;
16
- constructor(viewer: Viewer, src: EdgeQuerySource<TSource>, countLoaderFactory: AssocEdgeCountLoaderFactory, dataLoaderFactory: AssocEdgeLoaderFactory<TEdge>, options: LoadEntOptions<TDest> | loaderOptionsFunc);
17
- private resolveIDs;
14
+ constructor(viewer: Viewer, src: EdgeQuerySource<TSource, TDest>, countLoaderFactory: AssocEdgeCountLoaderFactory, dataLoaderFactory: AssocEdgeLoaderFactory<TEdge>, options: LoadEntOptions<TDest> | loaderOptionsFunc);
18
15
  private isEdgeQuery;
19
- private addID;
16
+ abstract sourceEnt(id: ID): Promise<Ent | null>;
20
17
  private getSingleID;
21
18
  queryRawCount(): Promise<number>;
22
19
  queryAllRawCount(): Promise<Map<ID, number>>;
23
20
  protected loadEntsFromEdges(id: ID, edges: AssocEdge[]): Promise<TDest[]>;
24
21
  dataToID(edge: AssocEdge): ID;
25
- protected loadRawData(options: EdgeQueryableDataOptions): Promise<void>;
22
+ protected loadRawIDs(addID: (src: ID | TSource) => void): Promise<void>;
23
+ protected loadRawData(infos: IDInfo[], options: EdgeQueryableDataOptions): Promise<void>;
26
24
  queryID2(id2: ID): Promise<TEdge | undefined>;
27
25
  queryAllID2(id2: ID): Promise<Map<ID, TEdge>>;
28
26
  }
29
- export interface EdgeQueryCtr<T extends Ent, TEdge extends AssocEdge> {
30
- new (viewer: Viewer, src: EdgeQuerySource<T>): EdgeQuery<T, Ent, TEdge>;
27
+ export interface EdgeQueryCtr<TSource extends Ent, TDest extends Ent, TEdge extends AssocEdge> {
28
+ new (viewer: Viewer, src: EdgeQuerySource<TSource>): EdgeQuery<TSource, TDest, TEdge>;
31
29
  }
32
30
  export {};
@@ -14,27 +14,6 @@ class AssocEdgeQueryBase extends query_1.BaseEdgeQuery {
14
14
  this.countLoaderFactory = countLoaderFactory;
15
15
  this.dataLoaderFactory = dataLoaderFactory;
16
16
  this.options = options;
17
- this.resolvedIDs = [];
18
- }
19
- // TODO memoization...
20
- async resolveIDs() {
21
- if (this.idsResolved) {
22
- return this.resolvedIDs;
23
- }
24
- if (Array.isArray(this.src)) {
25
- this.src.forEach((obj) => this.addID(obj));
26
- }
27
- else if (this.isEdgeQuery(this.src)) {
28
- const idsMap = await this.src.queryAllIDs();
29
- for (const [_, ids] of idsMap) {
30
- ids.forEach((id) => this.resolvedIDs.push(id));
31
- }
32
- }
33
- else {
34
- this.addID(this.src);
35
- }
36
- this.idsResolved = true;
37
- return this.resolvedIDs;
38
17
  }
39
18
  isEdgeQuery(obj) {
40
19
  if (obj.queryIDs !== undefined) {
@@ -42,33 +21,34 @@ class AssocEdgeQueryBase extends query_1.BaseEdgeQuery {
42
21
  }
43
22
  return false;
44
23
  }
45
- addID(obj) {
46
- if (typeof obj === "object") {
47
- this.resolvedIDs.push(obj.id);
48
- }
49
- else {
50
- this.resolvedIDs.push(obj);
51
- }
52
- }
53
24
  async getSingleID() {
54
- const ids = await this.resolveIDs();
55
- if (ids.length !== 1) {
25
+ const infos = await this.genIDInfosToFetch();
26
+ if (infos.length !== 1) {
56
27
  throw new Error("cannot call queryRawCount when more than one id is requested");
57
28
  }
58
- return ids[0];
29
+ return infos[0];
59
30
  }
60
31
  // doesn't work with filters...
61
32
  async queryRawCount() {
62
- const id = await this.getSingleID();
63
- return this.countLoaderFactory.createLoader(this.viewer.context).load(id);
33
+ const info = await this.getSingleID();
34
+ if (info.invalidated) {
35
+ return 0;
36
+ }
37
+ return this.countLoaderFactory
38
+ .createLoader(this.viewer.context)
39
+ .load(info.id);
64
40
  }
65
41
  async queryAllRawCount() {
66
42
  let results = new Map();
67
- const ids = await this.resolveIDs();
43
+ const infos = await this.genIDInfosToFetch();
68
44
  const loader = this.countLoaderFactory.createLoader(this.viewer.context);
69
- await Promise.all(ids.map(async (id) => {
70
- const count = await loader.load(id);
71
- results.set(id, count);
45
+ await Promise.all(infos.map(async (info) => {
46
+ if (info.invalidated) {
47
+ results.set(info.id, 0);
48
+ return;
49
+ }
50
+ const count = await loader.load(info.id);
51
+ results.set(info.id, count);
72
52
  }));
73
53
  return results;
74
54
  }
@@ -108,33 +88,56 @@ class AssocEdgeQueryBase extends query_1.BaseEdgeQuery {
108
88
  dataToID(edge) {
109
89
  return edge.id2;
110
90
  }
111
- async loadRawData(options) {
112
- const ids = await this.resolveIDs();
91
+ async loadRawIDs(addID) {
92
+ if (Array.isArray(this.src)) {
93
+ this.src.forEach((obj) => addID(obj));
94
+ }
95
+ else if (this.isEdgeQuery(this.src)) {
96
+ const idsMap = await this.src.queryAllIDs();
97
+ for (const [_, ids] of idsMap) {
98
+ ids.forEach((id) => addID(id));
99
+ }
100
+ }
101
+ else {
102
+ addID(this.src);
103
+ }
104
+ }
105
+ async loadRawData(infos, options) {
113
106
  const loader = this.dataLoaderFactory.createConfigurableLoader(options, this.viewer.context);
114
- await Promise.all(ids.map(async (id) => {
107
+ await Promise.all(infos.map(async (info) => {
108
+ if (info.invalidated) {
109
+ this.edges.set(info.id, []);
110
+ return;
111
+ }
115
112
  // there'll be filters for special edges here...
116
113
  // and then depending on that, we use this
117
114
  // what happens if you do first(10).id2(XX)
118
115
  // doesn't make sense
119
116
  // so only makes sense if one of these...
120
117
  // Id2 needs to be an option
121
- const edges = await loader.load(id);
122
- this.edges.set(id, edges);
118
+ const edges = await loader.load(info.id);
119
+ this.edges.set(info.id, edges);
123
120
  }));
124
121
  }
125
122
  async queryID2(id2) {
126
- const id = await this.getSingleID();
123
+ const info = await this.getSingleID();
124
+ if (info.invalidated) {
125
+ return;
126
+ }
127
127
  const loader = this.dataLoaderFactory.createLoader(this.viewer.context);
128
- return loader.loadEdgeForID2(id, id2);
128
+ return loader.loadEdgeForID2(info.id, id2);
129
129
  }
130
130
  async queryAllID2(id2) {
131
- const ids = await this.resolveIDs();
131
+ const infos = await this.genIDInfosToFetch();
132
132
  const loader = this.dataLoaderFactory.createLoader(this.viewer.context);
133
133
  const m = new Map();
134
- await Promise.all(ids.map(async (id) => {
135
- const edge = await loader.loadEdgeForID2(id, id2);
134
+ await Promise.all(infos.map(async (info) => {
135
+ if (info.invalidated) {
136
+ return;
137
+ }
138
+ const edge = await loader.loadEdgeForID2(info.id, id2);
136
139
  if (edge) {
137
- m.set(id, edge);
140
+ m.set(info.id, edge);
138
141
  }
139
142
  }));
140
143
  return m;
@@ -1,20 +1,23 @@
1
1
  import { Data, Ent, ID, EdgeQueryableDataOptions, LoadEntOptions, Viewer, LoaderFactory, ConfigurableLoaderFactory } from "../base";
2
- import { BaseEdgeQuery } from "./query";
3
- export interface CustomEdgeQueryOptions<T extends Ent> {
4
- src: Ent | ID;
2
+ import { BaseEdgeQuery, IDInfo, EdgeQuery } from "./query";
3
+ export interface CustomEdgeQueryOptions<TSource extends Ent, TDest extends Ent> {
4
+ src: TSource | ID;
5
5
  countLoaderFactory: LoaderFactory<ID, number>;
6
6
  dataLoaderFactory: ConfigurableLoaderFactory<ID, Data[]>;
7
- options: LoadEntOptions<T>;
7
+ options: LoadEntOptions<TDest>;
8
8
  sortColumn?: string;
9
9
  }
10
- export declare class CustomEdgeQueryBase<TDest extends Ent> extends BaseEdgeQuery<TDest, Data> {
10
+ export declare abstract class CustomEdgeQueryBase<TSource extends Ent, TDest extends Ent> extends BaseEdgeQuery<TSource, TDest, Data> implements EdgeQuery<TSource, TDest, Data> {
11
11
  viewer: Viewer;
12
12
  private options;
13
13
  private id;
14
- constructor(viewer: Viewer, options: CustomEdgeQueryOptions<TDest>);
14
+ constructor(viewer: Viewer, options: CustomEdgeQueryOptions<TSource, TDest>);
15
+ abstract sourceEnt(id: ID): Promise<Ent | null>;
16
+ private idVisible;
15
17
  queryRawCount(): Promise<number>;
16
18
  queryAllRawCount(): Promise<Map<ID, number>>;
17
- protected loadRawData(options: EdgeQueryableDataOptions): Promise<void>;
19
+ protected loadRawIDs(addID: (src: ID | TSource) => void): Promise<void>;
20
+ protected loadRawData(infos: IDInfo[], options: EdgeQueryableDataOptions): Promise<void>;
18
21
  dataToID(edge: Data): ID;
19
22
  protected loadEntsFromEdges(id: ID, rows: Data[]): Promise<TDest[]>;
20
23
  }