@entity-access/entity-access 1.0.136 → 1.0.138

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.
@@ -114,7 +114,8 @@ export default class EternityStorage {
114
114
  queued: r.queued,
115
115
  state: r.state,
116
116
  output: r.output,
117
- error: r.error
117
+ error: r.error,
118
+ lastID: r.lastID
118
119
  };
119
120
  }
120
121
  return null;
@@ -135,7 +136,8 @@ export default class EternityStorage {
135
136
  queued: r.queued,
136
137
  state: r.state,
137
138
  output: r.output,
138
- error: r.error
139
+ error: r.error,
140
+ lastID: r.lastID
139
141
  };
140
142
  }
141
143
  return null;
@@ -187,7 +189,7 @@ export default class EternityStorage {
187
189
  }
188
190
 
189
191
  w.state ||= "queued";
190
- w.updated = DateTime.utcNow;
192
+ w.updated ??= DateTime.utcNow;
191
193
  await db.saveChanges();
192
194
  });
193
195
  }
@@ -65,9 +65,12 @@ export default abstract class Workflow<TIn = any, TOut = any> {
65
65
  return Promise.resolve("");
66
66
  }
67
67
 
68
- protected waitForExternalEvent(ts: TimeSpan, ... names: string[]) {
68
+ protected waitForExternalEvent<T = any, TA extends string[] = string[]>(ts: TimeSpan, ... names: TA) {
69
69
  // do nothing...
70
- return Promise.resolve("");
70
+ return Promise.resolve<{
71
+ name: TA[number] | null | undefined,
72
+ result: T
73
+ }>({} as any);
71
74
  }
72
75
 
73
76
  protected async runChild<TChildIn, TChildOut>(type: IClassOf<Workflow<TChildIn, TChildOut>>, input: TChildIn): Promise<TChildOut> {
@@ -0,0 +1,132 @@
1
+ import assert from "assert";
2
+ import Inject, { Register, RegisterScoped, RegisterSingleton, ServiceProvider } from "../../di/di.js";
3
+ import EternityContext from "../../eternity/EternityContext.js";
4
+ import Workflow, { Activity } from "../../eternity/Workflow.js";
5
+ import WorkflowClock from "../../eternity/WorkflowClock.js";
6
+ import DateTime from "../../types/DateTime.js";
7
+ import { TestConfig } from "../TestConfig.js";
8
+ import { BaseDriver } from "../../drivers/base/BaseDriver.js";
9
+ import EternityStorage from "../../eternity/EternityStorage.js";
10
+ import TimeSpan from "../../types/TimeSpan.js";
11
+ import sleep from "../../common/sleep.js";
12
+
13
+ class MockClock extends WorkflowClock {
14
+
15
+ public get utcNow(): DateTime {
16
+ return this.time;
17
+ }
18
+
19
+ public set utcNow(v: DateTime) {
20
+ this.time = v;
21
+ }
22
+
23
+ private time: DateTime = DateTime.utcNow;
24
+
25
+ public add(ts: TimeSpan) {
26
+ this.time = this.time.add(ts);
27
+ return this;
28
+ }
29
+ }
30
+
31
+ @RegisterSingleton
32
+ class StateLogger {
33
+ state: string;
34
+ }
35
+
36
+ class VerifyWorkflow extends Workflow<string, string> {
37
+
38
+ public async run() {
39
+
40
+ const ts = TimeSpan.fromSeconds(15);
41
+
42
+ for (let index = 0; index < 5; index++) {
43
+ const { name, result } = await this.waitForExternalEvent(ts, "verify", "resend");
44
+ switch(name) {
45
+ case "verify":
46
+ await this.log("verify");
47
+ if (result === this.input) {
48
+ return "ok";
49
+ }
50
+ break;
51
+ case "resend":
52
+ // do something...
53
+ await this.delay(TimeSpan.fromSeconds(1));
54
+ await this.log("resend");
55
+ break;
56
+ }
57
+ }
58
+
59
+ return "failed";
60
+ }
61
+
62
+ @Activity
63
+ async log(
64
+ state: string,
65
+ @Inject stateLogger?: StateLogger
66
+ ) {
67
+ console.log(`${state} logged`);
68
+ stateLogger.state = state;
69
+ }
70
+
71
+ }
72
+
73
+ export default async function (this: TestConfig) {
74
+
75
+ const mockClock = new MockClock();
76
+ const stateLogger = new StateLogger();
77
+ const scope = new ServiceProvider();
78
+ scope.add(WorkflowClock, mockClock);
79
+ scope.add(BaseDriver, this.driver);
80
+ scope.add(StateLogger, stateLogger);
81
+ const storage = new EternityStorage(this.driver, mockClock);
82
+ scope.add(EternityStorage, storage);
83
+
84
+ const c = new EternityContext(storage);
85
+ scope.add(EternityContext, c);
86
+
87
+ // this is an important step
88
+ c.register(VerifyWorkflow);
89
+
90
+ await storage.seed();
91
+
92
+ const id = await c.queue(VerifyWorkflow, "a");
93
+
94
+ mockClock.add(TimeSpan.fromSeconds(5));
95
+
96
+ await c.processQueueOnce();
97
+
98
+ let w = await c.get(VerifyWorkflow, id);
99
+ assert.equal("queued", w.state);
100
+
101
+ mockClock.add(TimeSpan.fromSeconds(5));
102
+ await c.processQueueOnce();
103
+
104
+ w = await c.get(VerifyWorkflow, id);
105
+ assert.equal("queued", w.state);
106
+
107
+ await c.raiseEvent(id, { name: "resend"});
108
+ mockClock.add(TimeSpan.fromSeconds(5));
109
+ await c.processQueueOnce();
110
+
111
+ assert.equal("resend", stateLogger.state);
112
+
113
+ w = await c.get(VerifyWorkflow, id);
114
+ assert.equal("queued", w.state);
115
+
116
+ mockClock.add(TimeSpan.fromSeconds(5));
117
+ await c.raiseEvent(id, { name: "verify", result: "a"});
118
+ mockClock.add(TimeSpan.fromSeconds(5));
119
+ await c.processQueueOnce();
120
+ assert.equal("verify", stateLogger.state);
121
+
122
+ w = await c.get(VerifyWorkflow, id);
123
+ assert.equal("done", w.state);
124
+
125
+ mockClock.add(TimeSpan.fromDays(1));
126
+ await c.processQueueOnce();
127
+
128
+ // make sure workflow is deleted...
129
+
130
+ w = await c.get(VerifyWorkflow, id);
131
+ assert.equal(null, w);
132
+ }