From 1d3a8ecd663b330ee22935a0218c7b6c8b90d05a Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Sun, 7 Sep 2025 14:10:11 +0530 Subject: [PATCH] fix(variable_replace_visitor): do not skip boolean value (#9021) --- pkg/variables/variable_replace_visitor.go | 26 +++++++++- .../variable_replace_visitor_test.go | 51 +++++++++++++++---- 2 files changed, 66 insertions(+), 11 deletions(-) diff --git a/pkg/variables/variable_replace_visitor.go b/pkg/variables/variable_replace_visitor.go index f7906ef777d5..6b4cbe016e0b 100644 --- a/pkg/variables/variable_replace_visitor.go +++ b/pkg/variables/variable_replace_visitor.go @@ -444,11 +444,14 @@ func (v *variableReplacementVisitor) VisitValue(ctx *grammar.ValueContext) any { // First get the original value var originalValue string if ctx.QUOTED_TEXT() != nil { - originalValue = ctx.QUOTED_TEXT().GetText() + quotedText := ctx.QUOTED_TEXT().GetText() + originalValue = trimQuotes(quotedText) } else if ctx.NUMBER() != nil { originalValue = ctx.NUMBER().GetText() } else if ctx.KEY() != nil { originalValue = ctx.KEY().GetText() + } else if ctx.BOOL() != nil { + originalValue = ctx.BOOL().GetText() } // Check if this is a variable (starts with $) @@ -477,6 +480,10 @@ func (v *variableReplacementVisitor) VisitValue(ctx *grammar.ValueContext) any { } // Return original value if not a variable or variable not found + // If it was quoted text and not a variable, return with quotes + if ctx.QUOTED_TEXT() != nil && !strings.HasPrefix(originalValue, "$") { + return ctx.QUOTED_TEXT().GetText() + } return originalValue } @@ -515,13 +522,19 @@ func (v *variableReplacementVisitor) formatVariableValue(value any) string { case string: // Quote string values return fmt.Sprintf("'%s'", strings.ReplaceAll(val, "'", "\\'")) + case []string: + parts := make([]string, len(val)) + for i, item := range val { + parts[i] = fmt.Sprintf("'%s'", strings.ReplaceAll(item, "'", "\\'")) + } + return "[" + strings.Join(parts, ", ") + "]" case []any: // Format array values parts := make([]string, len(val)) for i, item := range val { parts[i] = v.formatVariableValue(item) } - return "(" + strings.Join(parts, ", ") + ")" + return "[" + strings.Join(parts, ", ") + "]" case int, int32, int64, float32, float64: return fmt.Sprintf("%v", val) case bool: @@ -530,3 +543,12 @@ func (v *variableReplacementVisitor) formatVariableValue(value any) string { return fmt.Sprintf("%v", val) } } + +func trimQuotes(s string) string { + if len(s) >= 2 { + if (s[0] == '"' && s[len(s)-1] == '"') || (s[0] == '\'' && s[len(s)-1] == '\'') { + return s[1 : len(s)-1] + } + } + return s +} diff --git a/pkg/variables/variable_replace_visitor_test.go b/pkg/variables/variable_replace_visitor_test.go index ed0a7bddef27..d898d35de257 100644 --- a/pkg/variables/variable_replace_visitor_test.go +++ b/pkg/variables/variable_replace_visitor_test.go @@ -26,6 +26,39 @@ func TestReplaceVariablesInExpression(t *testing.T) { }, expected: "service.name = 'auth-service'", }, + { + name: "simple bool check", + expression: "has_error = true", + variables: map[string]qbtypes.VariableItem{ + "service": { + Type: qbtypes.DynamicVariableType, + Value: "auth-service", + }, + }, + expected: "has_error = true", + }, + { + name: "variable inside quotes", + expression: "service.name ='$service'", + variables: map[string]qbtypes.VariableItem{ + "service": { + Type: qbtypes.DynamicVariableType, + Value: "auth-service", + }, + }, + expected: "service.name = 'auth-service'", + }, + { + name: "IN clause with variable inside quotes", + expression: "service.name IN '$service'", + variables: map[string]qbtypes.VariableItem{ + "service": { + Type: qbtypes.DynamicVariableType, + Value: []string{"auth-service"}, + }, + }, + expected: "service.name IN ['auth-service']", + }, { name: "simple string variable replacement", expression: "service.name = $service", @@ -101,7 +134,7 @@ func TestReplaceVariablesInExpression(t *testing.T) { Value: []any{"auth", "api", "web"}, }, }, - expected: "service.name IN ('auth', 'api', 'web')", + expected: "service.name IN ['auth', 'api', 'web']", }, { name: "array variable with mixed types", @@ -112,7 +145,7 @@ func TestReplaceVariablesInExpression(t *testing.T) { Value: []any{1, 2, "three", 4.5}, }, }, - expected: "id IN (1, 2, 'three', 4.5)", + expected: "id IN [1, 2, 'three', 4.5]", }, { name: "multiple variables in expression", @@ -185,7 +218,7 @@ func TestReplaceVariablesInExpression(t *testing.T) { Value: []any{"test", "debug"}, }, }, - expected: "service.name NOT IN ('test', 'debug')", + expected: "service.name NOT IN ['test', 'debug']", }, { name: "variable in BETWEEN clause", @@ -233,7 +266,7 @@ func TestReplaceVariablesInExpression(t *testing.T) { Value: []any{"error", "warning", "info"}, }, }, - expected: "hasAny(tags, ('error', 'warning', 'info'))", + expected: "hasAny(tags, ['error', 'warning', 'info'])", }, { name: "variable in hasToken function", @@ -255,7 +288,7 @@ func TestReplaceVariablesInExpression(t *testing.T) { Value: []any{}, }, }, - expected: "service.name IN ()", + expected: "service.name IN []", }, { name: "expression with OR and variables", @@ -306,7 +339,7 @@ func TestReplaceVariablesInExpression(t *testing.T) { Value: 500, }, }, - expected: "(service.name IN ('auth', 'api') AND env = 'prod') OR (status_code >= 500)", + expected: "(service.name IN ['auth', 'api'] AND env = 'prod') OR (status_code >= 500)", }, { name: "float variable", @@ -446,17 +479,17 @@ func TestFormatVariableValue(t *testing.T) { { name: "array of strings", value: []any{"a", "b", "c"}, - expected: "('a', 'b', 'c')", + expected: "['a', 'b', 'c']", }, { name: "array of mixed types", value: []any{"string", 123, true, 45.6}, - expected: "('string', 123, true, 45.6)", + expected: "['string', 123, true, 45.6]", }, { name: "empty array", value: []any{}, - expected: "()", + expected: "[]", }, { name: "nil value",