anbaric-state-machine 1.2.2 → 1.3.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anbaric-state-machine",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "This is the state machine library that one can use to add state management to an application. It will, by default, be locally runnable but can deployed to the Anbaric Cloud via the CLI",
5
5
  "license": "MIT",
6
6
  "author": "chris@anbaric.ai",
@@ -15,8 +15,8 @@
15
15
  "typescript": "^7.0.2"
16
16
  },
17
17
  "dependencies": {
18
- "anbaric-cloud": "^1.2.2",
19
- "anbaric-tsapi": "^1.2.2"
18
+ "anbaric-cloud": "^1.3.0",
19
+ "anbaric-tsapi": "^1.3.0"
20
20
  },
21
21
  "files": [
22
22
  "src"
@@ -1,8 +1,9 @@
1
- import {Action, Actor, Consumer, Job, JobPersistence, PropertyDefinition, Queue, State} from "anbaric-tsapi";
1
+ import {Action, Actor, Auditor, Consumer, Job, JobPersistence, PropertyDefinition, Queue, State} from "anbaric-tsapi";
2
2
  import {JobPersistenceFactory} from "./persistence/JobPersistenceFactory";
3
3
  import {QueueFactory} from "./scheduling/QueueFactory";
4
4
  import {ConsumerFactory} from "./scheduling/ConsumerFactory";
5
- import {Auditor} from "./auditing/Auditor";
5
+ import {AuditorFactory} from "./auditing/AuditorFactory";
6
+ import {AuditorTransaction} from "./auditing/AuditorTransaction";
6
7
 
7
8
  class StateMachine {
8
9
 
@@ -13,8 +14,9 @@ class StateMachine {
13
14
  private persistence: JobPersistence;
14
15
  private queue: Queue;
15
16
  private consumer: Consumer;
17
+ private auditor: Auditor;
16
18
 
17
- constructor(workflowId : string, states : Array<State>, startState : string, dataSchema : Array<PropertyDefinition>, persistence : JobPersistence = JobPersistenceFactory.instance(), queue : Queue = QueueFactory.instance()) {
19
+ constructor(workflowId : string, states : Array<State>, startState : string, dataSchema : Array<PropertyDefinition>, persistence : JobPersistence = JobPersistenceFactory.instance(), queue : Queue = QueueFactory.instance(), auditor : Auditor = AuditorFactory.instance()) {
18
20
 
19
21
  this.workflowId = workflowId;
20
22
  this.states = new Map(states.map(state => [state.id, state]));
@@ -22,6 +24,7 @@ class StateMachine {
22
24
  this.dataSchema = new Map(dataSchema.map(property => [property.id, property]));
23
25
  this.persistence = persistence;
24
26
  this.queue = queue;
27
+ this.auditor = auditor;
25
28
 
26
29
  this.consumer = ConsumerFactory.instance(queue)
27
30
  this.consumer.subscribe(workflowId, jobId => this.progressJob(jobId));
@@ -35,7 +38,7 @@ class StateMachine {
35
38
  if (! this.validateProperties(properties ?? new Map(), true)) throw new Error("Invalid properties");
36
39
 
37
40
  if (! this.authorizeActor(actor, job)) {
38
- Auditor.instance().audit(job.id, actor, "Unauthorized start", null);
41
+ await this.auditor.audit(job.id, actor, "Unauthorized start", null);
39
42
  throw new Error("Unauthorized");
40
43
  }
41
44
 
@@ -49,14 +52,14 @@ class StateMachine {
49
52
  async updateJob(jobId : string, properties : Map<string, any>, actor : Actor) : Promise<void> {
50
53
 
51
54
  if (! this.authorizeActor(actor, await this.persistence.retrieve(jobId))) {
52
- Auditor.instance().audit(jobId, actor, "Unauthorized update", null);
55
+ await this.auditor.audit(jobId, actor, "Unauthorized update", null);
53
56
  throw new Error("Unauthorized");
54
57
  }
55
58
 
56
59
  if (! this.validateProperties(properties, false)) throw new Error("Invalid properties");
57
60
 
58
61
  await this.persistence.updateProperties(jobId, properties);
59
- Auditor.instance().audit(jobId, actor, "Properties updated", Object.fromEntries(properties));
62
+ await this.auditor.audit(jobId, actor, "Properties updated", Object.fromEntries(properties));
60
63
 
61
64
  await this.queue.enqueue(jobId, this.workflowId);
62
65
  }
@@ -67,19 +70,19 @@ class StateMachine {
67
70
  if (! action.predicate(job)) throw new Error("Action predicate unmet");
68
71
 
69
72
  if (! this.authorizeActor(action.actor, job)) {
70
- Auditor.instance().audit(jobId, action.actor, "Unauthorized update", null);
73
+ await this.auditor.audit(jobId, action.actor, "Unauthorized update", null);
71
74
  throw new Error("Actor not authorized to execute action");
72
75
  }
73
76
 
74
77
  const newProperties = await action.run(job);
75
78
 
76
79
  if (! this.validateProperties(newProperties, false)) {
77
- Auditor.instance().audit(jobId, action.actor, "Invalid properties", Object.fromEntries(newProperties));
80
+ await this.auditor.audit(jobId, action.actor, "Invalid properties", Object.fromEntries(newProperties));
78
81
  throw new Error("The action generated invalid properties");
79
82
  }
80
83
 
81
84
  await this.persistence.updateProperties(jobId, newProperties);
82
- Auditor.instance().audit(jobId, action.actor, "Properties updated", Object.fromEntries(newProperties));
85
+ await this.auditor.audit(jobId, action.actor, "Properties updated", Object.fromEntries(newProperties));
83
86
 
84
87
  await this.queue.enqueue(jobId, this.workflowId);
85
88
  }
@@ -100,7 +103,7 @@ class StateMachine {
100
103
  }
101
104
 
102
105
  private async progressJob(jobId : string) : Promise<void> {
103
- const auditTransaction = Auditor.instance().transaction();
106
+ const auditTransaction = new AuditorTransaction(this.auditor);
104
107
  let pristine = true;
105
108
 
106
109
  const job = await this.persistence.retrieve(jobId);
@@ -142,7 +145,7 @@ class StateMachine {
142
145
  await this.queue.enqueue(job.id, this.workflowId);
143
146
  }
144
147
 
145
- auditTransaction.flush();
148
+ await auditTransaction.flush();
146
149
  }
147
150
 
148
151
  async cleanUp() : Promise<void> {
@@ -0,0 +1,14 @@
1
+ import {Auditor} from "anbaric-tsapi";
2
+ import {CloudAuditor} from "anbaric-cloud";
3
+ import {ConsoleAuditor} from "./ConsoleAuditor";
4
+
5
+ const AuditorFactory = {
6
+
7
+ instance() : Auditor {
8
+ if (process.env.ANBARIC_AUDITOR_TYPE === "cloud") return new CloudAuditor();
9
+ return new ConsoleAuditor();
10
+ },
11
+
12
+ };
13
+
14
+ export { AuditorFactory }
@@ -1,7 +1,6 @@
1
- import {Actor} from "anbaric-tsapi";
2
- import type {Auditor} from "./Auditor";
1
+ import {Actor, Auditor} from "anbaric-tsapi";
3
2
 
4
- type AuditEntry = {
3
+ type BufferedEntry = {
5
4
  jobId : string,
6
5
  actor : Actor | undefined,
7
6
  changeDescription : string,
@@ -10,7 +9,7 @@ type AuditEntry = {
10
9
 
11
10
  class AuditorTransaction {
12
11
 
13
- private entries = new Array<AuditEntry>();
12
+ private entries = new Array<BufferedEntry>();
14
13
 
15
14
  constructor(private auditor : Auditor) {}
16
15
 
@@ -18,10 +17,12 @@ class AuditorTransaction {
18
17
  this.entries.push({ jobId, actor, changeDescription, details });
19
18
  }
20
19
 
21
- flush() : void {
22
- this.entries.forEach(entry =>
23
- this.auditor.audit(entry.jobId, entry.actor, entry.changeDescription, entry.details));
20
+ async flush() : Promise<void> {
21
+ const flushing = this.entries;
24
22
  this.entries = [];
23
+ for (const entry of flushing) {
24
+ await this.auditor.audit(entry.jobId, entry.actor, entry.changeDescription, entry.details);
25
+ }
25
26
  }
26
27
 
27
28
  }
@@ -0,0 +1,11 @@
1
+ import {Actor, Auditor} from "anbaric-tsapi";
2
+
3
+ class ConsoleAuditor implements Auditor {
4
+
5
+ async audit(jobId : string, actor : Actor | undefined, changeDescription : string, details : any) : Promise<void> {
6
+ console.log(`[${jobId}] ${actor?.id ?? "anonymous"} ${changeDescription} ${JSON.stringify(details ?? null)}`);
7
+ }
8
+
9
+ }
10
+
11
+ export { ConsoleAuditor }
package/src/index.ts CHANGED
@@ -2,8 +2,9 @@ export * from "./StateMachine";
2
2
  export * from "./actors/Agent";
3
3
  export * from "./actors/Code";
4
4
  export * from "./actors/Human";
5
- export * from "./auditing/Auditor";
5
+ export * from "./auditing/AuditorFactory";
6
6
  export * from "./auditing/AuditorTransaction";
7
+ export * from "./auditing/ConsoleAuditor";
7
8
  export * from "./persistence/InMemoryJobPersistence";
8
9
  export * from "./persistence/JobPersistenceFactory";
9
10
  export * from "./scheduling/InMemoryQueue";
@@ -1,25 +0,0 @@
1
- import {Actor} from "anbaric-tsapi";
2
- import {AuditorTransaction} from "./AuditorTransaction";
3
-
4
- class Auditor {
5
-
6
- private static singleton? : Auditor;
7
-
8
- static instance() : Auditor {
9
- if (!Auditor.singleton) {
10
- Auditor.singleton = new Auditor();
11
- }
12
- return Auditor.singleton;
13
- }
14
-
15
- audit(jobId : string, actor : Actor | undefined, changeDescription : string, details : any) : void {
16
- console.log(`[${jobId}] ${actor?.id ?? "anonymous"} ${changeDescription} ${JSON.stringify(details ?? null)}`);
17
- }
18
-
19
- transaction() : AuditorTransaction {
20
- return new AuditorTransaction(this);
21
- }
22
-
23
- }
24
-
25
- export { Auditor }