anbaric-state-machine 1.3.0 → 1.4.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.3.0",
3
+ "version": "1.4.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.3.0",
19
- "anbaric-tsapi": "^1.3.0"
18
+ "anbaric-cloud": "^1.4.0",
19
+ "anbaric-tsapi": "^1.4.0"
20
20
  },
21
21
  "files": [
22
22
  "src"
@@ -2,6 +2,7 @@ import {Action, Actor, Auditor, Consumer, Job, JobPersistence, PropertyDefinitio
2
2
  import {JobPersistenceFactory} from "./persistence/JobPersistenceFactory";
3
3
  import {QueueFactory} from "./scheduling/QueueFactory";
4
4
  import {ConsumerFactory} from "./scheduling/ConsumerFactory";
5
+ import {Code} from "./actors/Code";
5
6
  import {AuditorFactory} from "./auditing/AuditorFactory";
6
7
  import {AuditorTransaction} from "./auditing/AuditorTransaction";
7
8
 
@@ -15,6 +16,7 @@ class StateMachine {
15
16
  private queue: Queue;
16
17
  private consumer: Consumer;
17
18
  private auditor: Auditor;
19
+ private machineActor: Code;
18
20
 
19
21
  constructor(workflowId : string, states : Array<State>, startState : string, dataSchema : Array<PropertyDefinition>, persistence : JobPersistence = JobPersistenceFactory.instance(), queue : Queue = QueueFactory.instance(), auditor : Auditor = AuditorFactory.instance()) {
20
22
 
@@ -25,6 +27,7 @@ class StateMachine {
25
27
  this.persistence = persistence;
26
28
  this.queue = queue;
27
29
  this.auditor = auditor;
30
+ this.machineActor = new Code(workflowId, "state-machine");
28
31
 
29
32
  this.consumer = ConsumerFactory.instance(queue)
30
33
  this.consumer.subscribe(workflowId, jobId => this.progressJob(jobId));
@@ -38,11 +41,13 @@ class StateMachine {
38
41
  if (! this.validateProperties(properties ?? new Map(), true)) throw new Error("Invalid properties");
39
42
 
40
43
  if (! this.authorizeActor(actor, job)) {
41
- await this.auditor.audit(job.id, actor, "Unauthorized start", null);
44
+ await this.auditor.audit(job.id, actor ?? this.machineActor, "CREATE", "Unauthorized start", null);
42
45
  throw new Error("Unauthorized");
43
46
  }
44
47
 
45
48
  await this.persistence.save(job);
49
+ await this.auditor.audit(job.id, actor ?? this.machineActor, "CREATE", "Job started",
50
+ Object.fromEntries(job.properties));
46
51
 
47
52
  await this.queue.enqueue(job.id, this.workflowId);
48
53
 
@@ -52,14 +57,14 @@ class StateMachine {
52
57
  async updateJob(jobId : string, properties : Map<string, any>, actor : Actor) : Promise<void> {
53
58
 
54
59
  if (! this.authorizeActor(actor, await this.persistence.retrieve(jobId))) {
55
- await this.auditor.audit(jobId, actor, "Unauthorized update", null);
60
+ await this.auditor.audit(jobId, actor, "UPDATE_PROPERTIES", "Unauthorized update", null);
56
61
  throw new Error("Unauthorized");
57
62
  }
58
63
 
59
64
  if (! this.validateProperties(properties, false)) throw new Error("Invalid properties");
60
65
 
61
66
  await this.persistence.updateProperties(jobId, properties);
62
- await this.auditor.audit(jobId, actor, "Properties updated", Object.fromEntries(properties));
67
+ await this.auditor.audit(jobId, actor, "UPDATE_PROPERTIES", "Properties updated", Object.fromEntries(properties));
63
68
 
64
69
  await this.queue.enqueue(jobId, this.workflowId);
65
70
  }
@@ -70,19 +75,19 @@ class StateMachine {
70
75
  if (! action.predicate(job)) throw new Error("Action predicate unmet");
71
76
 
72
77
  if (! this.authorizeActor(action.actor, job)) {
73
- await this.auditor.audit(jobId, action.actor, "Unauthorized update", null);
78
+ await this.auditor.audit(jobId, action.actor, "UPDATE_PROPERTIES", "Unauthorized update", null);
74
79
  throw new Error("Actor not authorized to execute action");
75
80
  }
76
81
 
77
82
  const newProperties = await action.run(job);
78
83
 
79
84
  if (! this.validateProperties(newProperties, false)) {
80
- await this.auditor.audit(jobId, action.actor, "Invalid properties", Object.fromEntries(newProperties));
85
+ await this.auditor.audit(jobId, action.actor, "UPDATE_PROPERTIES", "Invalid properties", Object.fromEntries(newProperties));
81
86
  throw new Error("The action generated invalid properties");
82
87
  }
83
88
 
84
89
  await this.persistence.updateProperties(jobId, newProperties);
85
- await this.auditor.audit(jobId, action.actor, "Properties updated", Object.fromEntries(newProperties));
90
+ await this.auditor.audit(jobId, action.actor, "UPDATE_PROPERTIES", "Properties updated", Object.fromEntries(newProperties));
86
91
 
87
92
  await this.queue.enqueue(jobId, this.workflowId);
88
93
  }
@@ -113,18 +118,18 @@ class StateMachine {
113
118
  if (! action.predicate(job)) continue;
114
119
 
115
120
  if (! this.authorizeActor(action.actor, job)) {
116
- auditTransaction.audit(jobId, action.actor, "Unauthorized update", null);
121
+ auditTransaction.audit(jobId, action.actor, "UPDATE_PROPERTIES", "Unauthorized update", null);
117
122
  continue;
118
123
  }
119
124
 
120
125
  const newProperties = await action.run(job);
121
126
 
122
127
  if (! this.validateProperties(newProperties, false)) {
123
- auditTransaction.audit(jobId, action.actor, "Invalid properties", Object.fromEntries(newProperties));
128
+ auditTransaction.audit(jobId, action.actor, "UPDATE_PROPERTIES", "Invalid properties", Object.fromEntries(newProperties));
124
129
  continue;
125
130
  }
126
131
 
127
- auditTransaction.audit(jobId, action.actor, "Properties updated", Object.fromEntries(newProperties));
132
+ auditTransaction.audit(jobId, action.actor, "UPDATE_PROPERTIES", "Properties updated", Object.fromEntries(newProperties));
128
133
  newProperties.forEach((value, key) => {
129
134
  pristine = false;
130
135
  job.properties.set(key, value);
@@ -134,7 +139,7 @@ class StateMachine {
134
139
  for (const transition of currentState?.transitions ?? []) {
135
140
  if (! this.states.has(transition.to)) continue;
136
141
  if (job.transition(transition)) {
137
- auditTransaction.audit(jobId, undefined, `Transitioned to "${transition.to}"`, null);
142
+ auditTransaction.audit(jobId, this.machineActor, "CHANGE_STATE", `Transitioned to "${transition.to}"`, null);
138
143
  pristine = false;
139
144
  break;
140
145
  }
@@ -1,8 +1,9 @@
1
- import {Actor, Auditor} from "anbaric-tsapi";
1
+ import {Actor, AuditChange, Auditor} from "anbaric-tsapi";
2
2
 
3
3
  type BufferedEntry = {
4
4
  jobId : string,
5
- actor : Actor | undefined,
5
+ actor : Actor,
6
+ change : AuditChange,
6
7
  changeDescription : string,
7
8
  details : any,
8
9
  };
@@ -13,15 +14,15 @@ class AuditorTransaction {
13
14
 
14
15
  constructor(private auditor : Auditor) {}
15
16
 
16
- audit(jobId : string, actor : Actor | undefined, changeDescription : string, details : any) : void {
17
- this.entries.push({ jobId, actor, changeDescription, details });
17
+ audit(jobId : string, actor : Actor, change : AuditChange, changeDescription : string, details : any) : void {
18
+ this.entries.push({ jobId, actor, change, changeDescription, details });
18
19
  }
19
20
 
20
21
  async flush() : Promise<void> {
21
22
  const flushing = this.entries;
22
23
  this.entries = [];
23
24
  for (const entry of flushing) {
24
- await this.auditor.audit(entry.jobId, entry.actor, entry.changeDescription, entry.details);
25
+ await this.auditor.audit(entry.jobId, entry.actor, entry.change, entry.changeDescription, entry.details);
25
26
  }
26
27
  }
27
28
 
@@ -1,9 +1,10 @@
1
- import {Actor, Auditor} from "anbaric-tsapi";
1
+ import {Actor, AuditChange, Auditor} from "anbaric-tsapi";
2
2
 
3
3
  class ConsoleAuditor implements Auditor {
4
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)}`);
5
+ async audit(jobId : string, actor : Actor, change : AuditChange,
6
+ changeDescription : string, details : any) : Promise<void> {
7
+ console.log(`[${jobId}] ${actor.id} ${change} ${changeDescription} ${JSON.stringify(details ?? null)}`);
7
8
  }
8
9
 
9
10
  }