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
|
+
"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.
|
|
19
|
-
"anbaric-tsapi": "^1.
|
|
18
|
+
"anbaric-cloud": "^1.4.0",
|
|
19
|
+
"anbaric-tsapi": "^1.4.0"
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
22
|
"src"
|
package/src/StateMachine.ts
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
|
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
|
|
6
|
-
|
|
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
|
}
|