anbaric-tsapi 1.20.0 → 1.21.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 +1 -1
- package/src/api/auditing/Auditor.ts +2 -2
- package/src/api/auditing/NoOpAuditor.ts +2 -2
- package/src/api/cloud/AppAware.ts +14 -0
- package/src/api/cloud/AuditRecord.ts +1 -0
- package/src/api/cloud/QueueMessage.ts +1 -0
- package/src/api/cloud/WorkflowDefinition.ts +3 -1
- package/src/api/documents/JsonStore.ts +6 -5
- package/src/api/jobs/Job.ts +6 -1
- package/src/api/jobs/JobPersistence.ts +10 -8
- package/src/api/jobs/JobSerialization.ts +3 -1
- package/src/api/secrets/SecretStore.ts +6 -5
- package/src/api/sql/SqlStore.ts +3 -2
- package/src/api/state-machine/Consumer.ts +1 -1
- package/src/api/state-machine/Queue.ts +2 -2
- package/src/index.ts +1 -0
package/package.json
CHANGED
|
@@ -5,8 +5,8 @@ import {Actor} from "../actors/Actor";
|
|
|
5
5
|
defines no vocabulary. */
|
|
6
6
|
interface Auditor {
|
|
7
7
|
|
|
8
|
-
audit(resourceType : string, resourceId : string, actor : Actor,
|
|
9
|
-
description : string, details : any) : Promise<void>;
|
|
8
|
+
audit(appId : string | undefined, resourceType : string, resourceId : string, actor : Actor,
|
|
9
|
+
interaction : Array<string>, description : string, details : any) : Promise<void>;
|
|
10
10
|
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -6,8 +6,8 @@ import {Auditor} from "./Auditor";
|
|
|
6
6
|
already audited the interaction. */
|
|
7
7
|
class NoOpAuditor implements Auditor {
|
|
8
8
|
|
|
9
|
-
async audit(_resourceType : string, _resourceId : string, _actor : Actor,
|
|
10
|
-
_description : string, _details : any) : Promise<void> {
|
|
9
|
+
async audit(_appId : string | undefined, _resourceType : string, _resourceId : string, _actor : Actor,
|
|
10
|
+
_interaction : Array<string>, _description : string, _details : any) : Promise<void> {
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* A service that knows which deployed app it belongs to. The id always comes
|
|
2
|
+
from the environment (ANBARIC_APP_ID), never a constructor argument, so a
|
|
3
|
+
developer never has to supply it. Hosted services (the state machine, the
|
|
4
|
+
cloud/Postgres stores) implement this; purely local in-memory ones do not. */
|
|
5
|
+
interface AppAware {
|
|
6
|
+
getAppId() : string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// The current app's id from the environment, or "" when running outside a
|
|
10
|
+
// deployed app (locally). The single source for every AppAware implementation.
|
|
11
|
+
const currentAppId = () : string => process.env.ANBARIC_APP_ID ?? "";
|
|
12
|
+
|
|
13
|
+
export { currentAppId };
|
|
14
|
+
export type { AppAware };
|
|
@@ -9,6 +9,7 @@ import {Await} from "../actions/Await";
|
|
|
9
9
|
not part of the graph - awaiting jobs are surfaced separately. */
|
|
10
10
|
type WorkflowDefinition = {
|
|
11
11
|
|
|
12
|
+
appId? : string,
|
|
12
13
|
workflowId : string,
|
|
13
14
|
startState : string,
|
|
14
15
|
dataSchema : Array<{ id : string, required : boolean }>,
|
|
@@ -23,8 +24,9 @@ type WorkflowDefinition = {
|
|
|
23
24
|
|
|
24
25
|
namespace WorkflowDefinition {
|
|
25
26
|
|
|
26
|
-
export const describe = (workflowId : string, startState : string,
|
|
27
|
+
export const describe = (appId : string | undefined, workflowId : string, startState : string,
|
|
27
28
|
states : Array<State>, dataSchema : Array<PropertyDefinition>) : WorkflowDefinition => ({
|
|
29
|
+
appId,
|
|
28
30
|
workflowId,
|
|
29
31
|
startState,
|
|
30
32
|
dataSchema: dataSchema.map(property => ({ id: property.id, required: property.required })),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {Actor} from "../actors/Actor";
|
|
2
2
|
import {Auditor} from "../auditing/Auditor";
|
|
3
|
+
import {currentAppId} from "../cloud/AppAware";
|
|
3
4
|
|
|
4
5
|
/* A schema-validated JSON document store that audits every interaction, keyed
|
|
5
6
|
by collection and id. Public methods audit then defer to ...Internal. */
|
|
@@ -9,27 +10,27 @@ abstract class JsonStore {
|
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
async create(actor : Actor, id : string, document : any) : Promise<void> {
|
|
12
|
-
await this.auditor.audit("document", `${this.collection}/${id}`, actor, [JsonStore.Interaction.CREATE], "Document created", document);
|
|
13
|
+
await this.auditor.audit(currentAppId(), "document", `${this.collection}/${id}`, actor, [JsonStore.Interaction.CREATE], "Document created", document);
|
|
13
14
|
await this.saveInternal(id, document);
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
async save(actor : Actor, changeDescription : string, id : string, document : any) : Promise<void> {
|
|
17
|
-
await this.auditor.audit("document", `${this.collection}/${id}`, actor, [JsonStore.Interaction.SAVE], changeDescription, document);
|
|
18
|
+
await this.auditor.audit(currentAppId(), "document", `${this.collection}/${id}`, actor, [JsonStore.Interaction.SAVE], changeDescription, document);
|
|
18
19
|
await this.saveInternal(id, document);
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
async retrieve(id : string, actor : Actor) : Promise<any> {
|
|
22
|
-
await this.auditor.audit("document", `${this.collection}/${id}`, actor, [JsonStore.Interaction.READ], "", null);
|
|
23
|
+
await this.auditor.audit(currentAppId(), "document", `${this.collection}/${id}`, actor, [JsonStore.Interaction.READ], "", null);
|
|
23
24
|
return this.retrieveInternal(id);
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
async delete(id : string, actor : Actor) : Promise<void> {
|
|
27
|
-
await this.auditor.audit("document", `${this.collection}/${id}`, actor, [JsonStore.Interaction.DELETE], "", null);
|
|
28
|
+
await this.auditor.audit(currentAppId(), "document", `${this.collection}/${id}`, actor, [JsonStore.Interaction.DELETE], "", null);
|
|
28
29
|
await this.deleteInternal(id);
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
async list(actor : Actor, pageSize? : number, page? : number) : Promise<Array<any>> {
|
|
32
|
-
await this.auditor.audit("document", `${this.collection}/*`, actor, [JsonStore.Interaction.LIST], "", null);
|
|
33
|
+
await this.auditor.audit(currentAppId(), "document", `${this.collection}/*`, actor, [JsonStore.Interaction.LIST], "", null);
|
|
33
34
|
return this.listInternal(pageSize, page);
|
|
34
35
|
}
|
|
35
36
|
|
package/src/api/jobs/Job.ts
CHANGED
|
@@ -5,6 +5,10 @@ class Job {
|
|
|
5
5
|
readonly id : string;
|
|
6
6
|
readonly state: string;
|
|
7
7
|
readonly properties : Map<string, any>;
|
|
8
|
+
// A workflow is identified by the composite (appId, workflowId): the app the
|
|
9
|
+
// job's state machine was deployed in, and the machine's own id. appId is
|
|
10
|
+
// undefined when running outside a deployed app (locally).
|
|
11
|
+
readonly appId? : string;
|
|
8
12
|
readonly workflowId? : string;
|
|
9
13
|
readonly startedAt : Date;
|
|
10
14
|
readonly startedBy : string;
|
|
@@ -20,7 +24,7 @@ class Job {
|
|
|
20
24
|
awaitMetadata? : WaitForInput;
|
|
21
25
|
|
|
22
26
|
constructor(id : string, properties : Map<string, any> = new Map(), state: string, workflowId? : string,
|
|
23
|
-
startedBy : string = "system", startedAt : Date = new Date(),
|
|
27
|
+
appId? : string, startedBy : string = "system", startedAt : Date = new Date(),
|
|
24
28
|
lastUpdated : Date = startedAt, killed : boolean = false,
|
|
25
29
|
status : string = Job.Status.ACTIVE, awaitMetadata? : WaitForInput,
|
|
26
30
|
waitingFor? : string) {
|
|
@@ -29,6 +33,7 @@ class Job {
|
|
|
29
33
|
this.properties = properties;
|
|
30
34
|
this.state = state;
|
|
31
35
|
this.workflowId = workflowId;
|
|
36
|
+
this.appId = appId;
|
|
32
37
|
this.startedBy = startedBy;
|
|
33
38
|
this.startedAt = startedAt;
|
|
34
39
|
this.lastUpdated = lastUpdated;
|
|
@@ -2,6 +2,7 @@ import {Actor} from "../actors/Actor";
|
|
|
2
2
|
import {Auditor} from "../auditing/Auditor";
|
|
3
3
|
import {Job} from "./Job";
|
|
4
4
|
import {serializeWaitForInput} from "../actions/WaitForInput";
|
|
5
|
+
import {currentAppId} from "../cloud/AppAware";
|
|
5
6
|
|
|
6
7
|
/* Persists jobs and audits every interaction. Public methods record the
|
|
7
8
|
interaction against the injected auditor and then defer to the abstract
|
|
@@ -14,7 +15,7 @@ abstract class JobPersistence {
|
|
|
14
15
|
async create(actor : Actor,
|
|
15
16
|
job : Job) : Promise<void> {
|
|
16
17
|
|
|
17
|
-
await this.auditor.audit("job", job.id, actor, [JobPersistence.Interaction.CREATE], "Job created", job);
|
|
18
|
+
await this.auditor.audit(job.appId, "job", job.id, actor, [JobPersistence.Interaction.CREATE], "Job created", job);
|
|
18
19
|
|
|
19
20
|
this.saveInternal(job);
|
|
20
21
|
}
|
|
@@ -38,13 +39,14 @@ abstract class JobPersistence {
|
|
|
38
39
|
change.metadata = serializeWaitForInput(job.awaitMetadata);
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
await this.auditor.audit("job", job.id, actor, interaction, changeDescription, change);
|
|
42
|
+
await this.auditor.audit(job.appId, "job", job.id, actor, interaction, changeDescription, change);
|
|
42
43
|
|
|
43
44
|
const updatedJob = new Job(
|
|
44
45
|
job.id,
|
|
45
46
|
properties ? this.updateProperties(job.properties, properties) : job.properties,
|
|
46
47
|
state ? state : job.state,
|
|
47
48
|
job.workflowId,
|
|
49
|
+
job.appId,
|
|
48
50
|
job.startedBy,
|
|
49
51
|
job.startedAt,
|
|
50
52
|
new Date(),
|
|
@@ -58,18 +60,18 @@ abstract class JobPersistence {
|
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
async kill(id : string, actor : Actor) : Promise<void> {
|
|
61
|
-
await this.auditor.audit("job", id, actor, [JobPersistence.Interaction.KILL], "Job killed", null);
|
|
63
|
+
await this.auditor.audit(currentAppId(), "job", id, actor, [JobPersistence.Interaction.KILL], "Job killed", null);
|
|
62
64
|
await this.killInternal(id);
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
async killOlderThan(lastUpdatedBefore : Date, actor : Actor) : Promise<number> {
|
|
66
|
-
await this.auditor.audit("job", "*", actor, [JobPersistence.Interaction.KILL],
|
|
68
|
+
await this.auditor.audit(currentAppId(), "job", "*", actor, [JobPersistence.Interaction.KILL],
|
|
67
69
|
`Jobs not updated since ${lastUpdatedBefore.toISOString()} killed`, null);
|
|
68
70
|
return this.killOlderThanInternal(lastUpdatedBefore);
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
async countByState(actor : Actor) : Promise<Array<JobPersistence.StateCount>> {
|
|
72
|
-
await this.auditor.audit("job", "*", actor, [JobPersistence.Interaction.LIST], "", null);
|
|
74
|
+
await this.auditor.audit(currentAppId(), "job", "*", actor, [JobPersistence.Interaction.LIST], "", null);
|
|
73
75
|
return this.countByStateInternal();
|
|
74
76
|
}
|
|
75
77
|
|
|
@@ -91,17 +93,17 @@ abstract class JobPersistence {
|
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
async retrieve(id : string, actor : Actor) : Promise<Job> {
|
|
94
|
-
await this.auditor.audit("job", id, actor, [JobPersistence.Interaction.READ], "", null);
|
|
96
|
+
await this.auditor.audit(currentAppId(), "job", id, actor, [JobPersistence.Interaction.READ], "", null);
|
|
95
97
|
return this.retrieveInternal(id);
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
async delete(id : string, actor : Actor) : Promise<void> {
|
|
99
|
-
await this.auditor.audit("job", id, actor, [JobPersistence.Interaction.DELETE], "", null);
|
|
101
|
+
await this.auditor.audit(currentAppId(), "job", id, actor, [JobPersistence.Interaction.DELETE], "", null);
|
|
100
102
|
await this.deleteInternal(id);
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
async list(actor : Actor, pageSize? : number, page? : number) : Promise<Array<Job>> {
|
|
104
|
-
await this.auditor.audit("job", "*", actor, [JobPersistence.Interaction.LIST], "", null);
|
|
106
|
+
await this.auditor.audit(currentAppId(), "job", "*", actor, [JobPersistence.Interaction.LIST], "", null);
|
|
105
107
|
return this.listInternal(pageSize, page);
|
|
106
108
|
}
|
|
107
109
|
|
|
@@ -6,6 +6,7 @@ type SerializedJob = {
|
|
|
6
6
|
state : string,
|
|
7
7
|
properties : Record<string, any>,
|
|
8
8
|
workflowId? : string,
|
|
9
|
+
appId? : string,
|
|
9
10
|
startedAt? : string,
|
|
10
11
|
startedBy? : string,
|
|
11
12
|
lastUpdated? : string,
|
|
@@ -20,6 +21,7 @@ const serializeJob = (job : Job) : SerializedJob => ({
|
|
|
20
21
|
state: job.state,
|
|
21
22
|
properties: Object.fromEntries(job.properties),
|
|
22
23
|
workflowId: job.workflowId,
|
|
24
|
+
appId: job.appId,
|
|
23
25
|
startedAt: job.startedAt.toISOString(),
|
|
24
26
|
startedBy: job.startedBy,
|
|
25
27
|
lastUpdated: job.lastUpdated.toISOString(),
|
|
@@ -32,7 +34,7 @@ const serializeJob = (job : Job) : SerializedJob => ({
|
|
|
32
34
|
const deserializeJob = (serialized : SerializedJob) : Job => {
|
|
33
35
|
const startedAt = serialized.startedAt ? new Date(serialized.startedAt) : new Date();
|
|
34
36
|
return new Job(serialized.id, new Map(Object.entries(serialized.properties)), serialized.state,
|
|
35
|
-
serialized.workflowId, serialized.startedBy ?? "system", startedAt,
|
|
37
|
+
serialized.workflowId, serialized.appId, serialized.startedBy ?? "system", startedAt,
|
|
36
38
|
serialized.lastUpdated ? new Date(serialized.lastUpdated) : startedAt, serialized.killed ?? false,
|
|
37
39
|
serialized.status ?? Job.Status.ACTIVE,
|
|
38
40
|
serialized.awaitMetadata ? deserializeWaitForInput(serialized.awaitMetadata) : undefined,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {Actor} from "../actors/Actor";
|
|
2
2
|
import {Auditor} from "../auditing/Auditor";
|
|
3
|
+
import {currentAppId} from "../cloud/AppAware";
|
|
3
4
|
|
|
4
5
|
/* Stores named secrets and audits every interaction. The secret value is
|
|
5
6
|
never included in an audit record. Public methods audit then defer to the
|
|
@@ -10,27 +11,27 @@ abstract class SecretStore {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
async create(actor : Actor, name : string, value : string) : Promise<void> {
|
|
13
|
-
await this.auditor.audit("secret", name, actor, [SecretStore.Interaction.CREATE], "Secret created", null);
|
|
14
|
+
await this.auditor.audit(currentAppId(), "secret", name, actor, [SecretStore.Interaction.CREATE], "Secret created", null);
|
|
14
15
|
await this.saveInternal(name, value);
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
async save(actor : Actor, changeDescription : string, name : string, value : string) : Promise<void> {
|
|
18
|
-
await this.auditor.audit("secret", name, actor, [SecretStore.Interaction.SAVE], changeDescription, null);
|
|
19
|
+
await this.auditor.audit(currentAppId(), "secret", name, actor, [SecretStore.Interaction.SAVE], changeDescription, null);
|
|
19
20
|
await this.saveInternal(name, value);
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
async retrieve(name : string, actor : Actor) : Promise<string> {
|
|
23
|
-
await this.auditor.audit("secret", name, actor, [SecretStore.Interaction.READ], "", null);
|
|
24
|
+
await this.auditor.audit(currentAppId(), "secret", name, actor, [SecretStore.Interaction.READ], "", null);
|
|
24
25
|
return this.retrieveInternal(name);
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
async delete(name : string, actor : Actor) : Promise<void> {
|
|
28
|
-
await this.auditor.audit("secret", name, actor, [SecretStore.Interaction.DELETE], "", null);
|
|
29
|
+
await this.auditor.audit(currentAppId(), "secret", name, actor, [SecretStore.Interaction.DELETE], "", null);
|
|
29
30
|
await this.deleteInternal(name);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
async list(actor : Actor) : Promise<Array<string>> {
|
|
33
|
-
await this.auditor.audit("secret", "*", actor, [SecretStore.Interaction.LIST], "", null);
|
|
34
|
+
await this.auditor.audit(currentAppId(), "secret", "*", actor, [SecretStore.Interaction.LIST], "", null);
|
|
34
35
|
return this.listInternal();
|
|
35
36
|
}
|
|
36
37
|
|
package/src/api/sql/SqlStore.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {Actor} from "../actors/Actor";
|
|
2
2
|
import {Auditor} from "../auditing/Auditor";
|
|
3
|
+
import {currentAppId} from "../cloud/AppAware";
|
|
3
4
|
|
|
4
5
|
/* A relational (SQL) store for application data. Public methods audit the
|
|
5
6
|
interaction then defer to the abstract ...Internal. `query` returns rows;
|
|
@@ -12,12 +13,12 @@ abstract class SqlStore {
|
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
async query(actor : Actor, sql : string, parameters : Array<any> = []) : Promise<Array<Record<string, any>>> {
|
|
15
|
-
await this.auditor.audit("sql", "*", actor, [SqlStore.Interaction.QUERY], sql, null);
|
|
16
|
+
await this.auditor.audit(currentAppId(), "sql", "*", actor, [SqlStore.Interaction.QUERY], sql, null);
|
|
16
17
|
return this.queryInternal(sql, parameters);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
async execute(actor : Actor, sql : string, parameters : Array<any> = []) : Promise<number> {
|
|
20
|
-
await this.auditor.audit("sql", "*", actor, [SqlStore.Interaction.EXECUTE], sql, null);
|
|
21
|
+
await this.auditor.audit(currentAppId(), "sql", "*", actor, [SqlStore.Interaction.EXECUTE], sql, null);
|
|
21
22
|
return this.executeInternal(sql, parameters);
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -2,8 +2,8 @@ import {QueueMessage} from "../cloud/QueueMessage";
|
|
|
2
2
|
|
|
3
3
|
interface Queue {
|
|
4
4
|
|
|
5
|
-
enqueue(jobId : string, workflowId : string) : Promise<void>;
|
|
6
|
-
schedule(jobId : string, workflowId : string, due : Date) : Promise<void>;
|
|
5
|
+
enqueue(jobId : string, appId : string | undefined, workflowId : string) : Promise<void>;
|
|
6
|
+
schedule(jobId : string, appId : string | undefined, workflowId : string, due : Date) : Promise<void>;
|
|
7
7
|
|
|
8
8
|
}
|
|
9
9
|
|
package/src/index.ts
CHANGED