anbaric-cloud-hosting 1.7.0 → 2.0.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-cloud-hosting",
3
- "version": "1.7.0",
3
+ "version": "2.0.0",
4
4
  "description": "Anbaric Cloud hosting service: Postgres-backed job persistence and queuing exposed over an HTTP API",
5
5
  "license": "MIT",
6
6
  "author": "chris@anbaric.ai",
@@ -17,17 +17,17 @@
17
17
  "@aws-sdk/client-s3": "^3.1110.0",
18
18
  "@aws-sdk/client-secrets-manager": "^3.700.0",
19
19
  "@aws-sdk/client-servicediscovery": "^3.1110.0",
20
- "anbaric-data-store": "^1.7.0",
21
- "anbaric-plugins": "^1.7.0",
22
- "anbaric-tsapi": "^1.7.0",
20
+ "anbaric-data-store": "^2.0.0",
21
+ "anbaric-plugins": "^2.0.0",
22
+ "anbaric-tsapi": "^2.0.0",
23
23
  "esbuild": "^0.28.2",
24
24
  "pg": "^8.16.0"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^26.2.0",
28
28
  "@types/pg": "^8.15.0",
29
- "anbaric-impl-cloud": "^1.7.0",
30
- "anbaric-state-machine": "^1.7.0",
29
+ "anbaric-impl-cloud": "^2.0.0",
30
+ "anbaric-state-machine": "^2.0.0",
31
31
  "tsx": "^4.20.0",
32
32
  "typescript": "^7.0.2"
33
33
  },
@@ -1,9 +1,10 @@
1
- import {AuditChange, AuditRecord} from "anbaric-tsapi";
1
+ import {AuditInteraction, AuditRecord} from "anbaric-tsapi";
2
2
 
3
3
  type AuditFilter = {
4
- jobId? : string,
4
+ resourceType? : string,
5
+ resourceId? : string,
5
6
  actorId? : string,
6
- change? : AuditChange,
7
+ interaction? : AuditInteraction,
7
8
  search? : string,
8
9
  pageSize? : number,
9
10
  page? : number,
@@ -15,9 +15,10 @@ class InMemoryAuditRecordStore implements AuditRecordStore {
15
15
  const page = filter.page ?? 0;
16
16
 
17
17
  return this.records
18
- .filter(record => !filter.jobId || record.jobId === filter.jobId)
18
+ .filter(record => !filter.resourceType || record.resourceType === filter.resourceType)
19
+ .filter(record => !filter.resourceId || record.resourceId === filter.resourceId)
19
20
  .filter(record => !filter.actorId || record.actorId === filter.actorId)
20
- .filter(record => !filter.change || record.change === filter.change)
21
+ .filter(record => !filter.interaction || record.interaction.includes(filter.interaction))
21
22
  .filter(record => !filter.search ||
22
23
  record.description.toLowerCase().includes(filter.search.toLowerCase()))
23
24
  .reverse()
@@ -1,16 +1,37 @@
1
- import {AuditRecord} from "anbaric-tsapi";
1
+ import {AuditInteraction, AuditRecord} from "anbaric-tsapi";
2
2
  import {Pool} from "pg";
3
3
  import {AuditFilter, AuditRecordStore} from "../auditing/AuditRecordStore";
4
4
 
5
+ const DEFAULT_WRITE_MASK : Array<AuditInteraction> = [
6
+ AuditInteraction.CREATE, AuditInteraction.UPDATE_PROPERTIES, AuditInteraction.CHANGE_STATE, AuditInteraction.DELETE,
7
+ ];
8
+
9
+ /* Persists audit records, subject to a write mask: only the interactions in
10
+ the mask are stored, so high-volume reads can be audited at the call site
11
+ yet kept out of the durable trail. The mask defaults to every write and is
12
+ overridable via ANBARIC_AUDIT_INTERACTIONS. */
5
13
  class PostgresAuditRecordStore implements AuditRecordStore {
6
14
 
7
- constructor(private pool : Pool) {}
15
+ private writeMask : Set<AuditInteraction>;
16
+
17
+ constructor(private pool : Pool, writeMask : Set<AuditInteraction> = PostgresAuditRecordStore.maskFromEnvironment()) {
18
+ this.writeMask = writeMask;
19
+ }
20
+
21
+ static maskFromEnvironment() : Set<AuditInteraction> {
22
+ const configured = process.env.ANBARIC_AUDIT_INTERACTIONS;
23
+ if (!configured) return new Set(DEFAULT_WRITE_MASK);
24
+ return new Set(configured.split(",").map(entry => entry.trim()).filter(Boolean) as Array<AuditInteraction>);
25
+ }
8
26
 
9
27
  async save(record : AuditRecord) : Promise<void> {
28
+ if (!record.interaction.some(interaction => this.writeMask.has(interaction))) return;
29
+
10
30
  await this.pool.query(
11
- `INSERT INTO anbaric_system.audit_records (job_id, actor_id, actor_type, change, description, details)
12
- VALUES ($1, $2, $3, $4, $5, $6)`,
13
- [record.jobId, record.actorId, record.actorType, record.change,
31
+ `INSERT INTO anbaric_system.audit_records
32
+ (resource_type, resource_id, actor_id, actor_type, interaction, description, details)
33
+ VALUES ($1, $2, $3, $4, $5::anbaric_system.audit_interaction[], $6, $7)`,
34
+ [record.resourceType, record.resourceId, record.actorId, record.actorType, record.interaction,
14
35
  record.description, JSON.stringify(record.details ?? null)],
15
36
  );
16
37
  }
@@ -19,17 +40,21 @@ class PostgresAuditRecordStore implements AuditRecordStore {
19
40
  const conditions : Array<string> = [];
20
41
  const parameters : Array<any> = [];
21
42
 
22
- if (filter.jobId) {
23
- parameters.push(filter.jobId);
24
- conditions.push(`job_id = $${parameters.length}`);
43
+ if (filter.resourceType) {
44
+ parameters.push(filter.resourceType);
45
+ conditions.push(`resource_type = $${parameters.length}`);
46
+ }
47
+ if (filter.resourceId) {
48
+ parameters.push(filter.resourceId);
49
+ conditions.push(`resource_id = $${parameters.length}`);
25
50
  }
26
51
  if (filter.actorId) {
27
52
  parameters.push(filter.actorId);
28
53
  conditions.push(`actor_id = $${parameters.length}`);
29
54
  }
30
- if (filter.change) {
31
- parameters.push(filter.change);
32
- conditions.push(`change = $${parameters.length}::anbaric_system.audit_change`);
55
+ if (filter.interaction) {
56
+ parameters.push(filter.interaction);
57
+ conditions.push(`$${parameters.length}::anbaric_system.audit_interaction = ANY(interaction)`);
33
58
  }
34
59
  if (filter.search) {
35
60
  parameters.push(`%${filter.search}%`);
@@ -43,7 +68,7 @@ class PostgresAuditRecordStore implements AuditRecordStore {
43
68
  const offset = `OFFSET $${parameters.length}`;
44
69
 
45
70
  const result = await this.pool.query(
46
- `SELECT id, job_id, actor_id, actor_type, change, description, details, at
71
+ `SELECT id, resource_type, resource_id, actor_id, actor_type, interaction, description, details, at
47
72
  FROM anbaric_system.audit_records ${where}
48
73
  ORDER BY at DESC, id DESC ${limit} ${offset}`,
49
74
  parameters,
@@ -51,10 +76,11 @@ class PostgresAuditRecordStore implements AuditRecordStore {
51
76
 
52
77
  return result.rows.map(row => ({
53
78
  id: String(row.id),
54
- jobId: row.job_id,
79
+ resourceType: row.resource_type,
80
+ resourceId: row.resource_id,
55
81
  actorId: row.actor_id,
56
82
  actorType: row.actor_type,
57
- change: row.change,
83
+ interaction: row.interaction,
58
84
  description: row.description,
59
85
  details: row.details,
60
86
  at: row.at.toISOString(),
@@ -1,4 +1,4 @@
1
- import {Job, JobPersistence, JobTransition, deserializeJob} from "anbaric-tsapi";
1
+ import {Auditor, Job, JobPersistence, NoOpAuditor, deserializeJob} from "anbaric-tsapi";
2
2
  import {Pool} from "pg";
3
3
 
4
4
  type JobRow = {
@@ -9,28 +9,28 @@ type JobRow = {
9
9
  started_at : Date,
10
10
  started_by : string,
11
11
  last_updated : Date,
12
- transitions : Array<JobTransition>,
13
12
  };
14
13
 
15
- const JOB_COLUMNS = "id, state, properties, workflow_id, started_at, started_by, last_updated, transitions";
14
+ const JOB_COLUMNS = "id, state, properties, workflow_id, started_at, started_by, last_updated";
16
15
 
17
- class PostgresJobPersistence implements JobPersistence {
16
+ class PostgresJobPersistence extends JobPersistence {
18
17
 
19
- constructor(private pool : Pool) {}
18
+ constructor(private pool : Pool, auditor : Auditor = new NoOpAuditor()) {
19
+ super(auditor);
20
+ }
20
21
 
21
- async save(job : Job) : Promise<void> {
22
+ protected async saveInternal(job : Job) : Promise<void> {
22
23
  await this.pool.query(
23
- `INSERT INTO jobs (id, state, properties, workflow_id, started_at, started_by, last_updated, transitions)
24
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
24
+ `INSERT INTO jobs (id, state, properties, workflow_id, started_at, started_by, last_updated)
25
+ VALUES ($1, $2, $3, $4, $5, $6, $7)
25
26
  ON CONFLICT (id) DO UPDATE SET state = EXCLUDED.state, properties = EXCLUDED.properties,
26
- workflow_id = EXCLUDED.workflow_id, last_updated = EXCLUDED.last_updated,
27
- transitions = EXCLUDED.transitions`,
28
- [job.id, job.stateId, Object.fromEntries(job.properties), job.workflowId,
29
- job.startedAt, job.startedBy, job.lastUpdated, JSON.stringify(job.transitions)],
27
+ workflow_id = EXCLUDED.workflow_id, last_updated = EXCLUDED.last_updated`,
28
+ [job.id, job.state, Object.fromEntries(job.properties), job.workflowId,
29
+ job.startedAt, job.startedBy, job.lastUpdated],
30
30
  );
31
31
  }
32
32
 
33
- async retrieve(id : string) : Promise<Job> {
33
+ protected async retrieveInternal(id : string) : Promise<Job> {
34
34
  const result = await this.pool.query(
35
35
  `SELECT ${JOB_COLUMNS} FROM jobs WHERE id = $1`,
36
36
  [id],
@@ -40,11 +40,11 @@ class PostgresJobPersistence implements JobPersistence {
40
40
  return this.deserializeRow(result.rows[0]);
41
41
  }
42
42
 
43
- async delete(id : string) : Promise<void> {
43
+ protected async deleteInternal(id : string) : Promise<void> {
44
44
  await this.pool.query("DELETE FROM jobs WHERE id = $1", [id]);
45
45
  }
46
46
 
47
- async list(pageSize : number = 100, page : number = 0) : Promise<Array<Job>> {
47
+ protected async listInternal(pageSize : number = 100, page : number = 0) : Promise<Array<Job>> {
48
48
  const result = await this.pool.query(
49
49
  `SELECT ${JOB_COLUMNS} FROM jobs ORDER BY inserted_at LIMIT $1 OFFSET $2`,
50
50
  [pageSize, page * pageSize],
@@ -60,18 +60,9 @@ class PostgresJobPersistence implements JobPersistence {
60
60
  startedAt: row.started_at.toISOString(),
61
61
  startedBy: row.started_by,
62
62
  lastUpdated: row.last_updated.toISOString(),
63
- transitions: row.transitions,
64
63
  });
65
64
  }
66
65
 
67
- async updateProperties(id : string, properties : Map<string, any>) : Promise<void> {
68
- const result = await this.pool.query(
69
- "UPDATE jobs SET properties = properties || $2::jsonb, last_updated = now() WHERE id = $1",
70
- [id, Object.fromEntries(properties)],
71
- );
72
- if (result.rowCount === 0) throw new Error(`No job found with id "${id}"`);
73
- }
74
-
75
66
  }
76
67
 
77
68
  export { PostgresJobPersistence }
@@ -1,11 +1,13 @@
1
- import {JsonStore} from "anbaric-tsapi";
1
+ import {Auditor, JsonStore, NoOpAuditor} from "anbaric-tsapi";
2
2
  import {Pool} from "pg";
3
3
 
4
- class PostgresJsonStore implements JsonStore {
4
+ class PostgresJsonStore extends JsonStore {
5
5
 
6
- constructor(private pool : Pool, private collection : string) {}
6
+ constructor(private pool : Pool, collection : string, auditor : Auditor = new NoOpAuditor()) {
7
+ super(auditor, collection);
8
+ }
7
9
 
8
- async save(id : string, document : any) : Promise<void> {
10
+ protected async saveInternal(id : string, document : any) : Promise<void> {
9
11
  await this.pool.query(
10
12
  `INSERT INTO documents (collection, id, document) VALUES ($1, $2, $3)
11
13
  ON CONFLICT (collection, id) DO UPDATE SET document = EXCLUDED.document`,
@@ -13,7 +15,7 @@ class PostgresJsonStore implements JsonStore {
13
15
  );
14
16
  }
15
17
 
16
- async retrieve(id : string) : Promise<any> {
18
+ protected async retrieveInternal(id : string) : Promise<any> {
17
19
  const result = await this.pool.query(
18
20
  "SELECT document FROM documents WHERE collection = $1 AND id = $2",
19
21
  [this.collection, id],
@@ -23,11 +25,11 @@ class PostgresJsonStore implements JsonStore {
23
25
  return result.rows[0].document;
24
26
  }
25
27
 
26
- async delete(id : string) : Promise<void> {
28
+ protected async deleteInternal(id : string) : Promise<void> {
27
29
  await this.pool.query("DELETE FROM documents WHERE collection = $1 AND id = $2", [this.collection, id]);
28
30
  }
29
31
 
30
- async list(pageSize : number = 100, page : number = 0) : Promise<Array<any>> {
32
+ protected async listInternal(pageSize : number = 100, page : number = 0) : Promise<Array<any>> {
31
33
  const result = await this.pool.query(
32
34
  "SELECT document FROM documents WHERE collection = $1 ORDER BY inserted_at LIMIT $2 OFFSET $3",
33
35
  [this.collection, pageSize, page * pageSize],
@@ -54,30 +54,59 @@ const ensureSchema = async (pool : Pool) : Promise<void> => {
54
54
  await pool.query("ALTER TABLE anbaric_system.cli_keys ADD COLUMN IF NOT EXISTS tenant TEXT");
55
55
  await pool.query(`
56
56
  DO $$ BEGIN
57
- CREATE TYPE anbaric_system.audit_change AS ENUM ('CREATE', 'UPDATE_PROPERTIES', 'CHANGE_STATE', 'DELETE');
57
+ CREATE TYPE anbaric_system.audit_interaction AS ENUM
58
+ ('CREATE', 'UPDATE_PROPERTIES', 'CHANGE_STATE', 'DELETE', 'READ', 'LIST');
58
59
  EXCEPTION WHEN duplicate_object THEN null;
59
60
  END $$
60
61
  `);
61
62
  await pool.query(`
62
63
  CREATE TABLE IF NOT EXISTS anbaric_system.audit_records (
63
- id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
64
- job_id TEXT NOT NULL,
65
- actor_id TEXT NOT NULL,
66
- actor_type TEXT NOT NULL,
67
- change anbaric_system.audit_change NOT NULL,
68
- description TEXT NOT NULL,
69
- details JSONB,
70
- at TIMESTAMPTZ NOT NULL DEFAULT now()
64
+ id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
65
+ resource_type TEXT NOT NULL,
66
+ resource_id TEXT NOT NULL,
67
+ actor_id TEXT NOT NULL,
68
+ actor_type TEXT NOT NULL,
69
+ interaction anbaric_system.audit_interaction[] NOT NULL,
70
+ description TEXT NOT NULL,
71
+ details JSONB,
72
+ at TIMESTAMPTZ NOT NULL DEFAULT now()
71
73
  )
72
74
  `);
73
- await pool.query("ALTER TABLE anbaric_system.audit_records ADD COLUMN IF NOT EXISTS change anbaric_system.audit_change");
74
- await pool.query("UPDATE anbaric_system.audit_records SET change = 'UPDATE_PROPERTIES' WHERE change IS NULL");
75
- await pool.query("UPDATE anbaric_system.audit_records SET actor_id = 'system' WHERE actor_id IS NULL");
76
- await pool.query("UPDATE anbaric_system.audit_records SET actor_type = 'CODE' WHERE actor_type IS NULL");
77
- await pool.query("ALTER TABLE anbaric_system.audit_records ALTER COLUMN change SET NOT NULL");
78
- await pool.query("ALTER TABLE anbaric_system.audit_records ALTER COLUMN actor_id SET NOT NULL");
79
- await pool.query("ALTER TABLE anbaric_system.audit_records ALTER COLUMN actor_type SET NOT NULL");
80
- await pool.query("CREATE INDEX IF NOT EXISTS audit_records_job_id ON anbaric_system.audit_records (job_id)");
75
+ await pool.query("ALTER TABLE anbaric_system.audit_records ADD COLUMN IF NOT EXISTS resource_type TEXT");
76
+ await pool.query("ALTER TABLE anbaric_system.audit_records ADD COLUMN IF NOT EXISTS resource_id TEXT");
77
+ await pool.query("ALTER TABLE anbaric_system.audit_records ADD COLUMN IF NOT EXISTS interaction anbaric_system.audit_interaction[]");
78
+ await pool.query(`
79
+ DO $$ BEGIN
80
+ IF EXISTS (SELECT 1 FROM information_schema.columns
81
+ WHERE table_schema = 'anbaric_system' AND table_name = 'audit_records'
82
+ AND column_name = 'interaction' AND data_type <> 'ARRAY') THEN
83
+ ALTER TABLE anbaric_system.audit_records
84
+ ALTER COLUMN interaction TYPE anbaric_system.audit_interaction[] USING ARRAY[interaction];
85
+ END IF;
86
+ END $$
87
+ `);
88
+ await pool.query(`
89
+ DO $$ BEGIN
90
+ IF EXISTS (SELECT 1 FROM information_schema.columns
91
+ WHERE table_schema = 'anbaric_system' AND table_name = 'audit_records'
92
+ AND column_name = 'job_id') THEN
93
+ UPDATE anbaric_system.audit_records
94
+ SET resource_type = COALESCE(resource_type, 'job'),
95
+ resource_id = COALESCE(resource_id, job_id),
96
+ interaction = COALESCE(interaction, ARRAY[change::text::anbaric_system.audit_interaction])
97
+ WHERE resource_id IS NULL OR interaction IS NULL;
98
+ ALTER TABLE anbaric_system.audit_records DROP COLUMN job_id;
99
+ ALTER TABLE anbaric_system.audit_records DROP COLUMN change;
100
+ END IF;
101
+ END $$
102
+ `);
103
+ await pool.query("UPDATE anbaric_system.audit_records SET resource_type = 'job' WHERE resource_type IS NULL");
104
+ await pool.query("UPDATE anbaric_system.audit_records SET resource_id = 'unknown' WHERE resource_id IS NULL");
105
+ await pool.query("UPDATE anbaric_system.audit_records SET interaction = ARRAY['UPDATE_PROPERTIES']::anbaric_system.audit_interaction[] WHERE interaction IS NULL");
106
+ await pool.query("ALTER TABLE anbaric_system.audit_records ALTER COLUMN resource_type SET NOT NULL");
107
+ await pool.query("ALTER TABLE anbaric_system.audit_records ALTER COLUMN resource_id SET NOT NULL");
108
+ await pool.query("ALTER TABLE anbaric_system.audit_records ALTER COLUMN interaction SET NOT NULL");
109
+ await pool.query("CREATE INDEX IF NOT EXISTS audit_records_resource ON anbaric_system.audit_records (resource_type, resource_id)");
81
110
  };
82
111
 
83
112
  export { ensureSchema }
@@ -8,13 +8,16 @@ import {
8
8
  ResourceNotFoundException,
9
9
  SecretsManagerClient,
10
10
  } from "@aws-sdk/client-secrets-manager";
11
- import {SecretStore} from "anbaric-tsapi";
11
+ import {Auditor, NoOpAuditor, SecretStore} from "anbaric-tsapi";
12
12
 
13
- class SecretsManagerSecretStore implements SecretStore {
13
+ class SecretsManagerSecretStore extends SecretStore {
14
14
 
15
- constructor(private client : SecretsManagerClient, private prefix : string = "anbaric/") {}
15
+ constructor(private client : SecretsManagerClient, private prefix : string = "anbaric/",
16
+ auditor : Auditor = new NoOpAuditor()) {
17
+ super(auditor);
18
+ }
16
19
 
17
- async save(name : string, value : string) : Promise<void> {
20
+ protected async saveInternal(name : string, value : string) : Promise<void> {
18
21
  try {
19
22
  await this.client.send(new CreateSecretCommand({ Name: this.prefix + name, SecretString: value }));
20
23
  } catch (error) {
@@ -23,7 +26,7 @@ class SecretsManagerSecretStore implements SecretStore {
23
26
  }
24
27
  }
25
28
 
26
- async retrieve(name : string) : Promise<string> {
29
+ protected async retrieveInternal(name : string) : Promise<string> {
27
30
  try {
28
31
  const secret = await this.client.send(new GetSecretValueCommand({ SecretId: this.prefix + name }));
29
32
  return secret.SecretString ?? "";
@@ -33,7 +36,7 @@ class SecretsManagerSecretStore implements SecretStore {
33
36
  }
34
37
  }
35
38
 
36
- async delete(name : string) : Promise<void> {
39
+ protected async deleteInternal(name : string) : Promise<void> {
37
40
  try {
38
41
  await this.client.send(new DeleteSecretCommand({ SecretId: this.prefix + name, ForceDeleteWithoutRecovery: true }));
39
42
  } catch (error) {
@@ -41,7 +44,7 @@ class SecretsManagerSecretStore implements SecretStore {
41
44
  }
42
45
  }
43
46
 
44
- async list() : Promise<Array<string>> {
47
+ protected async listInternal() : Promise<Array<string>> {
45
48
  const result = await this.client.send(new ListSecretsCommand({
46
49
  Filters: [{ Key: "name", Values: [this.prefix] }],
47
50
  }));
@@ -1,9 +1,9 @@
1
- import {AuditChange} from "anbaric-tsapi";
1
+ import {AuditInteraction} from "anbaric-tsapi";
2
2
  import {AuditRecordStore} from "../../auditing/AuditRecordStore";
3
3
  import {Request} from "../Request";
4
4
  import {RequestHandler} from "../RequestHandler";
5
5
 
6
- const AUDIT_CHANGES = new Set(["CREATE", "UPDATE_PROPERTIES", "CHANGE_STATE", "DELETE"]);
6
+ const AUDIT_INTERACTIONS = new Set(["CREATE", "UPDATE_PROPERTIES", "CHANGE_STATE", "DELETE", "READ", "LIST"]);
7
7
 
8
8
  class AuditsHandler implements RequestHandler {
9
9
 
@@ -23,21 +23,24 @@ class AuditsHandler implements RequestHandler {
23
23
 
24
24
  private async handleRecord(request : Request) : Promise<void> {
25
25
  const record = await request.body();
26
- if (typeof record?.jobId !== "string" || typeof record?.description !== "string"
27
- || typeof record?.actorId !== "string" || typeof record?.actorType !== "string"
28
- || !AUDIT_CHANGES.has(record?.change)) {
29
- return request.reply(400, { error: "Expected a body of { jobId, actorId, actorType, change, description, ... }" });
26
+ if (typeof record?.resourceType !== "string" || typeof record?.resourceId !== "string"
27
+ || typeof record?.description !== "string" || typeof record?.actorId !== "string"
28
+ || typeof record?.actorType !== "string"
29
+ || !Array.isArray(record?.interaction) || record.interaction.length === 0
30
+ || !record.interaction.every((interaction : unknown) => typeof interaction === "string" && AUDIT_INTERACTIONS.has(interaction))) {
31
+ return request.reply(400, { error: "Expected a body of { resourceType, resourceId, actorId, actorType, interaction: [...], description, ... }" });
30
32
  }
31
33
  await this.auditRecords.save(record);
32
34
  request.reply(204);
33
35
  }
34
36
 
35
37
  private async handleQuery(request : Request) : Promise<void> {
36
- const change = request.query("change");
38
+ const interaction = request.query("interaction");
37
39
  const records = await this.auditRecords.list({
38
- jobId: request.query("jobId"),
40
+ resourceType: request.query("resourceType"),
41
+ resourceId: request.query("resourceId"),
39
42
  actorId: request.query("actorId"),
40
- change: change && AUDIT_CHANGES.has(change) ? change as AuditChange : undefined,
43
+ interaction: interaction && AUDIT_INTERACTIONS.has(interaction) ? interaction as AuditInteraction : undefined,
41
44
  search: request.query("search"),
42
45
  pageSize: request.query("pageSize") === undefined ? undefined : Number(request.query("pageSize")),
43
46
  page: request.query("page") === undefined ? undefined : Number(request.query("page")),
@@ -1,4 +1,4 @@
1
- import {JsonStore} from "anbaric-tsapi";
1
+ import {JsonStore, SystemActor} from "anbaric-tsapi";
2
2
  import {Request} from "../Request";
3
3
  import {RequestHandler} from "../RequestHandler";
4
4
 
@@ -17,12 +17,12 @@ class DocumentsHandler implements RequestHandler {
17
17
  private async handleDocument(request : Request, store : JsonStore, documentId : string) : Promise<void> {
18
18
  switch (request.method) {
19
19
  case "PUT":
20
- await store.save(documentId, await request.body());
20
+ await store.create(SystemActor.actor, documentId, await request.body());
21
21
  return request.reply(204);
22
22
  case "GET":
23
- return request.reply(200, await store.retrieve(documentId));
23
+ return request.reply(200, await store.retrieve(documentId, SystemActor.actor));
24
24
  case "DELETE":
25
- await store.delete(documentId);
25
+ await store.delete(documentId, SystemActor.actor);
26
26
  return request.reply(204);
27
27
  }
28
28
  request.notFound();
@@ -33,7 +33,7 @@ class DocumentsHandler implements RequestHandler {
33
33
  case "GET": {
34
34
  const pageSize = Number(request.query("pageSize") ?? 100);
35
35
  const page = Number(request.query("page") ?? 0);
36
- return request.reply(200, await store.list(pageSize, page));
36
+ return request.reply(200, await store.list(SystemActor.actor, pageSize, page));
37
37
  }
38
38
  }
39
39
  request.notFound();
@@ -1,4 +1,4 @@
1
- import {JobPersistence, deserializeJob, serializeJob} from "anbaric-tsapi";
1
+ import {JobPersistence, SystemActor, deserializeJob, serializeJob} from "anbaric-tsapi";
2
2
  import {Request} from "../Request";
3
3
  import {RequestHandler} from "../RequestHandler";
4
4
 
@@ -7,37 +7,20 @@ class JobsHandler implements RequestHandler {
7
7
  constructor(private persistence : JobPersistence) {}
8
8
 
9
9
  async handle(request : Request) : Promise<void> {
10
- switch (request.subresource) {
11
- case "properties":
12
- if (request.id) return this.handleProperties(request, request.id);
13
- break;
14
- case undefined:
15
- if (request.id) return this.handleJob(request, request.id);
16
- return this.handleCollection(request);
17
- }
18
- request.notFound();
19
- }
20
-
21
- private async handleProperties(request : Request, id : string) : Promise<void> {
22
- switch (request.method) {
23
- case "PATCH": {
24
- const properties = new Map<string, any>(Object.entries(await request.body()));
25
- await this.persistence.updateProperties(id, properties);
26
- return request.reply(204);
27
- }
28
- }
29
- request.notFound();
10
+ if (request.subresource) return request.notFound();
11
+ if (request.id) return this.handleJob(request, request.id);
12
+ return this.handleCollection(request);
30
13
  }
31
14
 
32
15
  private async handleJob(request : Request, id : string) : Promise<void> {
33
16
  switch (request.method) {
34
17
  case "PUT":
35
- await this.persistence.save(deserializeJob(await request.body()));
18
+ await this.persistence.create(SystemActor.actor, deserializeJob(await request.body()));
36
19
  return request.reply(204);
37
20
  case "GET":
38
- return request.reply(200, serializeJob(await this.persistence.retrieve(id)));
21
+ return request.reply(200, serializeJob(await this.persistence.retrieve(id, SystemActor.actor)));
39
22
  case "DELETE":
40
- await this.persistence.delete(id);
23
+ await this.persistence.delete(id, SystemActor.actor);
41
24
  return request.reply(204);
42
25
  }
43
26
  request.notFound();
@@ -48,7 +31,7 @@ class JobsHandler implements RequestHandler {
48
31
  case "GET": {
49
32
  const pageSize = Number(request.query("pageSize") ?? 100);
50
33
  const page = Number(request.query("page") ?? 0);
51
- const jobs = await this.persistence.list(pageSize, page);
34
+ const jobs = await this.persistence.list(SystemActor.actor, pageSize, page);
52
35
  return request.reply(200, jobs.map(serializeJob));
53
36
  }
54
37
  }
@@ -1,4 +1,4 @@
1
- import {SecretStore} from "anbaric-tsapi";
1
+ import {SecretStore, SystemActor} from "anbaric-tsapi";
2
2
  import {Request} from "../Request";
3
3
  import {RequestHandler} from "../RequestHandler";
4
4
 
@@ -17,13 +17,13 @@ class SecretsHandler implements RequestHandler {
17
17
  case "PUT": {
18
18
  const { value } = await request.body();
19
19
  if (typeof value !== "string") return request.reply(400, { error: "Expected a body of { value : string }" });
20
- await this.secretStore.save(name, value);
20
+ await this.secretStore.create(SystemActor.actor, name, value);
21
21
  return request.reply(204);
22
22
  }
23
23
  case "GET":
24
- return request.reply(200, { value: await this.secretStore.retrieve(name) });
24
+ return request.reply(200, { value: await this.secretStore.retrieve(name, SystemActor.actor) });
25
25
  case "DELETE":
26
- await this.secretStore.delete(name);
26
+ await this.secretStore.delete(name, SystemActor.actor);
27
27
  return request.reply(204);
28
28
  }
29
29
  request.notFound();
@@ -32,7 +32,7 @@ class SecretsHandler implements RequestHandler {
32
32
  private async handleCollection(request : Request) : Promise<void> {
33
33
  switch (request.method) {
34
34
  case "GET":
35
- return request.reply(200, await this.secretStore.list());
35
+ return request.reply(200, await this.secretStore.list(SystemActor.actor));
36
36
  }
37
37
  request.notFound();
38
38
  }