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.
|
|
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.
|
|
19
|
-
"anbaric-tsapi": "^1.
|
|
18
|
+
"anbaric-cloud": "^1.3.0",
|
|
19
|
+
"anbaric-tsapi": "^1.3.0"
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
22
|
"src"
|
package/src/StateMachine.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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<
|
|
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
|
|
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/
|
|
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";
|
package/src/auditing/Auditor.ts
DELETED
|
@@ -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 }
|