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

Add --header/-H to dapr invoke #1287

Closed
wants to merge 15 commits into from
Closed
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
26 changes: 25 additions & 1 deletion cmd/invoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"net/http"
"os"
"runtime"
"strings"

"github.com/spf13/cobra"

Expand All @@ -34,6 +35,7 @@ var (
invokeVerb string
invokeDataFile string
invokeSocket string
invokeHeaders = make([]string, 0)
)

var InvokeCmd = &cobra.Command{
Expand All @@ -43,6 +45,9 @@ var InvokeCmd = &cobra.Command{
# Invoke a sample method on target app with POST Verb
dapr invoke --app-id target --method sample --data '{"key":"value"}

# Invoke a sample method on target app with customized Header
mukundansundar marked this conversation as resolved.
Show resolved Hide resolved
dapr invoke --app-id target --method sample --data '{"key":"value"} --header Header1=Value1 --header Header2=Value2

# Invoke a sample method on target app with GET Verb
dapr invoke --app-id target --method sample --verb GET

Expand Down Expand Up @@ -78,7 +83,25 @@ dapr invoke --unix-domain-socket --app-id target --method sample --verb GET
}
}

response, err := client.Invoke(invokeAppID, invokeAppMethod, bytePayload, invokeVerb, invokeSocket)
header := http.Header{}
for _, h := range invokeHeaders {
p := strings.Split(strings.TrimSpace(h), "=")
if len(p) != 2 {
print.FailureStatusEvent(os.Stderr, "Should one \"=\" in HTTP header.")
os.Exit(1)
}

if p[0] == "" {
print.FailureStatusEvent(os.Stderr, "A header name is required.")
os.Exit(1)
} else if p[1] == "" {
print.FailureStatusEvent(os.Stderr, "Value for header name is required.")
os.Exit(1)
}
header.Add(p[0], p[1])
}

response, err := client.Invoke(invokeAppID, invokeAppMethod, bytePayload, invokeVerb, header, invokeSocket)
if err != nil {
err = fmt.Errorf("error invoking app %s: %w", invokeAppID, err)
print.FailureStatusEvent(os.Stderr, err.Error())
Expand All @@ -98,6 +121,7 @@ func init() {
InvokeCmd.Flags().StringVarP(&invokeData, "data", "d", "", "The JSON serialized data string (optional)")
InvokeCmd.Flags().StringVarP(&invokeVerb, "verb", "v", defaultHTTPVerb, "The HTTP verb to use")
InvokeCmd.Flags().StringVarP(&invokeDataFile, "data-file", "f", "", "A file containing the JSON serialized data (optional)")
InvokeCmd.Flags().StringArrayVarP(&invokeHeaders, "header", "H", []string{}, "HTTP headers to be used on invoke")
InvokeCmd.Flags().BoolP("help", "h", false, "Print this help message")
InvokeCmd.Flags().StringVarP(&invokeSocket, "unix-domain-socket", "u", "", "Path to a unix domain socket dir. If specified, Dapr API servers will use Unix Domain Sockets")
InvokeCmd.MarkFlagRequired("app-id")
Expand Down
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/Pallinder/sillyname-go v0.0.0-20130730142914-97aeae9e6ba1
github.com/briandowns/spinner v1.19.0
github.com/dapr/dapr v1.11.0
github.com/dapr/go-sdk v1.6.0
github.com/dapr/go-sdk v1.8.0
github.com/docker/docker v20.10.21+incompatible
github.com/fatih/color v1.15.0
github.com/gocarina/gocsv v0.0.0-20220927221512-ad3251f9fa25
Expand Down Expand Up @@ -38,6 +38,7 @@ require (
require (
github.com/Masterminds/semver/v3 v3.2.0
github.com/evanphx/json-patch v5.6.0+incompatible
google.golang.org/grpc v1.55.0
)

require (
Expand Down Expand Up @@ -88,6 +89,7 @@ require (
github.com/fasthttp/router v1.4.18 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect
github.com/go-chi/chi/v5 v5.0.8 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-gorp/gorp/v3 v3.0.2 // indirect
github.com/go-kit/log v0.2.1 // indirect
Expand Down Expand Up @@ -220,7 +222,6 @@ require (
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.54.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
Expand Down
12 changes: 7 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,8 @@ github.com/dapr/components-contrib v1.11.0-rc.11 h1:PFUTCCfZ+99BIorCNR+mB/CEGnGF
github.com/dapr/components-contrib v1.11.0-rc.11/go.mod h1:prx2ATX6wFnR6Cp1xXGW3J9s5Gyz9AtOrG0xBf7QnHI=
github.com/dapr/dapr v1.11.0 h1:sKDHKyehNm7xY2z6oXoUIcj2lXqKnlkAYPkvroldrwU=
github.com/dapr/dapr v1.11.0/go.mod h1:++3iGjeJcpraxZxdZi1EblplcCXgu/Ycz0Q/u+MYvxw=
github.com/dapr/go-sdk v1.6.0 h1:jg5A2khSCHF8bGZsig5RWN/gD0jjitszc2V6Uq2pPdY=
github.com/dapr/go-sdk v1.6.0/go.mod h1:KLQBltoD9K0w5hKTihdcyg9Epob9gypwL5dYcQzPro4=
github.com/dapr/go-sdk v1.8.0 h1:OEleeL3zUTqXxIZ7Vkk3PClAeCh1g8sZ1yR2JFZKfXM=
github.com/dapr/go-sdk v1.8.0/go.mod h1:MBcTKXg8PmBc8A968tVWQg1Xt+DZtmeVR6zVVVGcmeA=
github.com/dapr/kit v0.11.2 h1:4tJre4OWyOfBFDZeDvEIC+7nYgTRgqpCvo4/bfB2sN8=
github.com/dapr/kit v0.11.2/go.mod h1:Iq5mKuZnmO+Lyu7MC8755YWv+mp3h7/nomRQcjZfN5k=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -430,6 +430,8 @@ github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2H
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew=
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
Expand Down Expand Up @@ -520,8 +522,8 @@ github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOW
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand Down Expand Up @@ -1767,8 +1769,8 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
Expand Down
4 changes: 3 additions & 1 deletion pkg/standalone/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ limitations under the License.

package standalone

import "net/http"

type DaprProcess interface {
List() ([]ListOutput, error)
}
Expand All @@ -22,7 +24,7 @@ type daprProcess struct{}
// Client is the interface the wraps all the methods exposed by the Dapr CLI.
type Client interface {
// Invoke is a command to invoke a remote or local dapr instance.
Invoke(appID, method string, data []byte, verb string, socket string) (string, error)
Invoke(appID, method string, data []byte, verb string, header http.Header, socket string) (string, error)
// Publish is used to publish event to a topic in a pubsub for an app ID.
Publish(publishAppID, pubsubName, topic string, payload []byte, socket string, metadata map[string]interface{}) error
}
Expand Down
8 changes: 7 additions & 1 deletion pkg/standalone/invoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
)

// Invoke is a command to invoke a remote or local dapr instance.
func (s *Standalone) Invoke(appID, method string, data []byte, verb string, path string) (string, error) {
func (s *Standalone) Invoke(appID, method string, data []byte, verb string, header http.Header, path string) (string, error) {
list, err := s.process.List()
if err != nil {
return "", err
Expand All @@ -39,7 +39,13 @@ func (s *Standalone) Invoke(appID, method string, data []byte, verb string, path
if err != nil {
return "", err
}

req.Header.Set("Content-Type", "application/json")
for h, vs := range header {
for _, v := range vs {
req.Header.Add(h, v)
}
}

var httpc http.Client

Expand Down
28 changes: 24 additions & 4 deletions pkg/standalone/invoke_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package standalone

import (
"fmt"
"net/http"
"os"
"runtime"
"testing"
Expand All @@ -35,13 +36,15 @@ func TestInvoke(t *testing.T) {
listErr error
expectedPath string
postResponse string
header http.Header
resp string
}{
{
name: "list apps error",
errorExpected: true,
errString: assert.AnError.Error(),
listErr: assert.AnError,
header: nil,
},
{
name: "appID not found",
Expand All @@ -51,6 +54,7 @@ func TestInvoke(t *testing.T) {
lo: ListOutput{
AppID: "testapp",
},
header: nil,
},
{
name: "appID found successful invoke empty response",
Expand All @@ -59,6 +63,7 @@ func TestInvoke(t *testing.T) {
lo: ListOutput{
AppID: "testapp",
},
header: nil,
},
{
name: "appID found successful invoke",
Expand All @@ -69,8 +74,23 @@ func TestInvoke(t *testing.T) {
},
expectedPath: "/v1.0/invoke/testapp/method/test",
postResponse: "test payload",
header: nil,
resp: "successful invoke",
},
{
name: "appID found successful invoke with customized header",
Copy link
Collaborator

Choose a reason for hiding this comment

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

please add one test with multiple header values.

appID: "testapp",
method: "test",
lo: ListOutput{
AppID: "testapp",
},
expectedPath: "/v1.0/invoke/testapp/method/test",
postResponse: "test payload",
resp: "successful invoke",
header: http.Header{
"Customized-Header": []string{"Value"},
},
},
}

for _, socket := range []string{"", "/tmp"} {
Expand Down Expand Up @@ -105,7 +125,7 @@ func TestInvoke(t *testing.T) {
},
}

res, err := client.Invoke(tc.appID, tc.method, []byte(tc.resp), "GET", socket)
res, err := client.Invoke(tc.appID, tc.method, []byte(tc.resp), "GET", tc.header, socket)
if tc.errorExpected {
assert.Error(t, err, "expected an error")
assert.Equal(t, tc.errString, err.Error(), "expected error strings to match")
Expand Down Expand Up @@ -137,7 +157,7 @@ func TestInvoke(t *testing.T) {
Err: tc.listErr,
},
}
res, err := client.Invoke(tc.appID, tc.method, []byte(tc.resp), "POST", socket)
res, err := client.Invoke(tc.appID, tc.method, []byte(tc.resp), "POST", tc.header, socket)
if tc.errorExpected {
assert.Error(t, err, "expected an error")
assert.Equal(t, tc.errString, err.Error(), "expected error strings to match")
Expand Down Expand Up @@ -169,7 +189,7 @@ func TestInvoke(t *testing.T) {
Err: tc.listErr,
},
}
res, err := client.Invoke(tc.appID, tc.method, []byte(tc.resp), "DELETE", socket)
res, err := client.Invoke(tc.appID, tc.method, []byte(tc.resp), "DELETE", tc.header, socket)
if tc.errorExpected {
assert.Error(t, err, "expected an error")
assert.Equal(t, tc.errString, err.Error(), "expected error strings to match")
Expand Down Expand Up @@ -202,7 +222,7 @@ func TestInvoke(t *testing.T) {
Err: tc.listErr,
},
}
res, err := client.Invoke(tc.appID, tc.method, []byte(tc.resp), "PUT", socket)
res, err := client.Invoke(tc.appID, tc.method, []byte(tc.resp), "PUT", tc.header, socket)
if tc.errorExpected {
assert.Error(t, err, "expected an error")
assert.Equal(t, tc.errString, err.Error(), "expected error strings to match")
Expand Down
21 changes: 21 additions & 0 deletions tests/e2e/standalone/invoke_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
daprHttp "github.com/dapr/go-sdk/service/http"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/metadata"
)

func StartTestService(t *testing.T, port int) common.Service {
Expand All @@ -38,6 +39,18 @@ func StartTestService(t *testing.T, port int) common.Service {
ContentType: e.ContentType,
DataTypeURL: e.DataTypeURL,
}

md, ok := metadata.FromIncomingContext(ctx)
if ok {
data := []byte{}
for _, header := range []string{"Header1", "Header2"} {
values := md.Get(header)
if len(values) > 0 {
data = append(data, []byte(values[0])...)
}
}
val.Data = data
}
return val, nil
})

Expand Down Expand Up @@ -108,6 +121,14 @@ func TestStandaloneInvoke(t *testing.T) {
assert.Contains(t, output, "error invoking app invoke_e2e: 404 Not Found")
})

t.Run(fmt.Sprintf("invoke mehod %s with http headers", path), func(t *testing.T) {
output, err := cmdInvoke("invoke_e2e", "test", path, "--header", "Header1=aValue1", "--header", "Header2=aValue2")
t.Log(output)
assert.NoError(t, err, "")
assert.Contains(t, output, "aValue1")
assert.Contains(t, output, "aValue2")
})

output, err := cmdStopWithAppID("invoke_e2e")
t.Log(output)
require.NoError(t, err, "dapr stop failed")
Expand Down