Skip to content

Commit e0b583b

Browse files
Copilotintel352
andauthored
fix: support dot-notation in resolveParamValue for integration step references (#108)
* Initial plan * fix: support dot-notation in resolveParamValue for step result references Co-authored-by: intel352 <77607+intel352@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
1 parent 5d24c63 commit e0b583b

2 files changed

Lines changed: 79 additions & 1 deletion

File tree

handlers/integration.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,17 +354,35 @@ func ensureConnectorConnected(ctx context.Context, connector module.IntegrationC
354354
}
355355

356356
// resolveParamValue resolves a single input value, substituting step result references where applicable.
357-
// References use the ${varName} syntax. If the variable is not found, the original value is returned.
357+
// References use the ${varName} syntax. Dot-notation is supported: ${step1.value} looks up results["step1"]
358+
// and then retrieves the "value" key from the resulting map. If the variable is not found, the original value is returned.
358359
func resolveParamValue(v any, results map[string]any) any {
359360
strVal, ok := v.(string)
360361
if !ok || len(strVal) <= 3 || strVal[0:2] != "${" || strVal[len(strVal)-1] != '}' {
361362
return v
362363
}
363364
// Extract the variable name, e.g., ${step1.value} -> step1.value
364365
varName := strVal[2 : len(strVal)-1]
366+
// Fast path: exact match in results
365367
if result, found := results[varName]; found {
366368
return result
367369
}
370+
// Dot-notation path: split on "." and traverse nested maps
371+
parts := strings.SplitN(varName, ".", 2)
372+
if len(parts) != 2 {
373+
return v
374+
}
375+
stepResult, found := results[parts[0]]
376+
if !found {
377+
return v
378+
}
379+
nested, ok := stepResult.(map[string]any)
380+
if !ok {
381+
return v
382+
}
383+
if val, found := nested[parts[1]]; found {
384+
return val
385+
}
368386
return v
369387
}
370388

handlers/integration_handler_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,3 +489,63 @@ func TestExecuteStepWithRetry_ExhaustedRetries(t *testing.T) {
489489
t.Fatal("expected error after exhausted retries")
490490
}
491491
}
492+
493+
func TestResolveParamValue_PlainValue(t *testing.T) {
494+
results := map[string]any{"step1": map[string]any{"value": "hello"}}
495+
got := resolveParamValue(42, results)
496+
if got != 42 {
497+
t.Errorf("expected 42, got %v", got)
498+
}
499+
}
500+
501+
func TestResolveParamValue_ExactMatch(t *testing.T) {
502+
results := map[string]any{"step1": "direct"}
503+
got := resolveParamValue("${step1}", results)
504+
if got != "direct" {
505+
t.Errorf("expected 'direct', got %v", got)
506+
}
507+
}
508+
509+
func TestResolveParamValue_DotNotation(t *testing.T) {
510+
results := map[string]any{
511+
"step1": map[string]any{"value": "resolved"},
512+
}
513+
got := resolveParamValue("${step1.value}", results)
514+
if got != "resolved" {
515+
t.Errorf("expected 'resolved', got %v", got)
516+
}
517+
}
518+
519+
func TestResolveParamValue_DotNotation_MissingKey(t *testing.T) {
520+
results := map[string]any{
521+
"step1": map[string]any{"other": "x"},
522+
}
523+
got := resolveParamValue("${step1.value}", results)
524+
if got != "${step1.value}" {
525+
t.Errorf("expected original string, got %v", got)
526+
}
527+
}
528+
529+
func TestResolveParamValue_DotNotation_NonMapResult(t *testing.T) {
530+
results := map[string]any{"step1": "not-a-map"}
531+
got := resolveParamValue("${step1.value}", results)
532+
if got != "${step1.value}" {
533+
t.Errorf("expected original string when step result is not a map, got %v", got)
534+
}
535+
}
536+
537+
func TestResolveParamValue_DotNotation_MissingStep(t *testing.T) {
538+
results := map[string]any{}
539+
got := resolveParamValue("${step1.value}", results)
540+
if got != "${step1.value}" {
541+
t.Errorf("expected original string when step not found, got %v", got)
542+
}
543+
}
544+
545+
func TestResolveParamValue_NotAReference(t *testing.T) {
546+
results := map[string]any{"foo": "bar"}
547+
got := resolveParamValue("just-a-string", results)
548+
if got != "just-a-string" {
549+
t.Errorf("expected 'just-a-string', got %v", got)
550+
}
551+
}

0 commit comments

Comments
 (0)