anbaric-impl-cloud 1.20.1 → 1.21.1
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 +2 -2
- package/src/CloudApiClient.ts +10 -1
- package/src/CloudAuditor.ts +3 -2
- package/src/CloudJobPersistence.ts +6 -2
- package/src/CloudJsonStore.ts +6 -2
- package/src/CloudQueue.ts +4 -4
- package/src/CloudSecretStore.ts +6 -2
- package/src/PushConsumer.ts +12 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anbaric-impl-cloud",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.1",
|
|
4
4
|
"description": "Public client library for the Anbaric Cloud hosting API, providing JobPersistence and Queue implementations backed by the cloud service",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "chris@anbaric.ai",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"test": "vitest run"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"anbaric-tsapi": "^1.
|
|
14
|
+
"anbaric-tsapi": "^1.21.1"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@types/node": "^26.2.0",
|
package/src/CloudApiClient.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const DEFAULT_BASE_URL = "http://localhost:8787";
|
|
2
2
|
const API_PREFIX = "/api/v2";
|
|
3
|
+
const APP_HEADER = "x-anbaric-app";
|
|
3
4
|
|
|
4
5
|
class CloudApiClient {
|
|
5
6
|
|
|
@@ -10,9 +11,17 @@ class CloudApiClient {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
async request(method : string, path : string, body? : unknown) : Promise<any> {
|
|
14
|
+
// Every request carries the calling app's id as an ambient header, so
|
|
15
|
+
// the platform can scope app-owned resources (documents, secrets) to it
|
|
16
|
+
// without the app id appearing in any URL.
|
|
17
|
+
const headers : Record<string, string> = {};
|
|
18
|
+
const appId = process.env.ANBARIC_APP_ID;
|
|
19
|
+
if (appId) headers[APP_HEADER] = appId;
|
|
20
|
+
if (body !== undefined) headers["content-type"] = "application/json";
|
|
21
|
+
|
|
13
22
|
const response = await fetch(`${this.baseUrl}${API_PREFIX}${path}`, {
|
|
14
23
|
method,
|
|
15
|
-
headers
|
|
24
|
+
headers,
|
|
16
25
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
17
26
|
});
|
|
18
27
|
|
package/src/CloudAuditor.ts
CHANGED
|
@@ -9,9 +9,10 @@ class CloudAuditor implements Auditor {
|
|
|
9
9
|
this.client = new CloudApiClient(baseUrl);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
async audit(resourceType : string, resourceId : string, actor : Actor,
|
|
13
|
-
description : string, details : any) : Promise<void> {
|
|
12
|
+
async audit(appId : string | undefined, resourceType : string, resourceId : string, actor : Actor,
|
|
13
|
+
interaction : Array<string>, description : string, details : any) : Promise<void> {
|
|
14
14
|
await this.client.request("POST", "/audits", {
|
|
15
|
+
appId: appId || undefined,
|
|
15
16
|
resourceType,
|
|
16
17
|
resourceId,
|
|
17
18
|
actorId: actor.id,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {Auditor, Job, JobPersistence, NoOpAuditor, SerializedJob, deserializeJob, serializeJob} from "anbaric-tsapi";
|
|
1
|
+
import {AppAware, Auditor, currentAppId, Job, JobPersistence, NoOpAuditor, SerializedJob, deserializeJob, serializeJob} from "anbaric-tsapi";
|
|
2
2
|
import {CloudApiClient} from "./CloudApiClient";
|
|
3
3
|
|
|
4
|
-
class CloudJobPersistence extends JobPersistence {
|
|
4
|
+
class CloudJobPersistence extends JobPersistence implements AppAware {
|
|
5
5
|
|
|
6
6
|
private client : CloudApiClient;
|
|
7
7
|
|
|
@@ -10,6 +10,10 @@ class CloudJobPersistence extends JobPersistence {
|
|
|
10
10
|
this.client = new CloudApiClient(baseUrl);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
getAppId() : string {
|
|
14
|
+
return currentAppId();
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
protected async saveInternal(job : Job) : Promise<void> {
|
|
14
18
|
await this.client.request("PUT", `/jobs/${encodeURIComponent(job.id)}`, serializeJob(job));
|
|
15
19
|
}
|
package/src/CloudJsonStore.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {Auditor, JsonSchema, JsonStore, NoOpAuditor, validateDocument} from "anbaric-tsapi";
|
|
1
|
+
import {AppAware, Auditor, currentAppId, JsonSchema, JsonStore, NoOpAuditor, validateDocument} from "anbaric-tsapi";
|
|
2
2
|
import {CloudApiClient} from "./CloudApiClient";
|
|
3
3
|
|
|
4
|
-
class CloudJsonStore extends JsonStore {
|
|
4
|
+
class CloudJsonStore extends JsonStore implements AppAware {
|
|
5
5
|
|
|
6
6
|
private client : CloudApiClient;
|
|
7
7
|
private schema? : JsonSchema;
|
|
@@ -13,6 +13,10 @@ class CloudJsonStore extends JsonStore {
|
|
|
13
13
|
this.client = new CloudApiClient(baseUrl);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
getAppId() : string {
|
|
17
|
+
return currentAppId();
|
|
18
|
+
}
|
|
19
|
+
|
|
16
20
|
protected async saveInternal(id : string, document : any) : Promise<void> {
|
|
17
21
|
if (this.schema) {
|
|
18
22
|
const violations = validateDocument(document, this.schema);
|
package/src/CloudQueue.ts
CHANGED
|
@@ -9,12 +9,12 @@ class CloudQueue implements Queue {
|
|
|
9
9
|
this.client = new CloudApiClient(baseUrl);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
async enqueue(jobId : string, workflowId : string) : Promise<void> {
|
|
13
|
-
await this.client.request("POST", "/queue/enqueue", { jobId, workflowId });
|
|
12
|
+
async enqueue(jobId : string, appId : string | undefined, workflowId : string) : Promise<void> {
|
|
13
|
+
await this.client.request("POST", "/queue/enqueue", { jobId, appId, workflowId });
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
async schedule(jobId : string, workflowId : string, due : Date) : Promise<void> {
|
|
17
|
-
await this.client.request("POST", "/queue/schedule", { jobId, workflowId, due: due.toISOString() });
|
|
16
|
+
async schedule(jobId : string, appId : string | undefined, workflowId : string, due : Date) : Promise<void> {
|
|
17
|
+
await this.client.request("POST", "/queue/schedule", { jobId, appId, workflowId, due: due.toISOString() });
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
}
|
package/src/CloudSecretStore.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {Auditor, NoOpAuditor, SecretStore} from "anbaric-tsapi";
|
|
1
|
+
import {AppAware, Auditor, currentAppId, NoOpAuditor, SecretStore} from "anbaric-tsapi";
|
|
2
2
|
import {CloudApiClient} from "./CloudApiClient";
|
|
3
3
|
|
|
4
|
-
class CloudSecretStore extends SecretStore {
|
|
4
|
+
class CloudSecretStore extends SecretStore implements AppAware {
|
|
5
5
|
|
|
6
6
|
private client : CloudApiClient;
|
|
7
7
|
|
|
@@ -10,6 +10,10 @@ class CloudSecretStore extends SecretStore {
|
|
|
10
10
|
this.client = new CloudApiClient(baseUrl);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
getAppId() : string {
|
|
14
|
+
return currentAppId();
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
protected async saveInternal(name : string, value : string) : Promise<void> {
|
|
14
18
|
await this.client.request("PUT", this.secretPath(name), { value });
|
|
15
19
|
}
|
package/src/PushConsumer.ts
CHANGED
|
@@ -37,17 +37,23 @@ class PushConsumer implements Consumer {
|
|
|
37
37
|
return this.boundPort;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
// Subscribers are keyed by the (appId, workflowId) composite, encoded as a
|
|
41
|
+
// tuple so an app and a machine id can never collide.
|
|
42
|
+
private key(appId : string | undefined, workflowId : string) : string {
|
|
43
|
+
return JSON.stringify([appId || null, workflowId]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
subscribe(appId : string | undefined, workflowId : string, processJob : ProcessJob) : void {
|
|
47
|
+
this.subscribers.set(this.key(appId, workflowId), processJob);
|
|
42
48
|
if (!this.server) this.listening = this.listen();
|
|
43
|
-
void this.register(workflowId);
|
|
49
|
+
void this.register(appId, workflowId);
|
|
44
50
|
}
|
|
45
51
|
|
|
46
|
-
private async register(workflowId : string) : Promise<void> {
|
|
52
|
+
private async register(appId : string | undefined, workflowId : string) : Promise<void> {
|
|
47
53
|
try {
|
|
48
54
|
await this.listening;
|
|
49
55
|
const url = process.env.ANBARIC_CONSUMER_URL ?? `http://localhost:${this.boundPort}`;
|
|
50
|
-
await this.client.request("POST", "/consumers", { workflowId, url });
|
|
56
|
+
await this.client.request("POST", "/consumers", { appId, workflowId, url });
|
|
51
57
|
} catch {
|
|
52
58
|
}
|
|
53
59
|
}
|
|
@@ -94,7 +100,7 @@ class PushConsumer implements Consumer {
|
|
|
94
100
|
|
|
95
101
|
private async processAll(messages : Array<QueueMessage>) : Promise<void> {
|
|
96
102
|
for (const message of messages) {
|
|
97
|
-
const processJob = this.subscribers.get(message.workflowId);
|
|
103
|
+
const processJob = this.subscribers.get(this.key(message.appId, message.workflowId));
|
|
98
104
|
if (!processJob) continue;
|
|
99
105
|
try {
|
|
100
106
|
await processJob(message.jobId);
|