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
7 changes: 4 additions & 3 deletions .github/workflows/pull_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
go mod download
- name: Run Test
run: |
go test -v -count=10 ./...
go test -count=10 ./...
lint:
strategy:
matrix:
Expand All @@ -51,7 +51,7 @@ jobs:
release_check:
runs-on: ubuntu-latest
outputs:
git_diff: ${{ steps.changes.outputs.git_diff }}
git_diff: ${{ steps.output.outputs.git_diff }}
steps:
- name: Checkout repository
uses: actions/checkout@v2
Expand All @@ -61,11 +61,12 @@ jobs:
uses: technote-space/get-diff-action@v6
with:
PATTERNS: |
+**/*.go
**/*.go
!**/*_test.go
FILES: |
go.mod
- name: Output Diff
id: output
if: env.GIT_DIFF
run: |
echo "::set-output name=git_diff::${{ env.GIT_DIFF }}"
Expand Down
9 changes: 5 additions & 4 deletions .github/workflows/push_main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
go mod download
- name: Run Test
run: |
go test -v -count=10 ./...
go test -count=10 ./...
lint:
strategy:
matrix:
Expand Down Expand Up @@ -61,7 +61,7 @@ jobs:
go mod download
- name: Run Test
run: |
go test -v -coverprofile=coverage.txt -covermode=atomic -count=10 ./...
go test -coverprofile=coverage.txt -covermode=atomic -count=10 ./...
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2
with:
Expand All @@ -73,7 +73,7 @@ jobs:
runs-on: ubuntu-latest
needs: [test, lint]
outputs:
git_diff: ${{ steps.changes.outputs.git_diff }}
git_diff: ${{ steps.output.outputs.git_diff }}
steps:
- name: Checkout repository
uses: actions/checkout@v2
Expand All @@ -83,12 +83,13 @@ jobs:
uses: technote-space/get-diff-action@v6
with:
PATTERNS: |
+**/*.go
**/*.go
!**/*_test.go
FILES: |
go.mod
- name: Output Diff
if: env.GIT_DIFF
id: output
run: |
echo "::set-output name=git_diff::${{ env.GIT_DIFF }}"
release:
Expand Down
42 changes: 42 additions & 0 deletions script/standard/complex_operators.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,45 @@ func (op *selectorOperator) Evaluate(parameters map[string]interface{}) (interfa
}
return value, nil
}

type inOperator struct {
arg1, arg2 interface{}
}

func (op *inOperator) Evaluate(parameters map[string]interface{}) (interface{}, error) {
var item interface{} = op.arg1
if numValue, err := getNumber(op.arg1, parameters); err == nil {
item = numValue
} else if strValue, err := getString(op.arg1, parameters); err == nil {
item = strValue
} else if boolValue, err := getBoolean(op.arg1, parameters); err == nil {
item = boolValue
}

elements, err := getElements(op.arg2, parameters)
if err != nil {
return nil, err
}

for _, element := range elements {
if element == item {
return true, nil
}
}

return false, nil
}

type notInOperator struct {
arg1, arg2 interface{}
}

func (op *notInOperator) Evaluate(parameters map[string]interface{}) (interface{}, error) {
inOperator := &inOperator{arg1: op.arg1, arg2: op.arg2}
val, err := inOperator.Evaluate(parameters)
if err != nil {
return nil, err
}
boolVal := val.(bool)
return !boolVal, nil
}
160 changes: 160 additions & 0 deletions script/standard/complex_operators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,163 @@ func Test_selectorOperator(t *testing.T) {
}
batchOperatorTests(t, tests)
}

func Test_inOperator(t *testing.T) {
currentDSelector, _ := newSelectorOperator("@.d", &ScriptEngine{}, nil)

tests := []*operatorTest{
{
input: operatorTestInput{
operator: &inOperator{arg1: nil, arg2: nil},
paramters: map[string]interface{}{},
},
expected: operatorTestExpected{
err: "invalid argument. is nil",
},
},
{
input: operatorTestInput{
operator: &inOperator{arg1: nil, arg2: []interface{}{"one"}},
paramters: map[string]interface{}{},
},
expected: operatorTestExpected{
value: false,
},
},
{
input: operatorTestInput{
operator: &inOperator{arg1: "one", arg2: []interface{}{"one"}},
paramters: map[string]interface{}{},
},
expected: operatorTestExpected{
value: true,
},
},
{
input: operatorTestInput{
operator: &inOperator{arg1: "one", arg2: `["one","two"]`},
paramters: map[string]interface{}{},
},
expected: operatorTestExpected{
value: true,
},
},
{
input: operatorTestInput{
operator: &inOperator{arg1: "one", arg2: `{"1":"one","2":"two"}`},
paramters: map[string]interface{}{},
},
expected: operatorTestExpected{
value: true,
},
},
{
input: operatorTestInput{
operator: &inOperator{arg1: "1", arg2: `[1,2,3]`},
paramters: map[string]interface{}{},
},
expected: operatorTestExpected{
value: true,
},
},
{
input: operatorTestInput{
operator: &inOperator{arg1: "1", arg2: `["1","2","3"]`},
paramters: map[string]interface{}{},
},
expected: operatorTestExpected{
value: false,
},
},
{
input: operatorTestInput{
operator: &inOperator{
arg1: "2",
arg2: currentDSelector,
},
paramters: map[string]interface{}{
"@": map[string]interface{}{
"d": []interface{}{
float64(1),
float64(2),
float64(3),
},
},
},
},
expected: operatorTestExpected{
value: true,
},
},
}
batchOperatorTests(t, tests)
}

func Test_notInOperator(t *testing.T) {
tests := []*operatorTest{
{
input: operatorTestInput{
operator: &notInOperator{arg1: nil, arg2: nil},
paramters: map[string]interface{}{},
},
expected: operatorTestExpected{
err: "invalid argument. is nil",
},
},
{
input: operatorTestInput{
operator: &notInOperator{arg1: nil, arg2: []interface{}{"one"}},
paramters: map[string]interface{}{},
},
expected: operatorTestExpected{
value: true,
},
},
{
input: operatorTestInput{
operator: &notInOperator{arg1: "one", arg2: []interface{}{"one"}},
paramters: map[string]interface{}{},
},
expected: operatorTestExpected{
value: false,
},
},
{
input: operatorTestInput{
operator: &notInOperator{arg1: "one", arg2: `["one","two"]`},
paramters: map[string]interface{}{},
},
expected: operatorTestExpected{
value: false,
},
},
{
input: operatorTestInput{
operator: &notInOperator{arg1: "one", arg2: `{"1":"one","2":"two"}`},
paramters: map[string]interface{}{},
},
expected: operatorTestExpected{
value: false,
},
},
{
input: operatorTestInput{
operator: &notInOperator{arg1: "1", arg2: `[1,2,3]`},
paramters: map[string]interface{}{},
},
expected: operatorTestExpected{
value: false,
},
},
{
input: operatorTestInput{
operator: &notInOperator{arg1: "1", arg2: `["1","2","3"]`},
paramters: map[string]interface{}{},
},
expected: operatorTestExpected{
value: true,
},
},
}
batchOperatorTests(t, tests)
}
14 changes: 11 additions & 3 deletions script/standard/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ import (
"github.com/evilmonkeyinc/jsonpath/script"
)

// TODO : add tests for what is in readme
// TODO : update readme to give more details, maybe add readme to this package and link from main
// TODO : add support for bitwise operators | &^ ^ & << >> after + and -
var defaultTokens []string = []string{
"||", "&&",
"==", "!=", "<=", ">=", "<", ">", "=~", "!",
"+", "-",
"**", "*", "/", "%",
"@", "$",
"not in", "in", "@", "$",
}

// ScriptEngine standard implementation of the script engine interface
Expand Down Expand Up @@ -123,6 +121,16 @@ func (engine *ScriptEngine) buildOperators(expression string, tokens []string, o
arg1: leftside,
arg2: rightside,
}, nil
case "not in":
return &notInOperator{
arg1: leftside,
arg2: rightside,
}, nil
case "in":
return &inOperator{
arg1: leftside,
arg2: rightside,
}, nil
case "!":
if leftside != nil {
// There should not be a left side to this operator
Expand Down
33 changes: 33 additions & 0 deletions script/standard/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,39 @@ func Test_ScriptEngine_buildOperators(t *testing.T) {
err: "",
},
},
{
input: input{
expression: "1 in [1]",
tokens: defaultTokens,
},
expected: expected{
operator: &inOperator{arg1: "1", arg2: []interface{}{float64(1)}},
err: "",
},
},
{
input: input{
expression: "1 not in [1]",
tokens: defaultTokens,
},
expected: expected{
operator: &notInOperator{arg1: "1", arg2: []interface{}{float64(1)}},
err: "",
},
},
{
input: input{
expression: "1 in [1] && 1 not in [2]",
tokens: defaultTokens,
},
expected: expected{
operator: &andOperator{
arg1: &inOperator{arg1: "1", arg2: []interface{}{float64(1)}},
arg2: &notInOperator{arg1: "1", arg2: []interface{}{float64(2)}},
},
err: "",
},
},
}

for idx, test := range tests {
Expand Down
15 changes: 8 additions & 7 deletions script/standard/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import (
)

var (
errUnsupportedOperator error = fmt.Errorf("unsupported operator")
errInvalidArgument error = fmt.Errorf("invalid argument")
errInvalidArgumentNil error = fmt.Errorf("%w. is nil", errInvalidArgument)
errInvalidArgumentExpectedInteger error = fmt.Errorf("%w. expected integer", errInvalidArgument)
errInvalidArgumentExpectedNumber error = fmt.Errorf("%w. expected number", errInvalidArgument)
errInvalidArgumentExpectedBoolean error = fmt.Errorf("%w. expected boolean", errInvalidArgument)
errInvalidArgumentExpectedRegex error = fmt.Errorf("%w. expected a valid regexp", errInvalidArgument)
errUnsupportedOperator error = fmt.Errorf("unsupported operator")
errInvalidArgument error = fmt.Errorf("invalid argument")
errInvalidArgumentNil error = fmt.Errorf("%w. is nil", errInvalidArgument)
errInvalidArgumentExpectedInteger error = fmt.Errorf("%w. expected integer", errInvalidArgument)
errInvalidArgumentExpectedNumber error = fmt.Errorf("%w. expected number", errInvalidArgument)
errInvalidArgumentExpectedBoolean error = fmt.Errorf("%w. expected boolean", errInvalidArgument)
errInvalidArgumentExpectedRegex error = fmt.Errorf("%w. expected a valid regexp", errInvalidArgument)
errInvalidArgumentExpectedCollection error = fmt.Errorf("%w. expected array, map, or slice", errInvalidArgument)
)

func getInvalidExpressionEmptyError() error {
Expand Down
Loading