oracle: implement filters
This commit is contained in:
parent
e4528e59dc
commit
c420014cb5
6 changed files with 113 additions and 0 deletions
1
go.mod
1
go.mod
|
@ -1,6 +1,7 @@
|
|||
module github.com/nspcc-dev/neo-go
|
||||
|
||||
require (
|
||||
github.com/PaesslerAG/jsonpath v0.1.1
|
||||
github.com/Workiva/go-datastructures v1.0.50
|
||||
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible
|
||||
|
|
5
go.sum
5
go.sum
|
@ -8,6 +8,11 @@ github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
|
|||
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PaesslerAG/gval v1.0.0 h1:GEKnRwkWDdf9dOmKcNrar9EA1bz1z9DqPIO1+iLzhd8=
|
||||
github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I=
|
||||
github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8=
|
||||
github.com/PaesslerAG/jsonpath v0.1.1 h1:c1/AToHQMVsduPAa4Vh6xp2U0evy4t8SWp8imEsylIk=
|
||||
github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY=
|
||||
github.com/Workiva/go-datastructures v1.0.50 h1:slDmfW6KCHcC7U+LP3DDBbm4fqTwZGn1beOFPfGaLvo=
|
||||
github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA=
|
||||
github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw=
|
||||
|
|
|
@ -140,6 +140,10 @@ func TestOracle(t *testing.T) {
|
|||
putOracleRequest(t, cs.Hash, bc, "http://get.maxallowed", nil, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cs.Hash, bc, "http://get.maxallowed", nil, "handle", []byte{}, 100_000_000)
|
||||
|
||||
flt := "Values[1]"
|
||||
putOracleRequest(t, cs.Hash, bc, "http://get.filter", &flt, "handle", []byte{}, 10_000_000)
|
||||
putOracleRequest(t, cs.Hash, bc, "http://get.filterinv", &flt, "handle", []byte{}, 10_000_000)
|
||||
|
||||
checkResp := func(t *testing.T, id uint64, resp *transaction.OracleResponse) *state.OracleRequest {
|
||||
req, err := oracleCtr.GetRequestInternal(bc.dao, id)
|
||||
require.NoError(t, err)
|
||||
|
@ -242,6 +246,19 @@ func TestOracle(t *testing.T) {
|
|||
Result: make([]byte, transaction.MaxOracleResultSize),
|
||||
})
|
||||
})
|
||||
t.Run("WithFilter", func(t *testing.T) {
|
||||
checkResp(t, 10, &transaction.OracleResponse{
|
||||
ID: 10,
|
||||
Code: transaction.Success,
|
||||
Result: []byte(`[2]`),
|
||||
})
|
||||
t.Run("invalid response", func(t *testing.T) {
|
||||
checkResp(t, 11, &transaction.OracleResponse{
|
||||
ID: 11,
|
||||
Code: transaction.Error,
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestOracleFull(t *testing.T) {
|
||||
|
@ -354,6 +371,14 @@ func newDefaultHTTPClient() oracle.HTTPClient {
|
|||
code: http.StatusOK,
|
||||
body: make([]byte, transaction.MaxOracleResultSize),
|
||||
},
|
||||
"http://get.filter": {
|
||||
code: http.StatusOK,
|
||||
body: []byte(`{"Values":["one", 2, 3],"Another":null}`),
|
||||
},
|
||||
"http://get.filterinv": {
|
||||
code: http.StatusOK,
|
||||
body: []byte{0xFF},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
24
pkg/services/oracle/filter.go
Normal file
24
pkg/services/oracle/filter.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package oracle
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/PaesslerAG/jsonpath"
|
||||
)
|
||||
|
||||
func filter(value []byte, path string) ([]byte, error) {
|
||||
if !utf8.Valid(value) {
|
||||
return nil, errors.New("not an UTF-8")
|
||||
}
|
||||
var v interface{}
|
||||
if err := json.Unmarshal(value, &v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err := jsonpath.Get(path, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal([]interface{}{result})
|
||||
}
|
50
pkg/services/oracle/filter_test.go
Normal file
50
pkg/services/oracle/filter_test.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package oracle
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
js := `{
|
||||
"Stores": [ "Lambton Quay", "Willis Street" ],
|
||||
"Manufacturers": [
|
||||
{
|
||||
"Name": "Acme Co",
|
||||
"Products": [
|
||||
{ "Name": "Anvil", "Price": 50 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "Contoso",
|
||||
"Products": [
|
||||
{ "Name": "Elbow Grease", "Price": 99.95 },
|
||||
{ "Name": "Headlight Fluid", "Price": 4 }
|
||||
]
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
testCases := []struct {
|
||||
result, path string
|
||||
}{
|
||||
{`["Acme Co"]`, "Manufacturers[0].Name"},
|
||||
{`[50]`, "Manufacturers[0].Products[0].Price"},
|
||||
{`["Elbow Grease"]`, "Manufacturers[1].Products[0].Name"},
|
||||
{`[{"Name":"Elbow Grease","Price":99.95}]`, "Manufacturers[1].Products[0]"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.path, func(t *testing.T) {
|
||||
actual, err := filter([]byte(js), tc.path)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.result, string(actual))
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("not an UTF-8", func(t *testing.T) {
|
||||
_, err := filter([]byte{0xFF}, "Manufacturers[0].Name")
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
|
@ -115,6 +115,14 @@ func (o *Oracle) processRequest(priv *keys.PrivateKey, req request) error {
|
|||
}
|
||||
break
|
||||
}
|
||||
if req.Req.Filter != nil {
|
||||
res, err := filter(result, *req.Req.Filter)
|
||||
if err != nil {
|
||||
resp.Code = transaction.Error
|
||||
break
|
||||
}
|
||||
result = res
|
||||
}
|
||||
resp.Code = transaction.Success
|
||||
resp.Result = result
|
||||
case r.StatusCode == http.StatusForbidden:
|
||||
|
|
Loading…
Reference in a new issue