Skip to content

Commit fc3f4b5

Browse files
feat: bump go and add subdomains when running in server mode
* feat(deps): bump golang from 1.23-alpine to 1.24-alpine Bumps golang from 1.23-alpine to 1.24-alpine. --- updated-dependencies: - dependency-name: golang dependency-version: 1.24-alpine dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> * feat: update go mod too * feat: add the ability to set a subdomain in a request when a test is executed --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: xavidop <xavi_tb@hotmail.com>
1 parent 8da0f9a commit fc3f4b5

File tree

15 files changed

+240
-32
lines changed

15 files changed

+240
-32
lines changed

cloudrun.Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Build stage
2-
FROM golang:1.23-alpine AS builder
2+
FROM golang:1.24-alpine AS builder
33

44
WORKDIR /app
55

docs/docs/server/api-endpoints.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Content-Type: application/json
1414
1515
{
1616
"api_key": "your_api_key (optional)",
17+
"voiceflow_subdomain": "your_custom_subdomain (optional)",
1718
"suite": {
1819
"name": "Example Suite",
1920
"description": "Suite used as an example",
@@ -60,6 +61,20 @@ Content-Type: application/json
6061

6162
Executes a test suite asynchronously and returns an execution ID for tracking. The suite configuration and tests are now embedded directly in the request body, making the API more HTTP-friendly and eliminating the need for file system access.
6263

64+
### Request Parameters
65+
66+
- `api_key` (optional): Override the global Voiceflow API key for this specific test execution
67+
- `voiceflow_subdomain` (optional): Override the global Voiceflow subdomain for this specific test execution. This allows you to test against different Voiceflow environments or custom subdomains without affecting the global configuration
68+
- `suite`: The test suite configuration containing the test definitions
69+
70+
### Using Custom Subdomains
71+
72+
When you specify a `voiceflow_subdomain`, the API will use that subdomain for all interactions in the test suite. For example:
73+
74+
- If you set `"voiceflow_subdomain": "my-custom-env"`, requests will be sent to `https://general-runtime.my-custom-env.voiceflow.com`
75+
- If you omit this field, the global subdomain configuration will be used
76+
- This is particularly useful for testing against staging environments or customer-specific deployments
77+
6378
## Get Test Status
6479
```http
6580
GET /api/v1/tests/status/{execution_id}

docs/docs/server/introduction.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ The Voiceflow CLI now includes an HTTP API server that exposes test execution fu
88
- **Real-time Logging**: Capture and return test execution logs in API responses
99
- **OpenAPI/Swagger**: Auto-generated API documentation at `/swagger/index.html`
1010
- **Asynchronous Execution**: Non-blocking test execution with status tracking
11+
- **Custom Subdomains**: Support for per-request Voiceflow subdomain overrides
12+
- **Multi-Environment Testing**: Concurrent testing against different environments
13+
- **Thread Safety**: No race conditions when testing multiple subdomains simultaneously
1114
- **CORS Support**: Enable cross-origin requests for web frontends
1215
- **Health Checks**: Built-in health check endpoints
1316

docs/docs/server/usage-examples.md

Lines changed: 111 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,47 @@
66
```bash
77
curl -X POST http://localhost:8080/api/v1/tests/execute \
88
-H "Content-Type: application/json" \
9-
-d '{"api_key": "your_api_key (optional)","suite": {"name": "Example Suite","description": "Suite used as an example","environment_name": "production","tests": [{"id": "test_1","test": {"name": "Example test","description": "These are some tests","interactions": [{"id": "test_1_1","user": {"type": "text","text": "hi"},"agent": {"validate": [{"type": "contains","value": "hello"}]}}}]}}}]}}'
9+
-d '{"api_key": "your_api_key (optional)","voiceflow_subdomain": "your_custom_subdomain (optional)","suite": {"name": "Example Suite","description": "Suite used as an example","environment_name": "production","tests": [{"id": "test_1","test": {"name": "Example test","description": "These are some tests","interactions": [{"id": "test_1_1","user": {"type": "text","text": "hi"},"agent": {"validate": [{"type": "contains","value": "hello"}]}}]}}]}}'
10+
```
11+
12+
### 1b. Start a test execution with custom subdomain
13+
```bash
14+
curl -X POST http://localhost:8080/api/v1/tests/execute \
15+
-H "Content-Type: application/json" \
16+
-d '{
17+
"api_key": "VF.DM.YOUR_API_KEY",
18+
"voiceflow_subdomain": "staging-env",
19+
"suite": {
20+
"name": "Staging Environment Test",
21+
"description": "Testing against staging environment",
22+
"environment_name": "production",
23+
"tests": [
24+
{
25+
"id": "staging_test_1",
26+
"test": {
27+
"name": "Basic staging test",
28+
"description": "Test against staging subdomain",
29+
"interactions": [
30+
{
31+
"id": "staging_interaction_1",
32+
"user": {
33+
"type": "launch"
34+
},
35+
"agent": {
36+
"validate": [
37+
{
38+
"type": "contains",
39+
"value": "welcome"
40+
}
41+
]
42+
}
43+
}
44+
]
45+
}
46+
}
47+
]
48+
}
49+
}'
1050
```
1151

1252
### 2. Check test status
@@ -22,14 +62,15 @@ curl http://localhost:8080/health
2262
## Using JavaScript/fetch
2363

2464
```javascript
25-
// Execute a test suite
65+
// Execute a test suite with custom subdomain
2666
const response = await fetch('http://localhost:8080/api/v1/tests/execute', {
2767
method: 'POST',
2868
headers: {
2969
'Content-Type': 'application/json',
3070
},
3171
body: JSON.stringify({
32-
api_key: "your_api_key (optional)",
72+
api_key: "VF.DM.YOUR_API_KEY",
73+
voiceflow_subdomain: "staging-env", // Optional: use custom subdomain
3374
suite: {
3475
name: "Example Suite",
3576
description: "Suite used as an example",
@@ -72,6 +113,33 @@ const statusResponse = await fetch(`http://localhost:8080/api/v1/tests/status/${
72113
const status = await statusResponse.json();
73114
console.log('Status:', status.status);
74115
console.log('Logs:', status.logs);
116+
117+
// Example with multiple environments
118+
const environments = [
119+
{ name: "production", subdomain: "" }, // Use global subdomain
120+
{ name: "staging", subdomain: "staging-env" },
121+
{ name: "development", subdomain: "dev-env" }
122+
];
123+
124+
for (const env of environments) {
125+
const envResponse = await fetch('http://localhost:8080/api/v1/tests/execute', {
126+
method: 'POST',
127+
headers: { 'Content-Type': 'application/json' },
128+
body: JSON.stringify({
129+
api_key: "VF.DM.YOUR_API_KEY",
130+
voiceflow_subdomain: env.subdomain,
131+
suite: {
132+
name: `${env.name} Test Suite`,
133+
description: `Testing against ${env.name} environment`,
134+
environment_name: "production",
135+
tests: [/* your tests here */]
136+
}
137+
})
138+
});
139+
140+
const envExecution = await envResponse.json();
141+
console.log(`${env.name} execution started:`, envExecution.id);
142+
}
75143
```
76144

77145
## Using Python requests
@@ -80,9 +148,10 @@ console.log('Logs:', status.logs);
80148
import requests
81149
import time
82150

83-
# Execute a test suite
151+
# Execute a test suite with custom subdomain
84152
response = requests.post('http://localhost:8080/api/v1/tests/execute', json={
85-
'api_key': 'your_api_key (optional)',
153+
'api_key': 'VF.DM.YOUR_API_KEY',
154+
'voiceflow_subdomain': 'staging-env', # Optional: use custom subdomain
86155
'suite': {
87156
'name': 'Example Suite',
88157
'description': 'Suite used as an example',
@@ -132,4 +201,41 @@ while True:
132201
break
133202

134203
time.sleep(1)
204+
205+
# Example: Testing multiple environments
206+
environments = [
207+
{"name": "production", "subdomain": ""}, # Use global subdomain
208+
{"name": "staging", "subdomain": "staging-env"},
209+
{"name": "development", "subdomain": "dev-env"}
210+
]
211+
212+
execution_ids = []
213+
214+
for env in environments:
215+
print(f"\nStarting test for {env['name']} environment...")
216+
217+
response = requests.post('http://localhost:8080/api/v1/tests/execute', json={
218+
'api_key': 'VF.DM.YOUR_API_KEY',
219+
'voiceflow_subdomain': env['subdomain'],
220+
'suite': {
221+
'name': f"{env['name']} Test Suite",
222+
'description': f"Testing against {env['name']} environment",
223+
'environment_name': 'production',
224+
'tests': [
225+
# Your test definitions here
226+
]
227+
}
228+
})
229+
230+
execution = response.json()
231+
execution_ids.append((env['name'], execution['id']))
232+
print(f"{env['name']} execution started: {execution['id']}")
233+
234+
# Monitor all executions
235+
print("\nMonitoring all executions...")
236+
for env_name, execution_id in execution_ids:
237+
print(f"\nChecking {env_name} ({execution_id})...")
238+
status_response = requests.get(f"http://localhost:8080/api/v1/tests/status/{execution_id}")
239+
status = status_response.json()
240+
print(f"Status: {status['status']}")
135241
```

docs/docs/static/swagger.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,10 @@
199199
},
200200
"suite": {
201201
"$ref": "#/definitions/TestSuiteRequest"
202+
},
203+
"voiceflow_subdomain": {
204+
"description": "Optional subdomain to override global.VoiceflowSubdomain",
205+
"type": "string"
202206
}
203207
}
204208
},

docs/docs/static/swagger.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ definitions:
3131
type: string
3232
suite:
3333
$ref: '#/definitions/TestSuiteRequest'
34+
voiceflow_subdomain:
35+
description: Optional subdomain to override global.VoiceflowSubdomain
36+
type: string
3437
required:
3538
- suite
3639
type: object

go.mod

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
module github.com/xavidop/voiceflow-cli
22

3-
go 1.23.0
4-
5-
toolchain go1.23.6
3+
go 1.24
64

75
require (
86
github.com/pkg/errors v0.9.1

pkg/dialog/replay.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func Replay(userID, environment, recordFile string) error {
6464
time.Sleep(500 * time.Millisecond)
6565

6666
// Send interaction to Voiceflow
67-
responses, err := voiceflow.DialogManagerInteract(environment, userID, testInteraction, "")
67+
responses, err := voiceflow.DialogManagerInteract(environment, userID, testInteraction, "", "")
6868
if err != nil {
6969
return fmt.Errorf("error during dialog: %v", err)
7070
}

pkg/dialog/start.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func Start(userID, environment, recordFile string, saveTest bool) error {
9090
userID = uuid.New().String()
9191
}
9292

93-
responses, err := voiceflow.DialogManagerInteract(environment, userID, launchInteraction, "")
93+
responses, err := voiceflow.DialogManagerInteract(environment, userID, launchInteraction, "", "")
9494
if err != nil {
9595
return fmt.Errorf("error starting dialog: %v", err)
9696
}
@@ -150,7 +150,7 @@ func Start(userID, environment, recordFile string, saveTest bool) error {
150150
}
151151

152152
// Send user input to Voiceflow
153-
responses, err := voiceflow.DialogManagerInteract(environment, userID, interaction, "")
153+
responses, err := voiceflow.DialogManagerInteract(environment, userID, interaction, "", "")
154154
if err != nil {
155155
return fmt.Errorf("error during dialog: %v", err)
156156
}

pkg/test/execute.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ import (
99

1010
// HTTPSuiteRequest represents a test suite from HTTP request
1111
type HTTPSuiteRequest struct {
12-
Name string `json:"name"`
13-
Description string `json:"description"`
14-
EnvironmentName string `json:"environment_name"`
15-
Tests []HTTPTestRequest `json:"tests"`
16-
ApiKey string `json:"api_key,omitempty"` // Optional token to override global.VoiceflowAPIKey
12+
Name string `json:"name"`
13+
Description string `json:"description"`
14+
EnvironmentName string `json:"environment_name"`
15+
Tests []HTTPTestRequest `json:"tests"`
16+
ApiKey string `json:"api_key,omitempty"` // Optional token to override global.VoiceflowAPIKey
17+
VoiceflowSubdomain string `json:"voiceflow_subdomain,omitempty"` // Optional subdomain to override global.VoiceflowSubdomain
1718
}
1819

1920
// HTTPTestRequest represents a test from HTTP request
@@ -62,6 +63,10 @@ func executeHTTPSuite(suiteReq HTTPSuiteRequest, logCollector *LogCollector) err
6263
// Define the user ID
6364
userID := uuid.New().String()
6465

66+
if suiteReq.VoiceflowSubdomain != "" {
67+
logCollector.AddLog("Using Voiceflow subdomain: " + suiteReq.VoiceflowSubdomain)
68+
}
69+
6570
logCollector.AddLog("Suite: " + suiteReq.Name)
6671
logCollector.AddLog("Description: " + suiteReq.Description)
6772
logCollector.AddLog("Environment: " + suiteReq.EnvironmentName)
@@ -71,7 +76,7 @@ func executeHTTPSuite(suiteReq HTTPSuiteRequest, logCollector *LogCollector) err
7176
// Execute each test directly from the request data
7277
for _, testReq := range suiteReq.Tests {
7378
logCollector.AddLog("Running Test ID: " + testReq.ID)
74-
err := runTest(suiteReq.EnvironmentName, userID, testReq.Test, suiteReq.ApiKey, logCollector)
79+
err := runTest(suiteReq.EnvironmentName, userID, testReq.Test, suiteReq.ApiKey, suiteReq.VoiceflowSubdomain, logCollector)
7580
if err != nil {
7681
errorMsg := "Error running test " + testReq.ID + ": " + err.Error()
7782
logCollector.AddLog(errorMsg)
@@ -107,7 +112,7 @@ func ExecuteSuite(suitesPath string) error {
107112
}
108113
// Create a dummy log collector for the existing file-based execution
109114
logCollector := &LogCollector{Logs: []string{}}
110-
err = runTest(suite.EnvironmentName, userID, test, "", logCollector) // No token provided, will use global.VoiceflowAPIKey
115+
err = runTest(suite.EnvironmentName, userID, test, "", "", logCollector) // No token or subdomain provided, will use global values
111116
if err != nil {
112117
global.Log.Errorf("Error running test: %v", err)
113118
return err

0 commit comments

Comments
 (0)