@griffin-app/griffin-plan-executor 0.1.13 → 0.1.15
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/README.md +14 -14
- package/dist/events/adapters/in-memory.test.js +25 -23
- package/dist/events/adapters/in-memory.test.js.map +1 -1
- package/dist/events/adapters/kinesis.d.ts.map +1 -1
- package/dist/events/adapters/kinesis.js.map +1 -1
- package/dist/events/adapters/kinesis.test.js +22 -20
- package/dist/events/adapters/kinesis.test.js.map +1 -1
- package/dist/events/emitter.test.js +15 -15
- package/dist/events/emitter.test.js.map +1 -1
- package/dist/events/types.d.ts +12 -12
- package/dist/events/types.d.ts.map +1 -1
- package/dist/events/types.js +1 -1
- package/dist/executor.d.ts +2 -2
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +33 -43
- package/dist/executor.js.map +1 -1
- package/dist/executor.test.js +102 -102
- package/dist/executor.test.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/secrets/index.d.ts +4 -4
- package/dist/secrets/index.d.ts.map +1 -1
- package/dist/secrets/index.js +4 -4
- package/dist/secrets/index.js.map +1 -1
- package/dist/secrets/providers/aws.d.ts.map +1 -1
- package/dist/secrets/providers/aws.js +4 -5
- package/dist/secrets/providers/aws.js.map +1 -1
- package/dist/secrets/providers/env.js +1 -1
- package/dist/secrets/providers/env.js.map +1 -1
- package/dist/secrets/providers/vault.js +7 -7
- package/dist/secrets/providers/vault.js.map +1 -1
- package/dist/secrets/registry.d.ts +11 -33
- package/dist/secrets/registry.d.ts.map +1 -1
- package/dist/secrets/registry.js +65 -113
- package/dist/secrets/registry.js.map +1 -1
- package/dist/secrets/resolver.d.ts +12 -12
- package/dist/secrets/resolver.d.ts.map +1 -1
- package/dist/secrets/resolver.js +21 -21
- package/dist/secrets/resolver.js.map +1 -1
- package/dist/secrets/secrets.test.js +96 -120
- package/dist/secrets/secrets.test.js.map +1 -1
- package/dist/secrets/types.d.ts +2 -5
- package/dist/secrets/types.d.ts.map +1 -1
- package/dist/secrets/types.js +1 -4
- package/dist/secrets/types.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/package.json +4 -4
- package/src/events/adapters/README.md +7 -7
- package/src/events/adapters/in-memory.test.ts +27 -23
- package/src/events/adapters/kinesis.test.ts +23 -21
- package/src/events/adapters/kinesis.ts +6 -3
- package/src/events/emitter.test.ts +15 -15
- package/src/events/types.ts +13 -13
- package/src/executor.test.ts +103 -103
- package/src/executor.ts +40 -48
- package/src/index.ts +7 -7
- package/src/secrets/index.ts +5 -5
- package/src/secrets/providers/aws.ts +4 -5
- package/src/secrets/providers/env.ts +1 -1
- package/src/secrets/providers/vault.ts +7 -7
- package/src/secrets/registry.ts +75 -142
- package/src/secrets/resolver.ts +28 -26
- package/src/secrets/secrets.test.ts +124 -155
- package/src/secrets/types.ts +4 -13
- package/src/{test-plan-types.ts → test-monitor-types.ts} +1 -1
- package/src/types.ts +2 -2
|
@@ -3,18 +3,15 @@ import { isSecretRef, isStringLiteral } from "./types.js";
|
|
|
3
3
|
import { SecretProviderRegistry } from "./registry.js";
|
|
4
4
|
import { EnvSecretProvider } from "./providers/env.js";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
resolveSecretsInMonitor,
|
|
7
|
+
collectSecretsFromMonitor,
|
|
8
8
|
planHasSecrets,
|
|
9
9
|
} from "./resolver.js";
|
|
10
|
-
import {
|
|
10
|
+
import { MonitorV1 } from "@griffin-app/griffin-hub-sdk";
|
|
11
11
|
|
|
12
12
|
// Helper to create a secret ref (mirrors the DSL's secret function)
|
|
13
|
-
function createSecretRef(
|
|
14
|
-
|
|
15
|
-
const provider = path.slice(0, colonIndex);
|
|
16
|
-
const ref = path.slice(colonIndex + 1);
|
|
17
|
-
return { $secret: { provider, ref } };
|
|
13
|
+
function createSecretRef(ref: string) {
|
|
14
|
+
return { $secret: { ref } };
|
|
18
15
|
}
|
|
19
16
|
|
|
20
17
|
// Helper to create a string literal (mirrors the schema's StringLiteral)
|
|
@@ -25,12 +22,10 @@ function createStringLiteral(value: string) {
|
|
|
25
22
|
describe("Secret Types", () => {
|
|
26
23
|
describe("isSecretRef", () => {
|
|
27
24
|
it("should return true for valid secret refs", () => {
|
|
28
|
-
expect(
|
|
29
|
-
isSecretRef({ $secret: { provider: "env", ref: "API_KEY" } }),
|
|
30
|
-
).toBe(true);
|
|
25
|
+
expect(isSecretRef({ $secret: { ref: "API_KEY" } })).toBe(true);
|
|
31
26
|
expect(
|
|
32
27
|
isSecretRef({
|
|
33
|
-
$secret: {
|
|
28
|
+
$secret: { ref: "my-secret", version: "1" },
|
|
34
29
|
}),
|
|
35
30
|
).toBe(true);
|
|
36
31
|
});
|
|
@@ -41,9 +36,7 @@ describe("Secret Types", () => {
|
|
|
41
36
|
expect(isSecretRef(null)).toBe(false);
|
|
42
37
|
expect(isSecretRef(undefined)).toBe(false);
|
|
43
38
|
expect(isSecretRef({})).toBe(false);
|
|
44
|
-
expect(isSecretRef({ secret: {
|
|
45
|
-
false,
|
|
46
|
-
); // wrong key
|
|
39
|
+
expect(isSecretRef({ secret: { ref: "KEY" } })).toBe(false); // wrong key
|
|
47
40
|
expect(isSecretRef({ $literal: "value" })).toBe(false); // string literal
|
|
48
41
|
});
|
|
49
42
|
});
|
|
@@ -62,9 +55,7 @@ describe("Secret Types", () => {
|
|
|
62
55
|
expect(isStringLiteral(undefined)).toBe(false);
|
|
63
56
|
expect(isStringLiteral({})).toBe(false);
|
|
64
57
|
expect(isStringLiteral({ literal: "value" })).toBe(false); // wrong key
|
|
65
|
-
expect(
|
|
66
|
-
isStringLiteral({ $secret: { provider: "env", ref: "KEY" } }),
|
|
67
|
-
).toBe(false); // secret ref
|
|
58
|
+
expect(isStringLiteral({ $secret: { ref: "KEY" } })).toBe(false); // secret ref
|
|
68
59
|
});
|
|
69
60
|
});
|
|
70
61
|
});
|
|
@@ -76,33 +67,23 @@ describe("SecretProviderRegistry", () => {
|
|
|
76
67
|
registry = new SecretProviderRegistry();
|
|
77
68
|
});
|
|
78
69
|
|
|
79
|
-
it("should
|
|
80
|
-
const envProvider = new EnvSecretProvider(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
expect(registry.get("env")).toBe(envProvider);
|
|
85
|
-
expect(registry.getProviderNames()).toEqual(["env"]);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("should throw when getting unregistered provider", () => {
|
|
89
|
-
expect(() => registry.get("unknown")).toThrow(/not configured/);
|
|
90
|
-
});
|
|
70
|
+
it("should set and use provider", async () => {
|
|
71
|
+
const envProvider = new EnvSecretProvider({
|
|
72
|
+
env: { TEST_SECRET: "resolved-value" },
|
|
73
|
+
});
|
|
74
|
+
registry.setProvider(envProvider);
|
|
91
75
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
registry.register(envProvider);
|
|
95
|
-
expect(() => registry.register(envProvider)).toThrow(/already registered/);
|
|
76
|
+
const value = await registry.resolve({ ref: "TEST_SECRET" });
|
|
77
|
+
expect(value).toBe("resolved-value");
|
|
96
78
|
});
|
|
97
79
|
|
|
98
|
-
it("should resolve secrets using the
|
|
80
|
+
it("should resolve secrets using the configured provider", async () => {
|
|
99
81
|
const envProvider = new EnvSecretProvider({
|
|
100
82
|
env: { TEST_SECRET: "secret-value" },
|
|
101
83
|
});
|
|
102
|
-
registry.
|
|
84
|
+
registry.setProvider(envProvider);
|
|
103
85
|
|
|
104
86
|
const result = await registry.resolve({
|
|
105
|
-
provider: "env",
|
|
106
87
|
ref: "TEST_SECRET",
|
|
107
88
|
});
|
|
108
89
|
|
|
@@ -137,127 +118,119 @@ describe("EnvSecretProvider", () => {
|
|
|
137
118
|
});
|
|
138
119
|
});
|
|
139
120
|
|
|
140
|
-
describe("
|
|
141
|
-
const
|
|
142
|
-
headers?: Record<string,
|
|
143
|
-
body?:
|
|
144
|
-
):
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
121
|
+
describe("Monitor Secret Resolution", () => {
|
|
122
|
+
const createTestMonitor = (
|
|
123
|
+
headers?: Record<string, unknown>,
|
|
124
|
+
body?: unknown,
|
|
125
|
+
): MonitorV1 =>
|
|
126
|
+
({
|
|
127
|
+
id: "test-monitor-1",
|
|
128
|
+
name: "Test Monitor",
|
|
129
|
+
version: "1.0",
|
|
130
|
+
environment: "default",
|
|
131
|
+
project: "test-project",
|
|
132
|
+
frequency: { every: 1, unit: "MINUTE" },
|
|
133
|
+
nodes: [
|
|
134
|
+
{
|
|
135
|
+
id: "endpoint-1",
|
|
136
|
+
type: "HTTP_REQUEST",
|
|
137
|
+
method: "GET",
|
|
138
|
+
path: "/api/test",
|
|
139
|
+
base: "https://api.example.com",
|
|
140
|
+
response_format: "JSON",
|
|
141
|
+
headers,
|
|
142
|
+
body,
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
edges: [
|
|
146
|
+
{ from: "__START__", to: "endpoint-1" },
|
|
147
|
+
{ from: "endpoint-1", to: "__END__" },
|
|
148
|
+
],
|
|
149
|
+
}) as MonitorV1;
|
|
168
150
|
|
|
169
151
|
describe("planHasSecrets", () => {
|
|
170
|
-
it("should return false for
|
|
171
|
-
const
|
|
172
|
-
expect(planHasSecrets(
|
|
152
|
+
it("should return false for monitors without secrets or literals", () => {
|
|
153
|
+
const monitor = createTestMonitor({ "Content-Type": "application/json" });
|
|
154
|
+
expect(planHasSecrets(monitor)).toBe(false);
|
|
173
155
|
});
|
|
174
156
|
|
|
175
|
-
it("should return true for
|
|
176
|
-
const
|
|
177
|
-
Authorization: createSecretRef("
|
|
157
|
+
it("should return true for monitors with secret refs in headers", () => {
|
|
158
|
+
const monitor = createTestMonitor({
|
|
159
|
+
Authorization: createSecretRef("API_KEY"),
|
|
178
160
|
});
|
|
179
|
-
expect(planHasSecrets(
|
|
161
|
+
expect(planHasSecrets(monitor)).toBe(true);
|
|
180
162
|
});
|
|
181
163
|
|
|
182
|
-
it("should return true for
|
|
183
|
-
const
|
|
184
|
-
token: createSecretRef("
|
|
164
|
+
it("should return true for monitors with secret refs in body", () => {
|
|
165
|
+
const monitor = createTestMonitor(undefined, {
|
|
166
|
+
token: createSecretRef("TOKEN"),
|
|
185
167
|
});
|
|
186
|
-
expect(planHasSecrets(
|
|
168
|
+
expect(planHasSecrets(monitor)).toBe(true);
|
|
187
169
|
});
|
|
188
170
|
|
|
189
|
-
it("should return true for
|
|
190
|
-
const
|
|
171
|
+
it("should return true for monitors with string literals in headers", () => {
|
|
172
|
+
const monitor = createTestMonitor({
|
|
191
173
|
"Content-Type": createStringLiteral("application/json"),
|
|
192
174
|
});
|
|
193
|
-
expect(planHasSecrets(
|
|
175
|
+
expect(planHasSecrets(monitor)).toBe(true);
|
|
194
176
|
});
|
|
195
177
|
|
|
196
|
-
it("should return true for
|
|
197
|
-
const
|
|
178
|
+
it("should return true for monitors with string literals in body", () => {
|
|
179
|
+
const monitor = createTestMonitor(undefined, {
|
|
198
180
|
type: createStringLiteral("test"),
|
|
199
181
|
});
|
|
200
|
-
expect(planHasSecrets(
|
|
182
|
+
expect(planHasSecrets(monitor)).toBe(true);
|
|
201
183
|
});
|
|
202
184
|
});
|
|
203
185
|
|
|
204
|
-
describe("
|
|
186
|
+
describe("collectSecretsFromMonitor", () => {
|
|
205
187
|
it("should collect secrets from headers", () => {
|
|
206
|
-
const
|
|
207
|
-
Authorization: createSecretRef("
|
|
208
|
-
"X-Custom": createSecretRef("
|
|
188
|
+
const monitor = createTestMonitor({
|
|
189
|
+
Authorization: createSecretRef("API_KEY"),
|
|
190
|
+
"X-Custom": createSecretRef("custom-secret"),
|
|
209
191
|
});
|
|
210
192
|
|
|
211
|
-
const collected =
|
|
193
|
+
const collected = collectSecretsFromMonitor(monitor);
|
|
212
194
|
|
|
213
195
|
expect(collected.refs).toHaveLength(2);
|
|
214
|
-
expect(collected.refs).toContainEqual({
|
|
215
|
-
|
|
216
|
-
ref: "API_KEY",
|
|
217
|
-
});
|
|
218
|
-
expect(collected.refs).toContainEqual({
|
|
219
|
-
provider: "aws",
|
|
220
|
-
ref: "custom-secret",
|
|
221
|
-
});
|
|
196
|
+
expect(collected.refs).toContainEqual({ ref: "API_KEY" });
|
|
197
|
+
expect(collected.refs).toContainEqual({ ref: "custom-secret" });
|
|
222
198
|
});
|
|
223
199
|
|
|
224
200
|
it("should collect secrets from nested body", () => {
|
|
225
|
-
const
|
|
201
|
+
const monitor = createTestMonitor(undefined, {
|
|
226
202
|
auth: {
|
|
227
|
-
token: createSecretRef("
|
|
203
|
+
token: createSecretRef("TOKEN"),
|
|
228
204
|
},
|
|
229
|
-
items: [{ key: createSecretRef("
|
|
205
|
+
items: [{ key: createSecretRef("ITEM_KEY") }],
|
|
230
206
|
});
|
|
231
207
|
|
|
232
|
-
const collected =
|
|
208
|
+
const collected = collectSecretsFromMonitor(monitor);
|
|
233
209
|
|
|
234
210
|
expect(collected.refs).toHaveLength(2);
|
|
235
|
-
expect(collected.refs).toContainEqual({
|
|
236
|
-
expect(collected.refs).toContainEqual({
|
|
237
|
-
provider: "env",
|
|
238
|
-
ref: "ITEM_KEY",
|
|
239
|
-
});
|
|
211
|
+
expect(collected.refs).toContainEqual({ ref: "TOKEN" });
|
|
212
|
+
expect(collected.refs).toContainEqual({ ref: "ITEM_KEY" });
|
|
240
213
|
});
|
|
241
214
|
|
|
242
215
|
it("should deduplicate secret refs", () => {
|
|
243
|
-
const
|
|
244
|
-
Authorization: createSecretRef("
|
|
245
|
-
"X-Backup-Auth": createSecretRef("
|
|
216
|
+
const monitor = createTestMonitor({
|
|
217
|
+
Authorization: createSecretRef("API_KEY"),
|
|
218
|
+
"X-Backup-Auth": createSecretRef("API_KEY"),
|
|
246
219
|
});
|
|
247
220
|
|
|
248
|
-
const collected =
|
|
221
|
+
const collected = collectSecretsFromMonitor(monitor);
|
|
249
222
|
|
|
250
223
|
expect(collected.refs).toHaveLength(1);
|
|
251
224
|
expect(collected.paths).toHaveLength(2);
|
|
252
225
|
});
|
|
253
226
|
|
|
254
227
|
it("should collect string literals from headers", () => {
|
|
255
|
-
const
|
|
228
|
+
const monitor = createTestMonitor({
|
|
256
229
|
"Content-Type": createStringLiteral("application/json"),
|
|
257
230
|
Accept: createStringLiteral("application/xml"),
|
|
258
231
|
});
|
|
259
232
|
|
|
260
|
-
const collected =
|
|
233
|
+
const collected = collectSecretsFromMonitor(monitor);
|
|
261
234
|
|
|
262
235
|
expect(collected.refs).toHaveLength(0);
|
|
263
236
|
expect(collected.literalPaths).toHaveLength(2);
|
|
@@ -272,18 +245,15 @@ describe("Plan Secret Resolution", () => {
|
|
|
272
245
|
});
|
|
273
246
|
|
|
274
247
|
it("should collect both secrets and literals", () => {
|
|
275
|
-
const
|
|
276
|
-
Authorization: createSecretRef("
|
|
248
|
+
const monitor = createTestMonitor({
|
|
249
|
+
Authorization: createSecretRef("API_KEY"),
|
|
277
250
|
"Content-Type": createStringLiteral("application/json"),
|
|
278
251
|
});
|
|
279
252
|
|
|
280
|
-
const collected =
|
|
253
|
+
const collected = collectSecretsFromMonitor(monitor);
|
|
281
254
|
|
|
282
255
|
expect(collected.refs).toHaveLength(1);
|
|
283
|
-
expect(collected.refs).toContainEqual({
|
|
284
|
-
provider: "env",
|
|
285
|
-
ref: "API_KEY",
|
|
286
|
-
});
|
|
256
|
+
expect(collected.refs).toContainEqual({ ref: "API_KEY" });
|
|
287
257
|
expect(collected.literalPaths).toHaveLength(1);
|
|
288
258
|
expect(collected.literalPaths).toContainEqual({
|
|
289
259
|
path: ["nodes", 0, "headers", "Content-Type"],
|
|
@@ -292,21 +262,21 @@ describe("Plan Secret Resolution", () => {
|
|
|
292
262
|
});
|
|
293
263
|
});
|
|
294
264
|
|
|
295
|
-
describe("
|
|
265
|
+
describe("resolveSecretsInMonitor", () => {
|
|
296
266
|
it("should resolve secrets in headers", async () => {
|
|
297
|
-
const
|
|
298
|
-
Authorization: createSecretRef("
|
|
267
|
+
const monitor = createTestMonitor({
|
|
268
|
+
Authorization: createSecretRef("API_KEY"),
|
|
299
269
|
"Content-Type": "application/json",
|
|
300
270
|
});
|
|
301
271
|
|
|
302
272
|
const registry = new SecretProviderRegistry();
|
|
303
|
-
registry.
|
|
273
|
+
registry.setProvider(
|
|
304
274
|
new EnvSecretProvider({
|
|
305
275
|
env: { API_KEY: "Bearer secret-token" },
|
|
306
276
|
}),
|
|
307
277
|
);
|
|
308
278
|
|
|
309
|
-
const resolved = await
|
|
279
|
+
const resolved = await resolveSecretsInMonitor(monitor, registry);
|
|
310
280
|
|
|
311
281
|
const endpoint = resolved.nodes[0];
|
|
312
282
|
if (endpoint.type !== "HTTP_REQUEST") {
|
|
@@ -317,19 +287,19 @@ describe("Plan Secret Resolution", () => {
|
|
|
317
287
|
});
|
|
318
288
|
|
|
319
289
|
it("should resolve secrets in body", async () => {
|
|
320
|
-
const
|
|
321
|
-
token: createSecretRef("
|
|
290
|
+
const monitor = createTestMonitor(undefined, {
|
|
291
|
+
token: createSecretRef("TOKEN"),
|
|
322
292
|
data: "plain-value",
|
|
323
293
|
});
|
|
324
294
|
|
|
325
295
|
const registry = new SecretProviderRegistry();
|
|
326
|
-
registry.
|
|
296
|
+
registry.setProvider(
|
|
327
297
|
new EnvSecretProvider({
|
|
328
298
|
env: { TOKEN: "resolved-token" },
|
|
329
299
|
}),
|
|
330
300
|
);
|
|
331
301
|
|
|
332
|
-
const resolved = await
|
|
302
|
+
const resolved = await resolveSecretsInMonitor(monitor, registry);
|
|
333
303
|
|
|
334
304
|
const endpoint = resolved.nodes[0];
|
|
335
305
|
if (endpoint.type !== "HTTP_REQUEST") {
|
|
@@ -339,27 +309,27 @@ describe("Plan Secret Resolution", () => {
|
|
|
339
309
|
expect((endpoint.body as { data: string }).data).toBe("plain-value");
|
|
340
310
|
});
|
|
341
311
|
|
|
342
|
-
it("should not modify original
|
|
343
|
-
const
|
|
344
|
-
Authorization: createSecretRef("
|
|
312
|
+
it("should not modify original monitor", async () => {
|
|
313
|
+
const monitor = createTestMonitor({
|
|
314
|
+
Authorization: createSecretRef("API_KEY"),
|
|
345
315
|
});
|
|
346
316
|
|
|
347
317
|
const registry = new SecretProviderRegistry();
|
|
348
|
-
registry.
|
|
318
|
+
registry.setProvider(
|
|
349
319
|
new EnvSecretProvider({
|
|
350
320
|
env: { API_KEY: "secret" },
|
|
351
321
|
}),
|
|
352
322
|
);
|
|
353
323
|
|
|
354
|
-
const resolved = await
|
|
324
|
+
const resolved = await resolveSecretsInMonitor(monitor, registry);
|
|
355
325
|
|
|
356
326
|
// Original should still have secret ref
|
|
357
|
-
const originalEndpoint =
|
|
327
|
+
const originalEndpoint = monitor.nodes[0];
|
|
358
328
|
if (originalEndpoint.type !== "HTTP_REQUEST") {
|
|
359
329
|
throw new Error("HttpRequest not found");
|
|
360
330
|
}
|
|
361
331
|
expect(originalEndpoint.headers?.Authorization).toEqual(
|
|
362
|
-
createSecretRef("
|
|
332
|
+
createSecretRef("API_KEY"),
|
|
363
333
|
);
|
|
364
334
|
|
|
365
335
|
// Resolved should have string
|
|
@@ -370,41 +340,40 @@ describe("Plan Secret Resolution", () => {
|
|
|
370
340
|
expect(resolvedEndpoint.headers?.Authorization).toBe("secret");
|
|
371
341
|
});
|
|
372
342
|
|
|
373
|
-
it("should throw
|
|
374
|
-
const
|
|
375
|
-
Authorization: createSecretRef("
|
|
343
|
+
it("should throw when no provider configured", async () => {
|
|
344
|
+
const monitor = createTestMonitor({
|
|
345
|
+
Authorization: createSecretRef("API_KEY"),
|
|
376
346
|
});
|
|
377
347
|
|
|
378
348
|
const registry = new SecretProviderRegistry();
|
|
379
|
-
registry.register(new EnvSecretProvider({ env: {} }));
|
|
380
349
|
|
|
381
|
-
await expect(
|
|
382
|
-
/
|
|
350
|
+
await expect(resolveSecretsInMonitor(monitor, registry)).rejects.toThrow(
|
|
351
|
+
/No secret provider configured/,
|
|
383
352
|
);
|
|
384
353
|
});
|
|
385
354
|
|
|
386
355
|
it("should throw for missing secret", async () => {
|
|
387
|
-
const
|
|
388
|
-
Authorization: createSecretRef("
|
|
356
|
+
const monitor = createTestMonitor({
|
|
357
|
+
Authorization: createSecretRef("MISSING_KEY"),
|
|
389
358
|
});
|
|
390
359
|
|
|
391
360
|
const registry = new SecretProviderRegistry();
|
|
392
|
-
registry.
|
|
361
|
+
registry.setProvider(new EnvSecretProvider({ env: {} }));
|
|
393
362
|
|
|
394
|
-
await expect(
|
|
363
|
+
await expect(resolveSecretsInMonitor(monitor, registry)).rejects.toThrow(
|
|
395
364
|
/not set/,
|
|
396
365
|
);
|
|
397
366
|
});
|
|
398
367
|
|
|
399
368
|
it("should unwrap string literals in headers", async () => {
|
|
400
|
-
const
|
|
369
|
+
const monitor = createTestMonitor({
|
|
401
370
|
"Content-Type": createStringLiteral("application/json"),
|
|
402
371
|
Accept: createStringLiteral("application/xml"),
|
|
403
372
|
});
|
|
404
373
|
|
|
405
374
|
const registry = new SecretProviderRegistry();
|
|
406
375
|
|
|
407
|
-
const resolved = await
|
|
376
|
+
const resolved = await resolveSecretsInMonitor(monitor, registry);
|
|
408
377
|
|
|
409
378
|
const endpoint = resolved.nodes[0];
|
|
410
379
|
if (endpoint.type !== "HTTP_REQUEST") {
|
|
@@ -415,14 +384,14 @@ describe("Plan Secret Resolution", () => {
|
|
|
415
384
|
});
|
|
416
385
|
|
|
417
386
|
it("should unwrap string literals in body", async () => {
|
|
418
|
-
const
|
|
387
|
+
const monitor = createTestMonitor(undefined, {
|
|
419
388
|
type: createStringLiteral("test-type"),
|
|
420
389
|
data: "plain-value",
|
|
421
390
|
});
|
|
422
391
|
|
|
423
392
|
const registry = new SecretProviderRegistry();
|
|
424
393
|
|
|
425
|
-
const resolved = await
|
|
394
|
+
const resolved = await resolveSecretsInMonitor(monitor, registry);
|
|
426
395
|
|
|
427
396
|
const endpoint = resolved.nodes[0];
|
|
428
397
|
if (endpoint.type !== "HTTP_REQUEST") {
|
|
@@ -433,19 +402,19 @@ describe("Plan Secret Resolution", () => {
|
|
|
433
402
|
});
|
|
434
403
|
|
|
435
404
|
it("should resolve both secrets and literals", async () => {
|
|
436
|
-
const
|
|
437
|
-
Authorization: createSecretRef("
|
|
405
|
+
const monitor = createTestMonitor({
|
|
406
|
+
Authorization: createSecretRef("API_KEY"),
|
|
438
407
|
"Content-Type": createStringLiteral("application/json"),
|
|
439
408
|
});
|
|
440
409
|
|
|
441
410
|
const registry = new SecretProviderRegistry();
|
|
442
|
-
registry.
|
|
411
|
+
registry.setProvider(
|
|
443
412
|
new EnvSecretProvider({
|
|
444
413
|
env: { API_KEY: "Bearer secret-token" },
|
|
445
414
|
}),
|
|
446
415
|
);
|
|
447
416
|
|
|
448
|
-
const resolved = await
|
|
417
|
+
const resolved = await resolveSecretsInMonitor(monitor, registry);
|
|
449
418
|
|
|
450
419
|
const endpoint = resolved.nodes[0];
|
|
451
420
|
if (endpoint.type !== "HTTP_REQUEST") {
|
|
@@ -455,17 +424,17 @@ describe("Plan Secret Resolution", () => {
|
|
|
455
424
|
expect(endpoint.headers?.["Content-Type"]).toBe("application/json");
|
|
456
425
|
});
|
|
457
426
|
|
|
458
|
-
it("should not modify original
|
|
459
|
-
const
|
|
427
|
+
it("should not modify original monitor with literals", async () => {
|
|
428
|
+
const monitor = createTestMonitor({
|
|
460
429
|
"Content-Type": createStringLiteral("application/json"),
|
|
461
430
|
});
|
|
462
431
|
|
|
463
432
|
const registry = new SecretProviderRegistry();
|
|
464
433
|
|
|
465
|
-
const resolved = await
|
|
434
|
+
const resolved = await resolveSecretsInMonitor(monitor, registry);
|
|
466
435
|
|
|
467
436
|
// Original should still have literal wrapper
|
|
468
|
-
const originalEndpoint =
|
|
437
|
+
const originalEndpoint = monitor.nodes[0];
|
|
469
438
|
if (originalEndpoint.type !== "HTTP_REQUEST") {
|
|
470
439
|
throw new Error("HttpRequest not found");
|
|
471
440
|
}
|
package/src/secrets/types.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import { StringLiteral } from "@griffin-app/griffin-hub-sdk";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Data structure for a secret reference as it appears in a
|
|
4
|
+
* Data structure for a secret reference as it appears in a monitor.
|
|
5
5
|
*/
|
|
6
6
|
export interface SecretRefData {
|
|
7
|
-
provider: string;
|
|
8
7
|
ref: string;
|
|
9
8
|
version?: string;
|
|
10
9
|
field?: string;
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
|
-
* Secret reference marker in
|
|
13
|
+
* Secret reference marker in monitor JSON.
|
|
15
14
|
*/
|
|
16
15
|
export interface SecretRef {
|
|
17
16
|
$secret: SecretRefData;
|
|
@@ -60,17 +59,12 @@ export interface SecretProvider {
|
|
|
60
59
|
* Error thrown when secret resolution fails.
|
|
61
60
|
*/
|
|
62
61
|
export class SecretResolutionError extends Error {
|
|
63
|
-
public readonly provider: string;
|
|
64
62
|
public readonly ref: string;
|
|
65
63
|
public readonly cause?: Error;
|
|
66
64
|
|
|
67
|
-
constructor(
|
|
68
|
-
message: string,
|
|
69
|
-
details: { provider: string; ref: string; cause?: unknown },
|
|
70
|
-
) {
|
|
65
|
+
constructor(message: string, details: { ref: string; cause?: unknown }) {
|
|
71
66
|
super(message);
|
|
72
67
|
this.name = "SecretResolutionError";
|
|
73
|
-
this.provider = details.provider;
|
|
74
68
|
this.ref = details.ref;
|
|
75
69
|
if (details.cause instanceof Error) {
|
|
76
70
|
this.cause = details.cause;
|
|
@@ -96,10 +90,7 @@ export function isSecretRef(value: unknown): value is SecretRef {
|
|
|
96
90
|
}
|
|
97
91
|
|
|
98
92
|
const secretData = obj.$secret as Record<string, unknown>;
|
|
99
|
-
return
|
|
100
|
-
typeof secretData.provider === "string" &&
|
|
101
|
-
typeof secretData.ref === "string"
|
|
102
|
-
);
|
|
93
|
+
return typeof secretData.ref === "string";
|
|
103
94
|
}
|
|
104
95
|
|
|
105
96
|
/**
|
package/src/types.ts
CHANGED
|
@@ -57,12 +57,12 @@ export interface RunStatusUpdate {
|
|
|
57
57
|
*/
|
|
58
58
|
export interface StatusCallbacks {
|
|
59
59
|
/**
|
|
60
|
-
* Called when
|
|
60
|
+
* Called when monitor execution starts (after secret resolution, before graph execution).
|
|
61
61
|
*/
|
|
62
62
|
onStart?: () => Promise<void>;
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
|
-
* Called when
|
|
65
|
+
* Called when monitor execution completes (success or failure).
|
|
66
66
|
*/
|
|
67
67
|
onComplete?: (update: RunStatusUpdate) => Promise<void>;
|
|
68
68
|
}
|