mirror of https://github.com/dapr/go-sdk.git
request method verb on service invocation (#119)
* request method verb on service invocation * missing args tests * merge custom serialize * removes IDE specific stuff from project
This commit is contained in:
parent
bd7d126b4f
commit
ec13ce3483
|
@ -5,10 +5,10 @@ coverage:
|
|||
default: false
|
||||
client:
|
||||
paths: "client/"
|
||||
threshold: 0% # don't allow coverage to drop on client
|
||||
threshold: 2% # don't allow coverage to drop on client
|
||||
service:
|
||||
paths: "service"
|
||||
threshold: 3% # allow coverage to drop on service by 3% (wip)
|
||||
threshold: 2% # allow coverage to drop on service by 3% (wip)
|
||||
ignore:
|
||||
- "dapr/proto/**"
|
||||
- "example/**"
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
*.so
|
||||
*.dylib
|
||||
|
||||
# IDE
|
||||
.vscode
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${fileDirname}",
|
||||
"env": {},
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
}
|
|
@ -40,13 +40,13 @@ type Client interface {
|
|||
InvokeOutputBinding(ctx context.Context, in *BindingInvocation) error
|
||||
|
||||
// InvokeService invokes service without raw data
|
||||
InvokeService(ctx context.Context, serviceID, method string) (out []byte, err error)
|
||||
InvokeService(ctx context.Context, serviceID, method, verb string) (out []byte, err error)
|
||||
|
||||
// InvokeServiceWithContent invokes service with content
|
||||
InvokeServiceWithContent(ctx context.Context, serviceID, method string, content *DataContent) (out []byte, err error)
|
||||
InvokeServiceWithContent(ctx context.Context, serviceID, method, verb string, content *DataContent) (out []byte, err error)
|
||||
|
||||
// InvokeServiceWithCustomContent invokes service with custom content (struct + content type).
|
||||
InvokeServiceWithCustomContent(ctx context.Context, serviceID, method string, contentType string, content interface{}) (out []byte, err error)
|
||||
InvokeServiceWithCustomContent(ctx context.Context, serviceID, method, verb string, contentType string, content interface{}) (out []byte, err error)
|
||||
|
||||
// PublishEvent pubishes data onto topic in specific pubsub component.
|
||||
PublishEvent(ctx context.Context, component, topic string, in []byte) error
|
||||
|
|
|
@ -3,6 +3,7 @@ package client
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
v1 "github.com/dapr/go-sdk/dapr/proto/common/v1"
|
||||
pb "github.com/dapr/go-sdk/dapr/proto/runtime/v1"
|
||||
|
@ -38,60 +39,65 @@ func (c *GRPCClient) invokeServiceWithRequest(ctx context.Context, req *pb.Invok
|
|||
return
|
||||
}
|
||||
|
||||
// InvokeService invokes service without raw data ([]byte).
|
||||
func (c *GRPCClient) InvokeService(ctx context.Context, serviceID, method string) (out []byte, err error) {
|
||||
func verbToHTTPExtension(verb string) *v1.HTTPExtension {
|
||||
if v, ok := v1.HTTPExtension_Verb_value[strings.ToUpper(verb)]; ok {
|
||||
return &v1.HTTPExtension{Verb: v1.HTTPExtension_Verb(v)}
|
||||
}
|
||||
return &v1.HTTPExtension{Verb: v1.HTTPExtension_NONE}
|
||||
}
|
||||
|
||||
func hasRequiredInvokeArgs(serviceID, method, verb string) error {
|
||||
if serviceID == "" {
|
||||
return nil, errors.New("nil serviceID")
|
||||
return errors.New("serviceID")
|
||||
}
|
||||
if method == "" {
|
||||
return nil, errors.New("nil method")
|
||||
return errors.New("method")
|
||||
}
|
||||
if verb == "" {
|
||||
return errors.New("verb")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InvokeService invokes service without raw data ([]byte).
|
||||
func (c *GRPCClient) InvokeService(ctx context.Context, serviceID, method, verb string) (out []byte, err error) {
|
||||
if err := hasRequiredInvokeArgs(serviceID, method, verb); err != nil {
|
||||
return nil, errors.Wrap(err, "missing required parameter")
|
||||
}
|
||||
req := &pb.InvokeServiceRequest{
|
||||
Id: serviceID,
|
||||
Message: &v1.InvokeRequest{
|
||||
Method: method,
|
||||
HttpExtension: &v1.HTTPExtension{
|
||||
Verb: v1.HTTPExtension_POST,
|
||||
},
|
||||
Method: method,
|
||||
HttpExtension: verbToHTTPExtension(verb),
|
||||
},
|
||||
}
|
||||
return c.invokeServiceWithRequest(ctx, req)
|
||||
}
|
||||
|
||||
// InvokeServiceWithContent invokes service without content (data + content type).
|
||||
func (c *GRPCClient) InvokeServiceWithContent(ctx context.Context, serviceID, method string, content *DataContent) (out []byte, err error) {
|
||||
if serviceID == "" {
|
||||
return nil, errors.New("serviceID is required")
|
||||
}
|
||||
if method == "" {
|
||||
return nil, errors.New("method name is required")
|
||||
func (c *GRPCClient) InvokeServiceWithContent(ctx context.Context, serviceID, method, verb string, content *DataContent) (out []byte, err error) {
|
||||
if err := hasRequiredInvokeArgs(serviceID, method, verb); err != nil {
|
||||
return nil, errors.Wrap(err, "missing required parameter")
|
||||
}
|
||||
if content == nil {
|
||||
return nil, errors.New("content required")
|
||||
}
|
||||
|
||||
req := &pb.InvokeServiceRequest{
|
||||
Id: serviceID,
|
||||
Message: &v1.InvokeRequest{
|
||||
Method: method,
|
||||
Data: &anypb.Any{Value: content.Data},
|
||||
ContentType: content.ContentType,
|
||||
HttpExtension: &v1.HTTPExtension{
|
||||
Verb: v1.HTTPExtension_POST,
|
||||
},
|
||||
Method: method,
|
||||
Data: &anypb.Any{Value: content.Data},
|
||||
ContentType: content.ContentType,
|
||||
HttpExtension: verbToHTTPExtension(verb),
|
||||
},
|
||||
}
|
||||
|
||||
return c.invokeServiceWithRequest(ctx, req)
|
||||
}
|
||||
|
||||
// InvokeServiceWithCustomContent invokes service with custom content (struct + content type).
|
||||
func (c *GRPCClient) InvokeServiceWithCustomContent(ctx context.Context, serviceID, method string, contentType string, content interface{}) (out []byte, err error) {
|
||||
if serviceID == "" {
|
||||
return nil, errors.New("serviceID is required")
|
||||
}
|
||||
if method == "" {
|
||||
return nil, errors.New("method name is required")
|
||||
func (c *GRPCClient) InvokeServiceWithCustomContent(ctx context.Context, serviceID, method, verb string, contentType string, content interface{}) (out []byte, err error) {
|
||||
if err := hasRequiredInvokeArgs(serviceID, method, verb); err != nil {
|
||||
return nil, errors.Wrap(err, "missing required parameter")
|
||||
}
|
||||
if contentType == "" {
|
||||
return nil, errors.New("content type required")
|
||||
|
@ -101,7 +107,6 @@ func (c *GRPCClient) InvokeServiceWithCustomContent(ctx context.Context, service
|
|||
}
|
||||
|
||||
contentData, err := json.Marshal(content)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "error serializing input struct")
|
||||
}
|
||||
|
@ -109,12 +114,10 @@ func (c *GRPCClient) InvokeServiceWithCustomContent(ctx context.Context, service
|
|||
req := &pb.InvokeServiceRequest{
|
||||
Id: serviceID,
|
||||
Message: &v1.InvokeRequest{
|
||||
Method: method,
|
||||
Data: &anypb.Any{Value: contentData},
|
||||
ContentType: contentType,
|
||||
HttpExtension: &v1.HTTPExtension{
|
||||
Verb: v1.HTTPExtension_POST,
|
||||
},
|
||||
Method: method,
|
||||
Data: &anypb.Any{Value: contentData},
|
||||
ContentType: contentType,
|
||||
HttpExtension: verbToHTTPExtension(verb),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"testing"
|
||||
|
||||
v1 "github.com/dapr/go-sdk/dapr/proto/common/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -22,7 +23,6 @@ type _testStructwithSlices struct {
|
|||
}
|
||||
|
||||
// go test -timeout 30s ./client -count 1 -run ^TestInvokeServiceWithContent$
|
||||
|
||||
func TestInvokeServiceWithContent(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
data := "ping"
|
||||
|
@ -32,7 +32,7 @@ func TestInvokeServiceWithContent(t *testing.T) {
|
|||
ContentType: "text/plain",
|
||||
Data: []byte(data),
|
||||
}
|
||||
resp, err := testClient.InvokeServiceWithContent(ctx, "test", "fn", content)
|
||||
resp, err := testClient.InvokeServiceWithContent(ctx, "test", "fn", "post", content)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, string(resp), data)
|
||||
|
@ -40,18 +40,30 @@ func TestInvokeServiceWithContent(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("without content", func(t *testing.T) {
|
||||
resp, err := testClient.InvokeService(ctx, "test", "fn")
|
||||
resp, err := testClient.InvokeService(ctx, "test", "fn", "get")
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, resp)
|
||||
|
||||
})
|
||||
|
||||
t.Run("without service ID", func(t *testing.T) {
|
||||
_, err := testClient.InvokeService(ctx, "", "fn", "get")
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
t.Run("without method", func(t *testing.T) {
|
||||
_, err := testClient.InvokeService(ctx, "test", "", "get")
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
t.Run("without verb", func(t *testing.T) {
|
||||
_, err := testClient.InvokeService(ctx, "test", "fn", "")
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
t.Run("from struct with text", func(t *testing.T) {
|
||||
testdata := _testCustomContentwithText{
|
||||
Key1: "value1",
|
||||
Key2: "value2",
|
||||
}
|
||||
_, err := testClient.InvokeServiceWithCustomContent(ctx, "test", "fn", "text/plain", testdata)
|
||||
_, err := testClient.InvokeServiceWithCustomContent(ctx, "test", "fn", "post", "text/plain", testdata)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
|
@ -60,7 +72,7 @@ func TestInvokeServiceWithContent(t *testing.T) {
|
|||
Key1: "value1",
|
||||
Key2: 2500,
|
||||
}
|
||||
_, err := testClient.InvokeServiceWithCustomContent(ctx, "test", "fn", "text/plain", testdata)
|
||||
_, err := testClient.InvokeServiceWithCustomContent(ctx, "test", "fn", "post", "text/plain", testdata)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
|
@ -69,7 +81,27 @@ func TestInvokeServiceWithContent(t *testing.T) {
|
|||
Key1: []string{"value1", "value2", "value3"},
|
||||
Key2: []int{25, 40, 600},
|
||||
}
|
||||
_, err := testClient.InvokeServiceWithCustomContent(ctx, "test", "fn", "text/plain", testdata)
|
||||
_, err := testClient.InvokeServiceWithCustomContent(ctx, "test", "fn", "post", "text/plain", testdata)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVerbParsing(t *testing.T) {
|
||||
t.Run("valid lower case", func(t *testing.T) {
|
||||
v := verbToHTTPExtension("post")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, v1.HTTPExtension_POST, v.Verb)
|
||||
})
|
||||
|
||||
t.Run("valid upper case", func(t *testing.T) {
|
||||
v := verbToHTTPExtension("GET")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, v1.HTTPExtension_GET, v.Verb)
|
||||
})
|
||||
|
||||
t.Run("invalid verb", func(t *testing.T) {
|
||||
v := verbToHTTPExtension("BAD")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, v1.HTTPExtension_NONE, v.Verb)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ type StateItem struct {
|
|||
Etag string
|
||||
}
|
||||
|
||||
// StateItem represents a single state item.
|
||||
// BulkStateItem represents a single state item.
|
||||
type BulkStateItem struct {
|
||||
Key string
|
||||
Value []byte
|
||||
|
|
|
@ -71,7 +71,7 @@ func main() {
|
|||
ContentType: "text/plain",
|
||||
Data: []byte("hellow"),
|
||||
}
|
||||
resp, err := client.InvokeServiceWithContent(ctx, "serving", "echo", content)
|
||||
resp, err := client.InvokeServiceWithContent(ctx, "serving", "echo", "post", content)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue