Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions issues_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,3 +361,50 @@ func TestIssue110(t *testing.T) {
expected := `["111","333"]`
assert.JSONEq(t, expected, result.String())
}

func TestIssue125_InOperatorWithVarsInSlice(t *testing.T) {
// This test demonstrates the issue: vars within slices are not resolved
rule := strings.NewReader(`{"in": [{"var": "needle"}, [{"var": "item1"}, {"var": "item2"}]]}`)
data := strings.NewReader(`{"needle":"foo", "item1":"bar", "item2":"foo"}`)

var result bytes.Buffer
err := jsonlogic.Apply(rule, data, &result)
if err != nil {
t.Fatal(err)
}

// Should be true because "foo" should be found in the resolved array ["bar", "foo"]
// Currently fails because it compares "foo" against unresolved [{"var": "item1"}, {"var": "item2"}]
expected := `true`
assert.JSONEq(t, expected, result.String())
}

func TestIssue125_CustomOperatorWithVarsInSlice(t *testing.T) {
// Add a custom operator that processes slice elements
jsonlogic.AddOperator("contains_any", func(values, data any) any {
parsed := values.([]any)
needle := parsed[0]
haystack := parsed[1].([]any)

for _, item := range haystack {
if item == needle {
return true
}
}
return false
})

rule := strings.NewReader(`{"contains_any": [{"var": "needle"}, [{"var": "item1"}, {"var": "item2"}]]}`)
data := strings.NewReader(`{"needle":"foo", "item1":"bar", "item2":"foo"}`)

var result bytes.Buffer
err := jsonlogic.Apply(rule, data, &result)
if err != nil {
t.Fatal(err)
}

// Should be true because "foo" should be found in the resolved array ["bar", "foo"]
// Currently fails because the custom operator receives unresolved [{"var": "item1"}, {"var": "item2"}]
expected := `true`
assert.JSONEq(t, expected, result.String())
}
10 changes: 1 addition & 9 deletions jsonlogic.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,21 +162,13 @@ func parseValues(values, data any) any {
if typing.IsMap(value) {
parsed = append(parsed, apply(value, data))
} else {
parsed = append(parsed, value)
parsed = append(parsed, parseValues(value, data))
}
}

return parsed
}

// If values represents a map (an operation), returns the result. Otherwise returns the
// values without parsing. This means that each of the returned values might be a subtree
// of JSONLogic.
// Used in lazy evaluation of "AND", "OR", and "IF" operators
func getValuesWithoutParsing(values, data any) any {
return values.([]any)
}

func apply(rules, data any) any {
ruleMap := rules.(map[string]any)

Expand Down
6 changes: 3 additions & 3 deletions logic.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
)

func _and(values, data any) any {
values = getValuesWithoutParsing(values, data)
values = values.([]any)

var v float64

Expand Down Expand Up @@ -46,7 +46,7 @@ func _and(values, data any) any {
}

func _or(values, data any) any {
values = getValuesWithoutParsing(values, data)
values = values.([]any)

for _, value := range values.([]any) {
parsed := parseValues(value, data)
Expand All @@ -69,7 +69,7 @@ func evaluateClause(clause any, data any) any {
}

func conditional(values, data any) any {
values = getValuesWithoutParsing(values, data)
values = values.([]any)

clauses := values.([]any)

Expand Down