Skip to content
This repository has been archived by the owner on Oct 11, 2024. It is now read-only.

Commit

Permalink
Add test for more complicated GetOrders call
Browse files Browse the repository at this point in the history
  • Loading branch information
albrow committed Jul 29, 2020
1 parent 99aaff7 commit fcbe973
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 27 deletions.
23 changes: 20 additions & 3 deletions graphql/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,26 @@ type GetOrdersOpts struct {
func (c *Client) GetOrders(ctx context.Context, opts ...GetOrdersOpts) ([]*OrderWithMetadata, error) {
req := NewRequest(ordersQuery)

// TODO(albrow): Pass in opts.
if len(opts) > 0 {
opts := opts[0]
if len(opts.Filters) > 0 {
// Convert each filter value from the native Go type to a JSON-compatible type.
for i, filter := range opts.Filters {
jsonCompatibleValue, err := gqltypes.FilterValueToJSON(filter)
if err != nil {
return nil, err
}
opts.Filters[i].Value = jsonCompatibleValue
}
req.Var("filters", opts.Filters)
}
if len(opts.Sort) > 0 {
req.Var("sort", opts.Sort)
}
if opts.Limit != 0 {
req.Var("limit", opts.Limit)
}
}

var resp struct {
Orders []*gqltypes.OrderWithMetadata `json:"orders"`
Expand All @@ -164,5 +183,3 @@ func (c *Client) GetOrders(ctx context.Context, opts ...GetOrdersOpts) ([]*Order
}

// func (c *Client) GetStats() (*GetStatsResponse, error)
// func (c *Client) SubscribeToHeartbeat(ctx context.Context, ch chan<- string) (*rpc.ClientSubscription, error)
// func (c *Client) SubscribeToOrders(ctx context.Context, ch chan<- []*zeroex.OrderEvent) (*rpc.ClientSubscription, error)
17 changes: 7 additions & 10 deletions graphql/client/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,18 +115,10 @@ type OrderEvent struct {
}

// A filter on orders. Can be used in queries to only return orders that meet certain criteria.
type OrderFilter struct {
Field OrderField `json:"field"`
Kind FilterKind `json:"kind"`
// value must match the type of the filter field.
Value interface{} `json:"value"`
}
type OrderFilter = gqltypes.OrderFilter

// A sort ordering for orders. Can be used in queries to control the order in which results are returned.
type OrderSort struct {
Field OrderField `json:"field"`
Direction SortDirection `json:"direction"`
}
type OrderSort = gqltypes.OrderSort

// A signed 0x order along with some additional metadata about the order which is not part of the 0x protocol specification.
type OrderWithMetadata struct {
Expand Down Expand Up @@ -289,3 +281,8 @@ var AllRejectedOrderCode = gqltypes.AllRejectedOrderCode
type SortDirection = gqltypes.SortDirection

var AllSortDirection = gqltypes.AllSortDirection

const (
SortDirectionAsc = gqltypes.SortDirectionAsc
SortDirectionDesc = gqltypes.SortDirectionDesc
)
74 changes: 64 additions & 10 deletions graphql/gqltypes/conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package gqltypes
import (
"fmt"
"math/big"
"strings"

"github.com/0xProject/0x-mesh/common/types"
"github.com/0xProject/0x-mesh/db"
Expand Down Expand Up @@ -362,22 +363,75 @@ func FilterKindToDBType(kind FilterKind) (db.FilterKind, error) {
}
}

func ConvertFilterValue(f *OrderFilter) (interface{}, error) {
// FilterValueFromJSON converts the filter value from the JSON type to the
// corresponding Go type. It returns an error if the JSON type does not match
// what was expected based on the filter field.
func FilterValueFromJSON(f OrderFilter) (interface{}, error) {
switch f.Field {
case "chainID", "makerAssetAmount", "makerFee", "takerAssetAmount", "takerFee", "expirationTimeSeconds", "salt", "fillableTakerAssetAmount":
case OrderFieldChainID, OrderFieldMakerAssetAmount, OrderFieldMakerFee, OrderFieldTakerAssetAmount, OrderFieldTakerFee, OrderFieldExpirationTimeSeconds, OrderFieldSalt, OrderFieldFillableTakerAssetAmount:
return stringToBigInt(f.Value)
case "hash":
case OrderFieldHash:
return stringToHash(f.Value)
case "exchangeAddress", "makerAddress", "takerAddress", "senderAddress", "feeRecipientAddress":
case OrderFieldExchangeAddress, OrderFieldMakerAddress, OrderFieldTakerAddress, OrderFieldSenderAddress, OrderFieldFeeRecipientAddress:
return stringToAddress(f.Value)
case "makerAssetData", "makerFeeAssetData", "takerAssetData", "takerFeeAssetData":
case OrderFieldMakerAssetData, OrderFieldMakerFeeAssetData, OrderFieldTakerAssetData, OrderFieldTakerFeeAssetData:
return stringToBytes(f.Value)
default:
return "", fmt.Errorf("invalid filter field: %q", f.Field)
}
}

func filterValueToString(value interface{}) (string, error) {
// FilterValueToJSON converts the filter value from a native Go type to the
// corresponding JSON value. It returns an error if the Go type does not match
// what was expected based on the filter field.
func FilterValueToJSON(f OrderFilter) (string, error) {
switch f.Field {
case OrderFieldChainID, OrderFieldMakerAssetAmount, OrderFieldMakerFee, OrderFieldTakerAssetAmount, OrderFieldTakerFee, OrderFieldExpirationTimeSeconds, OrderFieldSalt, OrderFieldFillableTakerAssetAmount:
return bigIntToString(f.Value)
case OrderFieldHash:
return hashToString(f.Value)
case OrderFieldExchangeAddress, OrderFieldMakerAddress, OrderFieldTakerAddress, OrderFieldSenderAddress, OrderFieldFeeRecipientAddress:
return addressToString(f.Value)
case OrderFieldMakerAssetData, OrderFieldMakerFeeAssetData, OrderFieldTakerAssetData, OrderFieldTakerFeeAssetData:
return bytesToString(f.Value)
default:
return "", fmt.Errorf("invalid filter field: %q", f.Field)
}
}

func bigIntToString(value interface{}) (string, error) {
bigInt, ok := value.(*big.Int)
if !ok {
return "", fmt.Errorf("invalid type for filter value (expected *big.Int but got %T)", value)
}
return bigInt.String(), nil
}

func hashToString(value interface{}) (string, error) {
hash, ok := value.(common.Hash)
if !ok {
return "", fmt.Errorf("invalid type for filter value (expected common.Hash but got %T)", value)
}
return hash.Hex(), nil
}

func addressToString(value interface{}) (string, error) {
address, ok := value.(common.Address)
if !ok {
return "", fmt.Errorf("invalid type for filter value (expected common.Address but got %T)", value)
}
return strings.ToLower(address.Hex()), nil
}

func bytesToString(value interface{}) (string, error) {
bytes, ok := value.([]byte)
if !ok {
return "", fmt.Errorf("invalid type for filter value (expected []byte but got %T)", value)
}
return common.ToHex(bytes), nil
}

func filterValueAsString(value interface{}) (string, error) {
valueString, ok := value.(string)
if !ok {
return "", fmt.Errorf("invalid type for filter value (expected string but got %T)", value)
Expand All @@ -386,7 +440,7 @@ func filterValueToString(value interface{}) (string, error) {
}

func stringToBigInt(value interface{}) (*big.Int, error) {
valueString, err := filterValueToString(value)
valueString, err := filterValueAsString(value)
if err != nil {
return nil, err
}
Expand All @@ -398,23 +452,23 @@ func stringToBigInt(value interface{}) (*big.Int, error) {
}

func stringToHash(value interface{}) (common.Hash, error) {
valueString, err := filterValueToString(value)
valueString, err := filterValueAsString(value)
if err != nil {
return common.Hash{}, err
}
return common.HexToHash(valueString), nil
}

func stringToAddress(value interface{}) (common.Address, error) {
valueString, err := filterValueToString(value)
valueString, err := filterValueAsString(value)
if err != nil {
return common.Address{}, err
}
return common.HexToAddress(valueString), nil
}

func stringToBytes(value interface{}) ([]byte, error) {
valueString, err := filterValueToString(value)
valueString, err := filterValueAsString(value)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion graphql/schema.resolvers.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (r *queryResolver) Orders(ctx context.Context, sort []*gqltypes.OrderSort,
if err != nil {
return nil, err
}
filterValue, err := gqltypes.ConvertFilterValue(filter)
filterValue, err := gqltypes.FilterValueFromJSON(*filter)
if err != nil {
return nil, err
}
Expand Down
43 changes: 40 additions & 3 deletions integration-tests/graphql_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"encoding/json"
"fmt"
"math/big"
"sort"
"sync"
"sync/atomic"
Expand Down Expand Up @@ -141,6 +142,7 @@ func TestGetOrders(t *testing.T) {
assert.Len(t, validationResponse.Accepted, numOrders)
assert.Len(t, validationResponse.Rejected, 0)

// Get orders without any options.
actualOrders, err := client.GetOrders(ctx)
require.NoError(t, err)
require.Len(t, actualOrders, 10)
Expand Down Expand Up @@ -172,6 +174,35 @@ func TestGetOrders(t *testing.T) {
}
assertOrdersAreUnsortedEqual(t, expectedOrders, actualOrders)

// Get orders with filter, sort, and limit.
opts := gqlclient.GetOrdersOpts{
Filters: []gqlclient.OrderFilter{
{
Field: gqlclient.OrderFieldChainID,
Kind: gqlclient.FilterKindEqual,
Value: signedTestOrders[0].ChainID,
},
{
Field: gqlclient.OrderFieldExpirationTimeSeconds,
Kind: gqlclient.FilterKindGreaterOrEqual,
Value: big.NewInt(0),
},
},
Sort: []gqlclient.OrderSort{
{
Field: gqlclient.OrderFieldHash,
Direction: gqlclient.SortDirectionDesc,
},
},
Limit: 5,
}
actualOrdersWithOpts, err := client.GetOrders(ctx, opts)
require.NoError(t, err)
require.Len(t, actualOrdersWithOpts, 5)
sortOrdersByHashDesc(expectedOrders)
expectedOrdersWithOpts := expectedOrders[:5]
assertOrderSlicesAreEqual(t, expectedOrdersWithOpts, actualOrdersWithOpts)

cancel()
wg.Wait()
}
Expand Down Expand Up @@ -346,10 +377,10 @@ func assertOrdersAreUnsortedEqual(t *testing.T, expected, actual []*gqlclient.Or
// Make a copy of the given orders so we don't mess up the original when sorting them.
expectedCopy := make([]*gqlclient.OrderWithMetadata, len(expected))
copy(expectedCopy, expected)
sortOrdersByHash(expectedCopy)
sortOrdersByHashAsc(expectedCopy)
actualCopy := make([]*gqlclient.OrderWithMetadata, len(actual))
copy(actualCopy, actual)
sortOrdersByHash(actualCopy)
sortOrdersByHashAsc(actualCopy)
assertOrderSlicesAreEqual(t, expectedCopy, actualCopy)
}

Expand All @@ -373,8 +404,14 @@ func assertOrderSlicesAreEqual(t *testing.T, expected, actual []*gqlclient.Order
}
}

func sortOrdersByHash(orders []*gqlclient.OrderWithMetadata) {
func sortOrdersByHashAsc(orders []*gqlclient.OrderWithMetadata) {
sort.SliceStable(orders, func(i, j int) bool {
return bytes.Compare(orders[i].Hash.Bytes(), orders[j].Hash.Bytes()) == -1
})
}

func sortOrdersByHashDesc(orders []*gqlclient.OrderWithMetadata) {
sort.SliceStable(orders, func(i, j int) bool {
return bytes.Compare(orders[i].Hash.Bytes(), orders[j].Hash.Bytes()) == 1
})
}

0 comments on commit fcbe973

Please sign in to comment.