@snowtop/ent 0.1.0-alpha137 → 0.1.0-alpha139

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,5 +1,5 @@
1
1
  import { Ent, EntConstructor, Viewer, ID, Data, PrivacyPolicy, Context, WriteOperation } from "../core/base";
2
- import { DataOperation, AssocEdgeInputOptions } from "../core/ent";
2
+ import { DataOperation, AssocEdgeInputOptions } from "./operations";
3
3
  import { Queryer } from "../core/db";
4
4
  import { TransformedUpdateOperation, UpdateOperation } from "../schema";
5
5
  import { FieldInfoMap } from "../schema/schema";
@@ -17,6 +17,7 @@ export interface Builder<TEnt extends Ent<TViewer>, TViewer extends Viewer = Vie
17
17
  placeholderID: ID;
18
18
  readonly viewer: TViewer;
19
19
  build(): Promise<Changeset>;
20
+ buildWithOptions_BETA?(options: ChangesetOptions): Promise<Changeset>;
20
21
  operation: WriteOperation;
21
22
  editedEnt?(): Promise<TEnt | null>;
22
23
  nodeType: string;
@@ -26,6 +27,8 @@ export interface Builder<TEnt extends Ent<TViewer>, TViewer extends Viewer = Vie
26
27
  export interface Executor extends Iterable<DataOperation>, Iterator<DataOperation> {
27
28
  placeholderID: ID;
28
29
  resolveValue(val: any): Ent | null;
30
+ builderOpChanged(builder: Builder<any>): boolean;
31
+ builder?: Builder<Ent>;
29
32
  execute(): Promise<void>;
30
33
  preFetch?(queryer: Queryer, context?: Context): Promise<void>;
31
34
  postFetch?(queryer: Queryer, context?: Context): Promise<void>;
@@ -48,9 +51,13 @@ export interface Observer<TEnt extends Ent<TViewer>, TBuilder extends Builder<TE
48
51
  export interface Validator<TEnt extends Ent<TViewer>, TBuilder extends Builder<TEnt, TViewer, TExistingEnt>, TViewer extends Viewer = Viewer, TInput extends Data = Data, TExistingEnt extends TMaybleNullableEnt<TEnt> = MaybeNull<TEnt>> {
49
52
  validate(builder: TBuilder, input: TInput): Promise<void | undefined | Error> | void | Error | undefined;
50
53
  }
54
+ export interface ChangesetOptions {
55
+ conditionalBuilder: Builder<any, any>;
56
+ }
51
57
  export interface Action<TEnt extends Ent<TViewer>, TBuilder extends Builder<TEnt, TViewer, TExistingEnt>, TViewer extends Viewer = Viewer, TInput extends Data = Data, TExistingEnt extends TMaybleNullableEnt<TEnt> = MaybeNull<TEnt>> {
52
58
  readonly viewer: Viewer;
53
59
  changeset(): Promise<Changeset>;
60
+ changesetWithOptions_BETA?(options: ChangesetOptions): Promise<Changeset>;
54
61
  builder: TBuilder;
55
62
  getPrivacyPolicy(): PrivacyPolicy<TEnt>;
56
63
  getTriggers?(): (Trigger<TEnt, TBuilder, TViewer, TInput, TExistingEnt> | Trigger<TEnt, TBuilder, TViewer, TInput, TExistingEnt>[])[];
@@ -1,19 +1,23 @@
1
1
  import { ID, Ent, Viewer, Context, Data } from "../core/base";
2
- import { DataOperation } from "../core/ent";
3
2
  import { Changeset, Executor } from "../action/action";
4
3
  import { Builder } from "../action";
5
4
  import { OrchestratorOptions } from "./orchestrator";
6
5
  import { Queryer } from "../core/db";
6
+ import { DataOperation } from "./operations";
7
7
  export declare class ListBasedExecutor<T extends Ent> implements Executor {
8
8
  private viewer;
9
9
  placeholderID: ID;
10
10
  private operations;
11
11
  private options?;
12
+ private complexOptions?;
12
13
  private idx;
13
- constructor(viewer: Viewer, placeholderID: ID, operations: DataOperation<T>[], options?: OrchestratorOptions<T, Data, Viewer<Ent<any> | null, ID | null>, T | null> | undefined);
14
+ builder?: Builder<Ent> | undefined;
15
+ constructor(viewer: Viewer, placeholderID: ID, operations: DataOperation<T>[], options?: OrchestratorOptions<T, Data, Viewer<Ent<any> | null, ID | null>, T | null> | undefined, complexOptions?: ComplexExecutorOptions | undefined);
14
16
  private lastOp;
15
17
  private createdEnt;
18
+ private updatedOps;
16
19
  resolveValue(val: ID): Ent | null;
20
+ builderOpChanged(builder: Builder<any>): boolean;
17
21
  [Symbol.iterator](): this;
18
22
  next(): IteratorResult<DataOperation<T>>;
19
23
  executeObservers(): Promise<void>;
@@ -21,22 +25,31 @@ export declare class ListBasedExecutor<T extends Ent> implements Executor {
21
25
  preFetch?(queryer: Queryer, context: Context): Promise<void>;
22
26
  postFetch?(queryer: Queryer, context: Context): Promise<void>;
23
27
  }
28
+ interface ComplexExecutorOptions {
29
+ conditionalOverride: boolean;
30
+ builder: Builder<any, any>;
31
+ }
24
32
  export declare class ComplexExecutor<T extends Ent> implements Executor {
25
33
  private viewer;
26
34
  placeholderID: ID;
35
+ private complexOptions?;
27
36
  private idx;
28
37
  private mapper;
29
38
  private lastOp;
30
39
  private allOperations;
31
40
  private executors;
32
- constructor(viewer: Viewer, placeholderID: ID, operations: DataOperation[], dependencies: Map<ID, Builder<T>>, changesets: Changeset[], options?: OrchestratorOptions<T, Data, Viewer>);
41
+ private updatedOps;
42
+ builder?: Builder<Ent> | undefined;
43
+ constructor(viewer: Viewer, placeholderID: ID, operations: DataOperation[], dependencies: Map<ID, Builder<T>>, changesets: Changeset[], options?: OrchestratorOptions<T, Data, Viewer>, complexOptions?: ComplexExecutorOptions | undefined);
33
44
  [Symbol.iterator](): this;
34
45
  private handleCreatedEnt;
35
46
  next(): IteratorResult<DataOperation<Ent>>;
36
47
  resolveValue(val: ID): Ent | null;
48
+ builderOpChanged(builder: Builder<any>): boolean;
37
49
  executeObservers(): Promise<void>;
38
50
  execute(): Promise<void>;
39
51
  preFetch?(queryer: Queryer, context: Context): Promise<void>;
40
52
  postFetch?(queryer: Queryer, context: Context): Promise<void>;
41
53
  }
42
54
  export declare function executeOperations(executor: Executor, context?: Context, trackOps?: true): Promise<DataOperation<Ent<Viewer<Ent<any> | null, ID | null>>>[]>;
55
+ export {};
@@ -8,15 +8,19 @@ const graph_data_structure_1 = __importDefault(require("graph-data-structure"));
8
8
  const ent_1 = require("../core/ent");
9
9
  const db_1 = __importDefault(require("../core/db"));
10
10
  const logger_1 = require("../core/logger");
11
+ const operations_1 = require("./operations");
11
12
  // private to ent
12
13
  class ListBasedExecutor {
13
- constructor(viewer, placeholderID, operations, options) {
14
+ constructor(viewer, placeholderID, operations, options, complexOptions) {
14
15
  this.viewer = viewer;
15
16
  this.placeholderID = placeholderID;
16
17
  this.operations = operations;
17
18
  this.options = options;
19
+ this.complexOptions = complexOptions;
18
20
  this.idx = 0;
19
21
  this.createdEnt = null;
22
+ this.updatedOps = new Map();
23
+ this.builder = options?.builder;
20
24
  }
21
25
  resolveValue(val) {
22
26
  if (val === this.placeholderID && val !== undefined) {
@@ -24,6 +28,10 @@ class ListBasedExecutor {
24
28
  }
25
29
  return null;
26
30
  }
31
+ builderOpChanged(builder) {
32
+ const v = this.updatedOps.get(builder.placeholderID);
33
+ return v !== undefined && v !== builder.operation;
34
+ }
27
35
  [Symbol.iterator]() {
28
36
  return this;
29
37
  }
@@ -33,13 +41,15 @@ class ListBasedExecutor {
33
41
  if (createdEnt) {
34
42
  this.createdEnt = createdEnt;
35
43
  }
44
+ maybeUpdateOperationForOp(this.lastOp, this.updatedOps);
36
45
  const done = this.idx === this.operations.length;
37
- const op = this.operations[this.idx];
46
+ const op = changeOp(this.operations[this.idx], this.complexOptions);
38
47
  this.idx++;
39
48
  this.lastOp = op;
40
49
  // reset since this could be called multiple times. not needed if we have getSortedOps or something like that
41
50
  if (done) {
42
- this.idx = 0;
51
+ // TODO need to figure this out
52
+ // this.idx = 0;
43
53
  }
44
54
  return {
45
55
  value: op,
@@ -53,7 +63,13 @@ class ListBasedExecutor {
53
63
  }
54
64
  const builder = this.options.builder;
55
65
  await Promise.all(action.getObservers().map(async (observer) => {
56
- await observer.observe(builder, action.getInput());
66
+ try {
67
+ await observer.observe(builder, action.getInput());
68
+ }
69
+ catch (err) {
70
+ // TODO we eventually want a global observer error handler so that this can be logged or whatever...
71
+ // TODO https://github.com/lolopinto/ent/issues/1429
72
+ }
57
73
  }));
58
74
  }
59
75
  async execute() {
@@ -85,14 +101,28 @@ function getCreatedEnt(viewer, op) {
85
101
  }
86
102
  return null;
87
103
  }
104
+ function maybeUpdateOperationForOp(op, updatedOps) {
105
+ if (!op || !op.updatedOperation) {
106
+ return;
107
+ }
108
+ const r = op.updatedOperation();
109
+ if (!r || r.builder.operation === r.operation) {
110
+ return;
111
+ }
112
+ updatedOps.set(r.builder.placeholderID, r.operation);
113
+ // console.debug(updatedOps);
114
+ }
88
115
  class ComplexExecutor {
89
- constructor(viewer, placeholderID, operations, dependencies, changesets, options) {
116
+ constructor(viewer, placeholderID, operations, dependencies, changesets, options, complexOptions) {
90
117
  this.viewer = viewer;
91
118
  this.placeholderID = placeholderID;
119
+ this.complexOptions = complexOptions;
92
120
  this.idx = 0;
93
121
  this.mapper = new Map();
94
122
  this.allOperations = [];
95
123
  this.executors = [];
124
+ this.updatedOps = new Map();
125
+ this.builder = options?.builder;
96
126
  let graph = (0, graph_data_structure_1.default)();
97
127
  const changesetMap = new Map();
98
128
  const impl = (c) => {
@@ -141,6 +171,11 @@ class ComplexExecutor {
141
171
  // get ordered list of ops
142
172
  let executor = c.executor();
143
173
  for (let op of executor) {
174
+ if (!op) {
175
+ // TODO what is happening...
176
+ // change in behavior in next() leading to needing to do this
177
+ break;
178
+ }
144
179
  if (op.createdEnt) {
145
180
  nodeOps.add(op);
146
181
  }
@@ -171,25 +206,23 @@ class ComplexExecutor {
171
206
  }
172
207
  const placeholderID = this.lastOp.placeholderID;
173
208
  if (!placeholderID) {
174
- console.error(`op ${this.lastOp} which implements getCreatedEnt doesn't have a placeholderID`);
175
- return;
209
+ throw new Error(`op ${this.lastOp} which implements getCreatedEnt doesn't have a placeholderID`);
176
210
  }
177
211
  this.mapper.set(placeholderID, createdEnt);
178
212
  }
179
213
  next() {
180
214
  this.handleCreatedEnt();
215
+ maybeUpdateOperationForOp(this.lastOp, this.updatedOps);
181
216
  const done = this.idx === this.allOperations.length;
182
- const op = this.allOperations[this.idx];
217
+ const op = changeOp(this.allOperations[this.idx], this.complexOptions);
183
218
  this.idx++;
184
219
  this.lastOp = op;
185
220
  // reset since this could be called multiple times. not needed if we have getSortedOps or something like that
186
221
  if (done) {
187
- this.idx = 0;
222
+ // TODO need to figure this out
223
+ // this.idx = 0;
188
224
  }
189
- return {
190
- value: op,
191
- done: done,
192
- };
225
+ return { value: op, done };
193
226
  }
194
227
  resolveValue(val) {
195
228
  let ent = this.mapper.get(val);
@@ -204,8 +237,16 @@ class ComplexExecutor {
204
237
  }
205
238
  return null;
206
239
  }
240
+ builderOpChanged(builder) {
241
+ const v = this.updatedOps.get(builder.placeholderID);
242
+ // console.debug(this.updatedOps, builder.placeholderID, v, builder.operation);
243
+ return v !== undefined && v !== builder.operation;
244
+ }
207
245
  async executeObservers() {
208
246
  await Promise.all(this.executors.map((executor) => {
247
+ if (executor.builder && this.builderOpChanged(executor.builder)) {
248
+ return null;
249
+ }
209
250
  if (!executor.executeObservers) {
210
251
  return null;
211
252
  }
@@ -248,6 +289,9 @@ async function executeOperations(executor, context, trackOps) {
248
289
  if (isSyncClient(client)) {
249
290
  client.runInTransaction(() => {
250
291
  for (const operation of executor) {
292
+ if (operation.shortCircuit && operation.shortCircuit(executor)) {
293
+ continue;
294
+ }
251
295
  if (trackOps) {
252
296
  operations.push(operation);
253
297
  }
@@ -262,6 +306,9 @@ async function executeOperations(executor, context, trackOps) {
262
306
  (0, ent_1.logQuery)("BEGIN", []);
263
307
  await client.query("BEGIN");
264
308
  for (const operation of executor) {
309
+ if (operation.shortCircuit && operation.shortCircuit(executor)) {
310
+ continue;
311
+ }
265
312
  if (trackOps) {
266
313
  operations.push(operation);
267
314
  }
@@ -298,3 +345,16 @@ async function executeOperations(executor, context, trackOps) {
298
345
  return operations;
299
346
  }
300
347
  exports.executeOperations = executeOperations;
348
+ function changeOp(op, complexOptions) {
349
+ if (!op ||
350
+ !complexOptions?.conditionalOverride ||
351
+ op instanceof operations_1.ConditionalNodeOperation) {
352
+ return op;
353
+ }
354
+ if (op.createdEnt) {
355
+ return new operations_1.ConditionalNodeOperation(op, complexOptions.builder);
356
+ }
357
+ else {
358
+ return new operations_1.ConditionalOperation(op, complexOptions.builder);
359
+ }
360
+ }
package/action/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { WriteOperation, Builder, Changeset, Trigger, Observer, Validator, Action, saveBuilder, saveBuilderX, setEdgeTypeInGroup, TriggerReturn, } from "./action";
1
+ export { WriteOperation, Builder, Changeset, Trigger, Observer, Validator, Action, saveBuilder, saveBuilderX, setEdgeTypeInGroup, TriggerReturn, ChangesetOptions, } from "./action";
2
2
  export { OrchestratorOptions, Orchestrator, EntChangeset, EdgeInputData, } from "./orchestrator";
3
3
  export { DenyIfBuilder, AllowIfBuilder } from "./privacy";
4
4
  export { RelativeFieldValue, RelativeNumberValue, NumberOps, convertRelativeInput, maybeConvertRelativeInputPlusExpressions, } from "./relative_value";
@@ -0,0 +1,125 @@
1
+ import { Queryer, SyncQueryer } from "../core/db";
2
+ import { Viewer, Ent, ID, Data, DataOptions, EditRowOptions, LoadEntOptions, Context, CreateRowOptions } from "../core/base";
3
+ import { Executor } from "../action/action";
4
+ import { WriteOperation, Builder } from "../action";
5
+ import { AssocEdgeData, parameterizedQueryOptions } from "../core/ent";
6
+ export interface UpdatedOperation {
7
+ operation: WriteOperation;
8
+ builder: Builder<any>;
9
+ }
10
+ export interface DataOperation<T extends Ent = Ent> {
11
+ builder: Builder<T>;
12
+ preFetch?(queryer: Queryer, context?: Context): Promise<void>;
13
+ performWriteSync(queryer: SyncQueryer, context?: Context): void;
14
+ performWrite(queryer: Queryer, context?: Context): Promise<void>;
15
+ placeholderID?: ID;
16
+ returnedRow?(): Data | null;
17
+ createdEnt?(viewer: Viewer): T | null;
18
+ shortCircuit?(executor: Executor): boolean;
19
+ updatedOperation?(): UpdatedOperation | null;
20
+ resolve?(executor: Executor): void;
21
+ postFetch?(queryer: Queryer, context?: Context): Promise<void>;
22
+ }
23
+ export declare class DeleteNodeOperation implements DataOperation {
24
+ private id;
25
+ readonly builder: Builder<Ent>;
26
+ private options;
27
+ constructor(id: ID, builder: Builder<Ent>, options: DataOptions);
28
+ performWrite(queryer: Queryer, context?: Context): Promise<void>;
29
+ performWriteSync(queryer: SyncQueryer, context?: Context): void;
30
+ }
31
+ export declare class RawQueryOperation implements DataOperation {
32
+ builder: Builder<Ent>;
33
+ private queries;
34
+ constructor(builder: Builder<Ent>, queries: (string | parameterizedQueryOptions)[]);
35
+ performWrite(queryer: Queryer, context?: Context): Promise<void>;
36
+ performWriteSync(queryer: SyncQueryer, context?: Context): void;
37
+ }
38
+ export interface EditNodeOptions<T extends Ent> extends EditRowOptions {
39
+ fieldsToResolve: string[];
40
+ loadEntOptions: LoadEntOptions<T>;
41
+ key: string;
42
+ onConflict?: CreateRowOptions["onConflict"];
43
+ builder: Builder<T>;
44
+ }
45
+ export declare class EditNodeOperation<T extends Ent> implements DataOperation {
46
+ options: EditNodeOptions<T>;
47
+ private existingEnt;
48
+ private row;
49
+ placeholderID?: ID | undefined;
50
+ private updatedOp;
51
+ builder: Builder<T>;
52
+ private resolved;
53
+ constructor(options: EditNodeOptions<T>, existingEnt?: Ent | null);
54
+ resolve<T extends Ent>(executor: Executor): void;
55
+ private hasData;
56
+ private buildOnConflictQuery;
57
+ performWrite(queryer: Queryer, context?: Context): Promise<void>;
58
+ private buildReloadQuery;
59
+ private reloadRow;
60
+ performWriteSync(queryer: SyncQueryer, context?: Context): void;
61
+ returnedRow(): Data | null;
62
+ createdEnt(viewer: Viewer): T | null;
63
+ updatedOperation(): UpdatedOperation | null;
64
+ }
65
+ export interface AssocEdgeInputOptions extends AssocEdgeOptions {
66
+ time?: Date;
67
+ data?: string | Builder<Ent>;
68
+ }
69
+ export interface AssocEdgeOptions {
70
+ conditional?: boolean;
71
+ }
72
+ export interface AssocEdgeInput extends AssocEdgeInputOptions {
73
+ id1: ID;
74
+ id1Type: string;
75
+ edgeType: string;
76
+ id2: ID;
77
+ id2Type: string;
78
+ }
79
+ export declare class EdgeOperation implements DataOperation {
80
+ builder: Builder<any>;
81
+ edgeInput: AssocEdgeInput;
82
+ private options;
83
+ private edgeData;
84
+ private constructor();
85
+ preFetch(queryer: Queryer, context?: Context): Promise<void>;
86
+ performWrite(queryer: Queryer, context?: Context): Promise<void>;
87
+ performWriteSync(queryer: SyncQueryer, context?: Context): void;
88
+ private getDeleteRowParams;
89
+ private performDeleteWrite;
90
+ private performDeleteWriteSync;
91
+ private getInsertRowParams;
92
+ private performInsertWrite;
93
+ private performInsertWriteSync;
94
+ private resolveImpl;
95
+ resolve(executor: Executor): void;
96
+ symmetricEdge(): EdgeOperation;
97
+ inverseEdge(edgeData: AssocEdgeData): EdgeOperation;
98
+ private static resolveIDs;
99
+ private static isBuilder;
100
+ private static resolveData;
101
+ static inboundEdge<T extends Ent, T2 extends Ent>(builder: Builder<T>, edgeType: string, id1: Builder<T2> | ID, nodeType: string, options?: AssocEdgeInputOptions): EdgeOperation;
102
+ static outboundEdge<T extends Ent, T2 extends Ent>(builder: Builder<T>, edgeType: string, id2: Builder<T2> | ID, nodeType: string, options?: AssocEdgeInputOptions): EdgeOperation;
103
+ static removeInboundEdge<T extends Ent>(builder: Builder<T>, edgeType: string, id1: ID): EdgeOperation;
104
+ static removeOutboundEdge<T extends Ent>(builder: Builder<T>, edgeType: string, id2: ID): EdgeOperation;
105
+ }
106
+ export declare class ConditionalOperation<T extends Ent = Ent> implements DataOperation<T> {
107
+ protected op: DataOperation<T>;
108
+ private conditionalBuilder;
109
+ placeholderID?: ID | undefined;
110
+ protected shortCircuited: boolean;
111
+ readonly builder: Builder<T>;
112
+ constructor(op: DataOperation<T>, conditionalBuilder: Builder<any>);
113
+ shortCircuit(executor: Executor): boolean;
114
+ preFetch(queryer: Queryer, context?: Context<Viewer<Ent<any> | null, ID | null>> | undefined): Promise<void>;
115
+ performWriteSync(queryer: SyncQueryer, context?: Context<Viewer<Ent<any> | null, ID | null>> | undefined): void;
116
+ performWrite(queryer: Queryer, context?: Context<Viewer<Ent<any> | null, ID | null>> | undefined): Promise<void>;
117
+ returnedRow(): Data | null;
118
+ updatedOperation(): UpdatedOperation | null;
119
+ resolve(executor: Executor): void;
120
+ postFetch(queryer: Queryer, context?: Context<Viewer<Ent<any> | null, ID | null>> | undefined): Promise<void>;
121
+ }
122
+ export declare class ConditionalNodeOperation<T extends Ent> extends ConditionalOperation<T> {
123
+ createdEnt(viewer: Viewer): T | null;
124
+ updatedOperation(): UpdatedOperation | null;
125
+ }