diff --git a/.gitignore b/.gitignore index c61e685..bc16316 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out -.idea/ \ No newline at end of file +.idea/ +.vscode/ \ No newline at end of file diff --git a/jsonlogic.go b/jsonlogic.go index 734ce79..bd5c676 100644 --- a/jsonlogic.go +++ b/jsonlogic.go @@ -515,6 +515,28 @@ func Apply(rule, data io.Reader, result io.Writer) error { return json.NewEncoder(result).Encode(output) } +func GetJsonLogicWithSolvedVars(rule, data json.RawMessage) ([]byte, error) { + if data == nil { + data = json.RawMessage("{}") + } + + // parse rule and data from json.RawMessage to interface + var _rule interface{} + var _data interface{} + + err := json.Unmarshal(rule, &_rule) + if err != nil { + return nil, err + } + + err = json.Unmarshal(data, &_data) + if err != nil { + return nil, err + } + + return solveVarsBackToJsonLogic(_rule, _data) +} + func ApplyRaw(rule, data json.RawMessage) (json.RawMessage, error) { if data == nil { data = json.RawMessage("{}") diff --git a/jsonlogic_test.go b/jsonlogic_test.go index 709620d..1747f23 100644 --- a/jsonlogic_test.go +++ b/jsonlogic_test.go @@ -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)) +} diff --git a/readme.md b/readme.md index c752bcc..8e4547b 100644 --- a/readme.md +++ b/readme.md @@ -138,6 +138,66 @@ func main() { } ``` +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 : + + +```json +{ + "and":[ + { "==":[{ "var":"VariableA" }, true] }, + { "==":[{ "var":"VariableB" }, true] }, + { ">=":[{ "var":"VariableC" }, 17179869184] }, + { "==":[{ "var":"VariableD" }, "0"] }, + { "<":[{ "var":"VariableE" }, 20] } + ] +} + +``` + +You will get as output, the folowing response (using a specific data, all variables will be replaced with matching values) : + +```json +{ + "and":[ + { "==":[false, true] }, + { "==":[true, true] }, + { ">=":[34359738368, 17179869184] }, + { "==":[12, "0"] }, + { "<":[14, 20] } + ] +} + +``` \ No newline at end of file diff --git a/vars.go b/vars.go index 31fc2ac..46294d5 100644 --- a/vars.go +++ b/vars.go @@ -1,6 +1,8 @@ package jsonlogic import ( + "encoding/json" + "strconv" "strings" ) @@ -105,3 +107,28 @@ func getVar(value, data interface{}) interface{} { return _value } + +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) + } + + resultJson, err := json.Marshal(result) + + if err != nil { + return nil, err + } + + // 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 + } + + return []byte(resultEscaped), nil +}