@griffin-app/griffin-cli 1.0.31 → 1.0.33
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/dist/core/variables.js +50 -2
- package/dist/core/variables.test.d.ts +1 -0
- package/dist/core/variables.test.js +127 -0
- package/dist/monitor-runner.js +6 -12
- package/package.json +4 -4
package/dist/core/variables.js
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { getEnvironment } from "./state.js";
|
|
2
2
|
import { isNodeRef } from "@griffin-app/griffin-core/schema";
|
|
3
|
+
function isTemplateRef(value) {
|
|
4
|
+
if (typeof value !== "object" || value === null) {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
const obj = value;
|
|
8
|
+
if (!("$template" in obj) ||
|
|
9
|
+
typeof obj.$template !== "object" ||
|
|
10
|
+
obj.$template === null) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const tmpl = obj.$template;
|
|
14
|
+
return Array.isArray(tmpl.strings) && Array.isArray(tmpl.values);
|
|
15
|
+
}
|
|
3
16
|
/**
|
|
4
17
|
* Load variables from state file for a specific environment.
|
|
5
18
|
*
|
|
@@ -61,6 +74,38 @@ function resolveVariable(varRef, variables) {
|
|
|
61
74
|
const placeholder = `\${${key}}`;
|
|
62
75
|
return template.replace(placeholder, value);
|
|
63
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Resolve variables in a template, using partial application.
|
|
79
|
+
* Resolves $variable values, merges them into adjacent strings.
|
|
80
|
+
* Returns a plain string if all values resolved, or a reduced $template.
|
|
81
|
+
*/
|
|
82
|
+
function resolveTemplateVariables(tmpl, variables) {
|
|
83
|
+
const { strings, values } = tmpl.$template;
|
|
84
|
+
const newStrings = [strings[0]];
|
|
85
|
+
const newValues = [];
|
|
86
|
+
for (let i = 0; i < values.length; i++) {
|
|
87
|
+
const val = values[i];
|
|
88
|
+
if (isVariableRef(val)) {
|
|
89
|
+
const resolved = resolveVariable(val, variables);
|
|
90
|
+
// Merge resolved value into the last string segment
|
|
91
|
+
newStrings[newStrings.length - 1] += resolved + strings[i + 1];
|
|
92
|
+
}
|
|
93
|
+
else if (isStringLiteral(val)) {
|
|
94
|
+
// Unwrap literals and merge
|
|
95
|
+
newStrings[newStrings.length - 1] += val.$literal + strings[i + 1];
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// Keep this value and its trailing string
|
|
99
|
+
newValues.push(val);
|
|
100
|
+
newStrings.push(strings[i + 1]);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// If all values resolved, collapse to a $literal
|
|
104
|
+
if (newValues.length === 0) {
|
|
105
|
+
return { $literal: newStrings[0] };
|
|
106
|
+
}
|
|
107
|
+
return { $template: { strings: newStrings, values: newValues } };
|
|
108
|
+
}
|
|
64
109
|
/**
|
|
65
110
|
* Recursively walk a monitor object and resolve all variable references.
|
|
66
111
|
*
|
|
@@ -74,10 +119,13 @@ function resolveVariable(varRef, variables) {
|
|
|
74
119
|
export function resolveVariablesInMonitor(obj, variables) {
|
|
75
120
|
// Check if this is a variable reference
|
|
76
121
|
if (isVariableRef(obj)) {
|
|
77
|
-
return resolveVariable(obj, variables);
|
|
122
|
+
return { $literal: resolveVariable(obj, variables) };
|
|
123
|
+
}
|
|
124
|
+
if (isTemplateRef(obj)) {
|
|
125
|
+
return resolveTemplateVariables(obj, variables);
|
|
78
126
|
}
|
|
79
127
|
if (isStringLiteral(obj)) {
|
|
80
|
-
return obj
|
|
128
|
+
return obj;
|
|
81
129
|
}
|
|
82
130
|
if (isNodeRef(obj)) {
|
|
83
131
|
return obj;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { resolveVariablesInMonitor } from "./variables.js";
|
|
3
|
+
describe("resolveVariablesInMonitor", () => {
|
|
4
|
+
const variables = { env: "staging", version: "v2" };
|
|
5
|
+
it("should resolve a simple variable ref to $literal", () => {
|
|
6
|
+
const result = resolveVariablesInMonitor({ $variable: { key: "env" } }, variables);
|
|
7
|
+
expect(result).toEqual({ $literal: "staging" });
|
|
8
|
+
});
|
|
9
|
+
it("should resolve a variable with template to $literal", () => {
|
|
10
|
+
const result = resolveVariablesInMonitor({ $variable: { key: "env", template: "https://${env}.api.com" } }, variables);
|
|
11
|
+
expect(result).toEqual({ $literal: "https://staging.api.com" });
|
|
12
|
+
});
|
|
13
|
+
it("should pass through $literal unchanged", () => {
|
|
14
|
+
const literal = { $literal: "hello" };
|
|
15
|
+
const result = resolveVariablesInMonitor(literal, variables);
|
|
16
|
+
expect(result).toEqual(literal);
|
|
17
|
+
});
|
|
18
|
+
it("should pass through $nodeRef unchanged", () => {
|
|
19
|
+
const nodeRef = {
|
|
20
|
+
$nodeRef: { nodeId: "step1", subject: "body", path: ["id"] },
|
|
21
|
+
};
|
|
22
|
+
const result = resolveVariablesInMonitor(nodeRef, variables);
|
|
23
|
+
expect(result).toEqual(nodeRef);
|
|
24
|
+
});
|
|
25
|
+
describe("$template resolution", () => {
|
|
26
|
+
it("should fully collapse a template with only variables to $literal", () => {
|
|
27
|
+
const tmpl = {
|
|
28
|
+
$template: {
|
|
29
|
+
strings: ["https://", ".api.com/", "/health"],
|
|
30
|
+
values: [
|
|
31
|
+
{ $variable: { key: "env" } },
|
|
32
|
+
{ $variable: { key: "version" } },
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
const result = resolveVariablesInMonitor(tmpl, variables);
|
|
37
|
+
expect(result).toEqual({ $literal: "https://staging.api.com/v2/health" });
|
|
38
|
+
});
|
|
39
|
+
it("should partially resolve a template with mixed variable and secret", () => {
|
|
40
|
+
const tmpl = {
|
|
41
|
+
$template: {
|
|
42
|
+
strings: ["https://", ".api.com?key=", ""],
|
|
43
|
+
values: [
|
|
44
|
+
{ $variable: { key: "env" } },
|
|
45
|
+
{ $secret: { ref: "API_KEY" } },
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
const result = resolveVariablesInMonitor(tmpl, variables);
|
|
50
|
+
expect(result).toEqual({
|
|
51
|
+
$template: {
|
|
52
|
+
strings: ["https://staging.api.com?key=", ""],
|
|
53
|
+
values: [{ $secret: { ref: "API_KEY" } }],
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
it("should partially resolve a template with mixed variable and nodeRef", () => {
|
|
58
|
+
const tmpl = {
|
|
59
|
+
$template: {
|
|
60
|
+
strings: ["/api/", "/resource/", ""],
|
|
61
|
+
values: [
|
|
62
|
+
{ $variable: { key: "version" } },
|
|
63
|
+
{ $nodeRef: { nodeId: "step1", subject: "body", path: ["id"] } },
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
const result = resolveVariablesInMonitor(tmpl, variables);
|
|
68
|
+
expect(result).toEqual({
|
|
69
|
+
$template: {
|
|
70
|
+
strings: ["/api/v2/resource/", ""],
|
|
71
|
+
values: [
|
|
72
|
+
{ $nodeRef: { nodeId: "step1", subject: "body", path: ["id"] } },
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
it("should pass through a template with no variables unchanged", () => {
|
|
78
|
+
const tmpl = {
|
|
79
|
+
$template: {
|
|
80
|
+
strings: ["Bearer ", ""],
|
|
81
|
+
values: [{ $secret: { ref: "TOKEN" } }],
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
const result = resolveVariablesInMonitor(tmpl, variables);
|
|
85
|
+
expect(result).toEqual(tmpl);
|
|
86
|
+
});
|
|
87
|
+
it("should handle adjacent variable interpolations", () => {
|
|
88
|
+
const tmpl = {
|
|
89
|
+
$template: {
|
|
90
|
+
strings: ["", "", "/path"],
|
|
91
|
+
values: [
|
|
92
|
+
{ $variable: { key: "env" } },
|
|
93
|
+
{ $variable: { key: "version" } },
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
const result = resolveVariablesInMonitor(tmpl, variables);
|
|
98
|
+
expect(result).toEqual({ $literal: "stagingv2/path" });
|
|
99
|
+
});
|
|
100
|
+
it("should resolve templates nested in monitor structure", () => {
|
|
101
|
+
const monitor = {
|
|
102
|
+
nodes: [
|
|
103
|
+
{
|
|
104
|
+
type: "HTTP_REQUEST",
|
|
105
|
+
path: {
|
|
106
|
+
$template: {
|
|
107
|
+
strings: ["/api/", "/health"],
|
|
108
|
+
values: [{ $variable: { key: "version" } }],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
base: {
|
|
112
|
+
$template: {
|
|
113
|
+
strings: ["https://", ".api.com"],
|
|
114
|
+
values: [{ $variable: { key: "env" } }],
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
const result = resolveVariablesInMonitor(monitor, variables);
|
|
121
|
+
expect(result.nodes[0].path).toEqual({ $literal: "/api/v2/health" });
|
|
122
|
+
expect(result.nodes[0].base).toEqual({
|
|
123
|
+
$literal: "https://staging.api.com",
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
package/dist/monitor-runner.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "tsx";
|
|
2
2
|
import { Value } from "typebox/value";
|
|
3
|
-
import { executeMonitorV1, AxiosAdapter,
|
|
3
|
+
import { executeMonitorV1, AxiosAdapter, } from "@griffin-app/griffin-executor";
|
|
4
4
|
import { MonitorDSLSchema } from "@griffin-app/griffin-core/schema";
|
|
5
5
|
import { randomUUID } from "crypto";
|
|
6
6
|
import { loadVariables } from "./core/variables.js";
|
|
@@ -28,19 +28,13 @@ export async function runTestFile(filePath, envName) {
|
|
|
28
28
|
const resolvedMonitor = resolveMonitor(rawMonitor, projectId, envName, variables);
|
|
29
29
|
// Create secret provider: prefer hub (authenticated) for cloud secrets,
|
|
30
30
|
// fall back to env provider for fully-local runs.
|
|
31
|
-
let secretProvider;
|
|
32
31
|
const monitorV1 = { ...resolvedMonitor, id: randomUUID() };
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
// Hub not available — fall back to env provider
|
|
40
|
-
secretProvider = createEnvSecretsProvider();
|
|
41
|
-
}
|
|
32
|
+
let secretProvider;
|
|
33
|
+
try {
|
|
34
|
+
const sdk = await createSdkFromState();
|
|
35
|
+
secretProvider = new HubSecretProvider(sdk, envName);
|
|
42
36
|
}
|
|
43
|
-
|
|
37
|
+
catch {
|
|
44
38
|
secretProvider = createEnvSecretsProvider();
|
|
45
39
|
}
|
|
46
40
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@griffin-app/griffin-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.33",
|
|
4
4
|
"description": "CLI tool for running and managing griffin API tests",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"author": "",
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@griffin-app/griffin-
|
|
28
|
-
"@griffin-app/griffin-executor": "0.1.
|
|
29
|
-
"@griffin-app/griffin-
|
|
27
|
+
"@griffin-app/griffin-core": "0.2.5",
|
|
28
|
+
"@griffin-app/griffin-executor": "0.1.7",
|
|
29
|
+
"@griffin-app/griffin-hub-sdk": "1.0.29",
|
|
30
30
|
"better-auth": "^1.4.17",
|
|
31
31
|
"cli-table3": "^0.6.5",
|
|
32
32
|
"commander": "^12.1.0",
|