Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use reflect api to improve find api, more tests #149

Merged
merged 3 commits into from
Dec 6, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Use reflect api to improve find api, more tests
Signed-off-by: Aaron Sutula <[email protected]>
  • Loading branch information
asutula committed Dec 4, 2019
commit 287333202e7277146200b2f4a6a51722776b4801
22 changes: 18 additions & 4 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"reflect"

ma "github.com/multiformats/go-multiaddr"
"github.com/textileio/go-textile-core/crypto/symmetric"
Expand Down Expand Up @@ -159,10 +160,10 @@ func (c *Client) ModelHas(storeID, modelName string, entityIDs ...string) (bool,
}

// ModelFind finds records by query
func (c *Client) ModelFind(storeID, modelName string, query es.JSONQuery) ([][]byte, error) {
func (c *Client) ModelFind(storeID, modelName string, query es.JSONQuery, dummySlice interface{}) (interface{}, error) {
queryBytes, err := json.Marshal(query)
if err != nil {
return [][]byte{}, err
return nil, err
}
req := &pb.ModelFindRequest{
StoreID: storeID,
Expand All @@ -171,9 +172,22 @@ func (c *Client) ModelFind(storeID, modelName string, query es.JSONQuery) ([][]b
}
resp, err := c.client.ModelFind(c.ctx, req)
if err != nil {
return [][]byte{}, err
return nil, err
}
sliceType := reflect.TypeOf(dummySlice)
elementType := sliceType.Elem().Elem()
length := len(resp.GetEntities())
results := reflect.MakeSlice(sliceType, length, length)
for i, result := range resp.GetEntities() {
target := reflect.New(elementType).Interface()
err := json.Unmarshal(result, target)
if err != nil {
return nil, err
}
val := results.Index(i)
val.Set(reflect.ValueOf(target))
}
return resp.GetEntities(), nil
return results.Interface(), nil
}

// ModelFindByID finds a record by id
Expand Down
59 changes: 50 additions & 9 deletions api/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,18 +201,11 @@ func TestModelFind(t *testing.T) {
},
}

jsonResults, err := client.ModelFind(storeID, modelName, q)
rawResults, err := client.ModelFind(storeID, modelName, q, []*Person{})
if err != nil {
t.Fatalf("failed to find: %v", err)
}
results := make([]*Person, len(jsonResults))
for i, jsonResult := range jsonResults {
person := &Person{}
if err := json.Unmarshal(jsonResult, person); err != nil {
t.Fatalf("failed to unmarshal json result: %v", err)
}
results[i] = person
}
results := rawResults.([]*Person)
if len(results) != 1 {
t.Fatalf("expected 1 result, but got %v", len(results))
}
Expand Down Expand Up @@ -287,6 +280,30 @@ func TestReadTransaction(t *testing.T) {
if !reflect.DeepEqual(foundPerson, person) {
t.Fatal("txn model found by id does't equal the original")
}

q := es.JSONQuery{
Ands: []es.JSONCriterion{
es.JSONCriterion{
FieldPath: "lastName",
Operation: es.Eq,
Value: es.JSONValue{
String: &person.LastName,
},
},
},
}

rawResults, err := txn.Find(q, []*Person{})
if err != nil {
t.Fatalf("failed to find: %v", err)
}
results := rawResults.([]*Person)
if len(results) != 1 {
t.Fatalf("expected 1 result, but got %v", len(results))
}
if !reflect.DeepEqual(results[0], person) {
t.Fatal("model found by query does't equal the original")
}
}

func TestWriteTransaction(t *testing.T) {
Expand Down Expand Up @@ -343,6 +360,30 @@ func TestWriteTransaction(t *testing.T) {
t.Fatalf("txn model found by id does't equal the original")
}

q := es.JSONQuery{
Ands: []es.JSONCriterion{
es.JSONCriterion{
FieldPath: "lastName",
Operation: es.Eq,
Value: es.JSONValue{
String: &existingPerson.LastName,
},
},
},
}

rawResults, err := txn.Find(q, []*Person{})
if err != nil {
t.Fatalf("failed to find: %v", err)
}
results := rawResults.([]*Person)
if len(results) != 1 {
t.Fatalf("expected 1 result, but got %v", len(results))
}
if !reflect.DeepEqual(results[0], existingPerson) {
t.Fatal("model found by query does't equal the original")
}

existingPerson.Age = 99
err = txn.Save(existingPerson)
if err != nil {
Expand Down
26 changes: 20 additions & 6 deletions api/client/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package client
import (
"encoding/json"
"fmt"
"reflect"

pb "github.com/textileio/go-textile-threads/api/pb"
es "github.com/textileio/go-textile-threads/eventstore"
Expand Down Expand Up @@ -69,25 +70,38 @@ func (t *ReadTransaction) FindByID(entityID string, entity interface{}) error {
}

// Find finds entities by query
func (t *ReadTransaction) Find(query es.JSONQuery) ([][]byte, error) {
func (t *ReadTransaction) Find(query es.JSONQuery, dummySlice interface{}) (interface{}, error) {
queryBytes, err := json.Marshal(query)
if err != nil {
return [][]byte{}, err
return nil, err
}
innerReq := &pb.ModelFindRequest{QueryJSON: queryBytes}
option := &pb.ReadTransactionRequest_ModelFindRequest{ModelFindRequest: innerReq}
var resp *pb.ReadTransactionReply
if err = t.client.Send(&pb.ReadTransactionRequest{Option: option}); err != nil {
return [][]byte{}, err
return nil, err
}
if resp, err = t.client.Recv(); err != nil {
return [][]byte{}, err
return nil, err
}
switch x := resp.GetOption().(type) {
case *pb.ReadTransactionReply_ModelFindReply:
return x.ModelFindReply.GetEntities(), nil
sliceType := reflect.TypeOf(dummySlice)
elementType := sliceType.Elem().Elem()
length := len(x.ModelFindReply.GetEntities())
results := reflect.MakeSlice(sliceType, length, length)
for i, result := range x.ModelFindReply.GetEntities() {
target := reflect.New(elementType).Interface()
err := json.Unmarshal(result, target)
if err != nil {
return nil, err
}
val := results.Index(i)
val.Set(reflect.ValueOf(target))
}
return results.Interface(), nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible to make a reusable function?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea of course. Good call.

default:
return [][]byte{}, fmt.Errorf("ReadTransactionReply.Option has unexpected type %T", x)
return nil, fmt.Errorf("ReadTransactionReply.Option has unexpected type %T", x)
}
}

Expand Down
26 changes: 20 additions & 6 deletions api/client/write.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package client
import (
"encoding/json"
"fmt"
"reflect"

pb "github.com/textileio/go-textile-threads/api/pb"
es "github.com/textileio/go-textile-threads/eventstore"
Expand Down Expand Up @@ -66,25 +67,38 @@ func (t *WriteTransaction) FindByID(entityID string, entity interface{}) error {
}

// Find finds entities by query
func (t *WriteTransaction) Find(query es.JSONQuery) ([][]byte, error) {
func (t *WriteTransaction) Find(query es.JSONQuery, dummySlice interface{}) (interface{}, error) {
queryBytes, err := json.Marshal(query)
if err != nil {
return [][]byte{}, err
return nil, err
}
innerReq := &pb.ModelFindRequest{QueryJSON: queryBytes}
option := &pb.WriteTransactionRequest_ModelFindRequest{ModelFindRequest: innerReq}
if err = t.client.Send(&pb.WriteTransactionRequest{Option: option}); err != nil {
return [][]byte{}, err
return nil, err
}
var resp *pb.WriteTransactionReply
if resp, err = t.client.Recv(); err != nil {
return [][]byte{}, err
return nil, err
}
switch x := resp.GetOption().(type) {
case *pb.WriteTransactionReply_ModelFindReply:
return x.ModelFindReply.GetEntities(), nil
sliceType := reflect.TypeOf(dummySlice)
elementType := sliceType.Elem().Elem()
length := len(x.ModelFindReply.GetEntities())
results := reflect.MakeSlice(sliceType, length, length)
for i, result := range x.ModelFindReply.GetEntities() {
target := reflect.New(elementType).Interface()
err := json.Unmarshal(result, target)
if err != nil {
return nil, err
}
val := results.Index(i)
val.Set(reflect.ValueOf(target))
}
return results.Interface(), nil
default:
return [][]byte{}, fmt.Errorf("WriteTransactionReply.Option has unexpected type %T", x)
return nil, fmt.Errorf("WriteTransactionReply.Option has unexpected type %T", x)
}
}

Expand Down