Skip to content

Commit

Permalink
feat: method to get json logic with solved vars
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianRuen committed Aug 1, 2023
1 parent dece775 commit 4d3c856
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 20 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

.idea/
.idea/
.vscode/
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
module github.com/CIDgravity/jsonlogic/v3
module github.com/diegoholiveira/jsonlogic/v3

go 1.14

require (
github.com/diegoholiveira/jsonlogic/v3 v3.2.7
github.com/mitchellh/copystructure v1.0.0
github.com/stretchr/testify v1.6.1
)
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/diegoholiveira/jsonlogic/v3 v3.2.7 h1:awX07pFPnlntZzRNBcO4a2Ivxa77NMt+narq/6xcS0E=
github.com/diegoholiveira/jsonlogic/v3 v3.2.7/go.mod h1:9oE8z9G+0OMxOoLHF3fhek3KuqD5CBqM0B6XFL08MSg=
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
Expand Down
52 changes: 52 additions & 0 deletions jsonlogic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -740,3 +740,55 @@ func TestIssue58_example(t *testing.T) {
expected := `{"foo":"is_bar","path":"foo_is_bar"}`
assert.JSONEq(t, expected, result.String())
}

func TestJsonLogicWithSolvedVars(t *testing.T) {
rule := json.RawMessage(`{
"or":[
{
"and":[
{"==": [{ "var":"is_foo" }, true ]},
{"==": [{ "var":"is_bar" }, true ]},
{">=": [{ "var":"foo" }, 17179869184 ]},
{"==": [{ "var":"bar" }, 0 ]}
]
},
{
"and":[
{"==": [{ "var":"is_bar" }, true ]},
{"==": [{ "var":"is_foo" }, false ]},
{"==": [{ "var":"foo" }, 34359738368 ]},
{"==": [{ "var":"bar" }, 0 ]}
]
}]
}`)

data := json.RawMessage(`{"foo": 34359738368, "bar": 10, "is_foo": false, "is_bar": true}`)

output, err := GetJsonLogicWithSolvedVars(rule, data)

if err != nil {
t.Fatal(err)
}

expected := `{
"or":[
{
"and":[
{ "==":[ false, true ] },
{ "==":[ true, true ] },
{ ">=":[ 34359738368, 17179869184 ] },
{ "==":[ 10, 0 ] }
]
},
{
"and":[
{ "==":[ true, true ] },
{ "==":[ false, false ] },
{ "==":[ 34359738368, 34359738368 ] },
{ "==":[ 10, 0 ] }
]
}]
}`

assert.JSONEq(t, expected, string(output))
}
170 changes: 158 additions & 12 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,176 @@
# Go JSON Logic (CIDgravity custom version)
# Go JSON Logic

Forked from [https://github.com/diegoholiveira/jsonlogic](https://github.com/diegoholiveira/jsonlogic)
For original documentation, view the original repository
![test workflow](https://github.com/diegoholiveira/jsonlogic/actions/workflows/test.yml/badge.svg)
[![codecov](https://codecov.io/gh/diegoholiveira/jsonlogic/branch/master/graph/badge.svg)](https://codecov.io/gh/diegoholiveira/jsonlogic)
[![Go Report Card](https://goreportcard.com/badge/github.com/diegoholiveira/jsonlogic)](https://goreportcard.com/report/github.com/diegoholiveira/jsonlogic)

Added function to generate the json logic with solved variables
Implementation of [JSON Logic](http://jsonlogic.com) in Go Lang.

## What's JSON Logic?

JSON Logic is a DSL to write logic decisions in JSON. It's has a great specification and is very simple to learn.
The [official website](http://jsonlogic.com) has a great documentation with examples.

## How to use it

The use of this library is very straightforward. Here's a simple example:

```go
package main

import (
"bytes"
"fmt"
"strings"

"github.com/diegoholiveira/jsonlogic/v3"
)

func main() {
logic := strings.NewReader(`{"==": [1, 1]}`)
data := strings.NewReader(`{}`)

var result bytes.Buffer

jsonlogic.Apply(logic, data, &result)

fmt.Println(result.String())
}
```

This will output `true` in your console.

Here's another example, but this time using variables passed in the `data` parameter:

```go
func solveVarsBackToJsonLogic(rule, data interface{}) ([]byte, error) {
ruleMap := rule.(map[string]interface{})
result := make(map[string]interface{})
package main

import (
"bytes"
"encoding/json"
"fmt"
"strings"

for operator, values := range ruleMap {
result[operator] = solveVars(values, data)
"github.com/diegoholiveira/jsonlogic/v3"
)

type (
User struct {
Name string `json:"name"`
Age int `json:"age"`
Location string `json:"location"`
}

body, err := json.Marshal(result)
Users []User
)

func main() {
logic := strings.NewReader(`{
"filter": [
{"var": "users"},
{">=": [
{"var": ".age"},
18
]}
]
}`)

data := strings.NewReader(`{
"users": [
{"name": "Diego", "age": 33, "location": "Florianópolis"},
{"name": "Jack", "age": 12, "location": "London"},
{"name": "Pedro", "age": 19, "location": "Lisbon"},
{"name": "Leopoldina", "age": 30, "location": "Rio de Janeiro"}
]
}`)

var result bytes.Buffer

err := jsonlogic.Apply(logic, data, &result)
if err != nil {
return nil, err
fmt.Println(err.Error())

return
}

return body, nil
var users Users

decoder := json.NewDecoder(&result)
decoder.Decode(&users)

for _, user := range users {
fmt.Printf(" - %s\n", user.Name)
}
}
```

If you have a function you want to expose as a JSON Logic operation, you can use:

```go
package main

import (
"bytes"
"fmt"
"strings"

"github.com/diegoholiveira/jsonlogic/v3"
)

func main() {
// add a new operator "strlen" for get string length
jsonlogic.AddOperator("strlen", func(values, data interface{}) interface{} {
v, ok := values.(string)
if ok {
return len(v)
}
return 0
})

logic := strings.NewReader(`{ "strlen": { "var": "foo" } }`)
data := strings.NewReader(`{"foo": "bar"}`)

var result bytes.Buffer

jsonlogic.Apply(logic, data, &result)

fmt.Println(result.String()) // the string length of "bar" is 3
}
```

If you want to get the json logic used, with the variables replaced by their values :

```go
package main

import (
"fmt"
"encoding/json"

"github.com/diegoholiveira/jsonlogic/v3"
)

func main() {
logic := json.RawMessage(`{ "==":[{ "var":"foo" }, true] }`)
data := json.RawMessage(`{"foo": "false"}`)

result, err := jsonlogic.GetJsonLogicWithSolvedVars(logic, data)

if err != nil {
fmt.Println(err)
}

fmt.Println(string(result)) // will output { "==":[false, true] }
}

```

# License

This project is licensed under the MIT License - see the LICENSE file for details



For example, if you specify the folowing rules model :


Expand Down
15 changes: 12 additions & 3 deletions vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package jsonlogic

import (
"encoding/json"
"strconv"
"strings"
)

Expand Down Expand Up @@ -107,19 +108,27 @@ func getVar(value, data interface{}) interface{} {
return _value
}

func solveVarsBackToJsonLogic(rule, data interface{}) ([]byte, error) {
func solveVarsBackToJsonLogic(rule, data interface{}) (json.RawMessage, error) {
ruleMap := rule.(map[string]interface{})
result := make(map[string]interface{})

for operator, values := range ruleMap {
result[operator] = solveVars(values, data)
}

body, err := json.Marshal(result)
resultJson, err := json.Marshal(result)

if err != nil {
return nil, err
}

Check warning on line 123 in vars.go

View check run for this annotation

Codecov / codecov/patch/pr_coverage

vars.go#L122-L123

Added lines #L122 - L123 were not covered by tests

return body, nil
// we need to use Unquote due to unicode characters (example \u003e= need to be >=)
// used for prettier json.RawMessage
resultEscaped, err := strconv.Unquote(strings.Replace(strconv.Quote(string(resultJson)), `\\u`, `\u`, -1))

if err != nil {
return nil, err
}

Check warning on line 131 in vars.go

View check run for this annotation

Codecov / codecov/patch/pr_coverage

vars.go#L130-L131

Added lines #L130 - L131 were not covered by tests

return []byte(resultEscaped), nil
}

0 comments on commit 4d3c856

Please sign in to comment.