From 8e75e2704125b412e50133133efa2bb5b7ffd56f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 05:28:55 +0000 Subject: [PATCH 1/2] Initial plan From 42cf30dc39156cfb10af0a9e11369fcb91bf42c6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 05:32:28 +0000 Subject: [PATCH 2/2] fix: support dot-notation in resolveParamValue for step result references Co-authored-by: intel352 <77607+intel352@users.noreply.github.com> --- handlers/integration.go | 20 +++++++++- handlers/integration_handler_test.go | 60 ++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/handlers/integration.go b/handlers/integration.go index 034a6332..ec8d0dd3 100644 --- a/handlers/integration.go +++ b/handlers/integration.go @@ -354,7 +354,8 @@ func ensureConnectorConnected(ctx context.Context, connector module.IntegrationC } // resolveParamValue resolves a single input value, substituting step result references where applicable. -// References use the ${varName} syntax. If the variable is not found, the original value is returned. +// References use the ${varName} syntax. Dot-notation is supported: ${step1.value} looks up results["step1"] +// and then retrieves the "value" key from the resulting map. If the variable is not found, the original value is returned. func resolveParamValue(v any, results map[string]any) any { strVal, ok := v.(string) if !ok || len(strVal) <= 3 || strVal[0:2] != "${" || strVal[len(strVal)-1] != '}' { @@ -362,9 +363,26 @@ func resolveParamValue(v any, results map[string]any) any { } // Extract the variable name, e.g., ${step1.value} -> step1.value varName := strVal[2 : len(strVal)-1] + // Fast path: exact match in results if result, found := results[varName]; found { return result } + // Dot-notation path: split on "." and traverse nested maps + parts := strings.SplitN(varName, ".", 2) + if len(parts) != 2 { + return v + } + stepResult, found := results[parts[0]] + if !found { + return v + } + nested, ok := stepResult.(map[string]any) + if !ok { + return v + } + if val, found := nested[parts[1]]; found { + return val + } return v } diff --git a/handlers/integration_handler_test.go b/handlers/integration_handler_test.go index 58d7b69c..3f64c74d 100644 --- a/handlers/integration_handler_test.go +++ b/handlers/integration_handler_test.go @@ -489,3 +489,63 @@ func TestExecuteStepWithRetry_ExhaustedRetries(t *testing.T) { t.Fatal("expected error after exhausted retries") } } + +func TestResolveParamValue_PlainValue(t *testing.T) { + results := map[string]any{"step1": map[string]any{"value": "hello"}} + got := resolveParamValue(42, results) + if got != 42 { + t.Errorf("expected 42, got %v", got) + } +} + +func TestResolveParamValue_ExactMatch(t *testing.T) { + results := map[string]any{"step1": "direct"} + got := resolveParamValue("${step1}", results) + if got != "direct" { + t.Errorf("expected 'direct', got %v", got) + } +} + +func TestResolveParamValue_DotNotation(t *testing.T) { + results := map[string]any{ + "step1": map[string]any{"value": "resolved"}, + } + got := resolveParamValue("${step1.value}", results) + if got != "resolved" { + t.Errorf("expected 'resolved', got %v", got) + } +} + +func TestResolveParamValue_DotNotation_MissingKey(t *testing.T) { + results := map[string]any{ + "step1": map[string]any{"other": "x"}, + } + got := resolveParamValue("${step1.value}", results) + if got != "${step1.value}" { + t.Errorf("expected original string, got %v", got) + } +} + +func TestResolveParamValue_DotNotation_NonMapResult(t *testing.T) { + results := map[string]any{"step1": "not-a-map"} + got := resolveParamValue("${step1.value}", results) + if got != "${step1.value}" { + t.Errorf("expected original string when step result is not a map, got %v", got) + } +} + +func TestResolveParamValue_DotNotation_MissingStep(t *testing.T) { + results := map[string]any{} + got := resolveParamValue("${step1.value}", results) + if got != "${step1.value}" { + t.Errorf("expected original string when step not found, got %v", got) + } +} + +func TestResolveParamValue_NotAReference(t *testing.T) { + results := map[string]any{"foo": "bar"} + got := resolveParamValue("just-a-string", results) + if got != "just-a-string" { + t.Errorf("expected 'just-a-string', got %v", got) + } +}