mirror of https://github.com/dapr/go-sdk.git
Compare commits
1 Commits
main
...
v1.9.0-rc-
Author | SHA1 | Date |
---|---|---|
|
add6b1c6b5 |
|
@ -1 +0,0 @@
|
|||
*.go text eol=lf
|
|
@ -4,7 +4,3 @@ updates:
|
|||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/.github/workflows/dapr-bot"
|
||||
schedule:
|
||||
interval: "daily"
|
|
@ -1,6 +0,0 @@
|
|||
organization: dapr
|
||||
defaultSticker: clrqfdv4x24910fl5n4iwu5oa
|
||||
stickers:
|
||||
-
|
||||
id: clrqfdv4x24910fl5n4iwu5oa
|
||||
alias: sdk-badge
|
|
@ -1,21 +0,0 @@
|
|||
# Description
|
||||
|
||||
<!--
|
||||
Please explain the changes you've made.
|
||||
-->
|
||||
|
||||
## Issue reference
|
||||
|
||||
<!--
|
||||
We strive to have all PR being opened based on an issue, where the problem or feature have been discussed prior to implementation.
|
||||
-->
|
||||
|
||||
Please reference the issue this PR will close: #_[issue number]_
|
||||
|
||||
## Checklist
|
||||
|
||||
Please make sure you've completed the relevant tasks for this PR, out of the following list:
|
||||
|
||||
* [ ] Code compiles correctly
|
||||
* [ ] Created/updated tests
|
||||
* [ ] Extended the documentation
|
|
@ -1,34 +0,0 @@
|
|||
name: dapr-bot
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
bot-run:
|
||||
name: bot-processor
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
contents: read
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version-file: "./.github/workflows/dapr-bot/go.mod"
|
||||
cache-dependency-path: |
|
||||
./.github/workflows/dapr-bot/
|
||||
|
||||
- name: go-bot-mod
|
||||
working-directory: ./.github/workflows/dapr-bot/
|
||||
run: go mod tidy
|
||||
|
||||
- name: go-bot-run
|
||||
working-directory: ./.github/workflows/dapr-bot/
|
||||
run: go run .
|
|
@ -1,17 +0,0 @@
|
|||
GO_COMPAT_VERSION=1.22
|
||||
|
||||
.PHONY: cover
|
||||
cover:
|
||||
go test -coverprofile=cover.out ./ && go tool cover -html=cover.out
|
||||
|
||||
.PHONY: tidy
|
||||
tidy: ## Updates the go modules
|
||||
go mod tidy -compat=$(GO_COMPAT_VERSION)
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -count=1 \
|
||||
-race \
|
||||
-coverprofile=coverage.txt \
|
||||
-covermode=atomic \
|
||||
./...
|
|
@ -1,95 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-github/v55/github"
|
||||
)
|
||||
|
||||
var (
|
||||
errCommentBodyEmpty = errors.New("comment body is empty")
|
||||
errIssueClosed = errors.New("issue is closed")
|
||||
errIssueAlreadyAssigned = errors.New("issue is already assigned")
|
||||
errUnauthorizedClient = errors.New("possibly unauthorized client issue")
|
||||
)
|
||||
|
||||
type issueInterface interface {
|
||||
CreateComment(ctx context.Context, owner string, repo string, number int, comment *github.IssueComment) (*github.IssueComment, *github.Response, error)
|
||||
AddAssignees(ctx context.Context, owner string, repo string, number int, assignees []string) (*github.Issue, *github.Response, error)
|
||||
}
|
||||
|
||||
type Bot struct {
|
||||
ctx context.Context
|
||||
issueClient issueInterface
|
||||
}
|
||||
|
||||
func NewBot(ghClient *github.Client) *Bot {
|
||||
return &Bot{
|
||||
ctx: context.Background(),
|
||||
issueClient: ghClient.Issues,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bot) HandleEvent(ctx context.Context, event Event) (res string, err error) {
|
||||
commentBody := event.IssueCommentEvent.Comment.GetBody()
|
||||
|
||||
// split the comment after any potential new lines
|
||||
newline := strings.Split(strings.ReplaceAll(commentBody, "\r\n", "\n"), "\n")[0]
|
||||
|
||||
command := strings.Split(newline, " ")[0]
|
||||
|
||||
if command[0] != '/' {
|
||||
return "no command found", err
|
||||
}
|
||||
|
||||
switch command {
|
||||
case "/assign":
|
||||
assignee, err := b.AssignIssueToCommenter(event)
|
||||
res = "👍 Issue assigned to " + assignee
|
||||
if err == nil {
|
||||
err = b.CreateIssueComment("🚀 Issue assigned to you @"+assignee, event)
|
||||
} else {
|
||||
err = b.CreateIssueComment("⚠️ Unable to assign issue", event)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Sprintf("failed to comment on issue: %v", err), err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (b *Bot) CreateIssueComment(body string, event Event) error {
|
||||
if body == "" {
|
||||
return errCommentBodyEmpty
|
||||
}
|
||||
ctx := context.Background()
|
||||
comment := &github.IssueComment{
|
||||
Body: github.String(body),
|
||||
}
|
||||
_, response, err := b.issueClient.CreateComment(ctx, event.GetIssueOrg(), event.GetIssueRepo(), event.GetIssueNumber(), comment)
|
||||
if err != nil || response.StatusCode == http.StatusNotFound {
|
||||
return fmt.Errorf("failed to create comment: %v%v", err, response.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bot) AssignIssueToCommenter(event Event) (string, error) {
|
||||
if event.GetIssueState() == "closed" {
|
||||
return "", errIssueClosed
|
||||
}
|
||||
|
||||
if len(event.GetIssueAssignees()) > 0 {
|
||||
return "", errIssueAlreadyAssigned
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
_, response, err := b.issueClient.AddAssignees(ctx, event.GetIssueOrg(), event.GetIssueRepo(), event.GetIssueNumber(), []string{event.GetIssueUser()})
|
||||
if response.StatusCode == http.StatusNotFound {
|
||||
return "", errUnauthorizedClient
|
||||
}
|
||||
return event.GetIssueUser(), err
|
||||
}
|
|
@ -1,207 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-github/v55/github"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var testBot = &Bot{
|
||||
ctx: context.Background(),
|
||||
issueClient: &testClient{},
|
||||
}
|
||||
|
||||
type testClient struct {
|
||||
issue *github.Issue
|
||||
issueComment *github.IssueComment
|
||||
resp *github.Response
|
||||
}
|
||||
|
||||
func (tc *testClient) CreateComment(ctx context.Context, org, repo string, number int, comment *github.IssueComment) (*github.IssueComment, *github.Response, error) {
|
||||
return tc.issueComment, tc.resp, nil
|
||||
}
|
||||
|
||||
func (tc *testClient) AddAssignees(ctx context.Context, org, repo string, number int, assignees []string) (*github.Issue, *github.Response, error) {
|
||||
return tc.issue, tc.resp, nil
|
||||
}
|
||||
|
||||
func TestNewBot(t *testing.T) {
|
||||
t.Run("create a bot test", func(t *testing.T) {
|
||||
bot := NewBot(github.NewClient(nil))
|
||||
assert.NotNil(t, bot)
|
||||
})
|
||||
}
|
||||
|
||||
func TestHandleEvent(t *testing.T) {
|
||||
t.Run("handle valid event", func(t *testing.T) {
|
||||
tc := testClient{
|
||||
resp: &github.Response{Response: &http.Response{StatusCode: http.StatusOK}},
|
||||
}
|
||||
testBot.issueClient = &tc
|
||||
ctx := context.Background()
|
||||
var testEventCopy Event
|
||||
errC := copier.CopyWithOption(&testEventCopy, &testEvent, copier.Option{DeepCopy: true})
|
||||
if errC != nil {
|
||||
t.Error(errC)
|
||||
}
|
||||
testEventCopy.IssueCommentEvent.Comment.Body = github.String("/assign")
|
||||
res, err := testBot.HandleEvent(ctx, testEventCopy)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, res)
|
||||
})
|
||||
|
||||
t.Run("handle valid (longer body) event", func(t *testing.T) {
|
||||
tc := testClient{
|
||||
resp: &github.Response{Response: &http.Response{StatusCode: http.StatusOK}},
|
||||
}
|
||||
testBot.issueClient = &tc
|
||||
ctx := context.Background()
|
||||
var testEventCopy Event
|
||||
errC := copier.CopyWithOption(&testEventCopy, &testEvent, copier.Option{DeepCopy: true})
|
||||
if errC != nil {
|
||||
t.Error(errC)
|
||||
}
|
||||
testEventCopy.IssueCommentEvent.Comment.Body = github.String("/assign \r \ntest body")
|
||||
res, err := testBot.HandleEvent(ctx, testEventCopy)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, res)
|
||||
})
|
||||
|
||||
t.Run("handle unable to assign", func(t *testing.T) {
|
||||
tc := testClient{
|
||||
resp: &github.Response{Response: &http.Response{StatusCode: http.StatusNotFound}},
|
||||
}
|
||||
testBot.issueClient = &tc
|
||||
ctx := context.Background()
|
||||
var testEventCopy Event
|
||||
errC := copier.CopyWithOption(&testEventCopy, &testEvent, copier.Option{DeepCopy: true})
|
||||
if errC != nil {
|
||||
t.Error(errC)
|
||||
}
|
||||
testEventCopy.IssueCommentEvent.Comment.Body = github.String("/assign")
|
||||
res, err := testBot.HandleEvent(ctx, testEventCopy)
|
||||
require.Error(t, err)
|
||||
assert.NotEmpty(t, res)
|
||||
})
|
||||
|
||||
t.Run("handle no event", func(t *testing.T) {
|
||||
tc := testClient{}
|
||||
testBot.issueClient = &tc
|
||||
ctx := context.Background()
|
||||
var testEventCopy Event
|
||||
errC := copier.CopyWithOption(&testEventCopy, &testEvent, copier.Option{DeepCopy: true})
|
||||
if errC != nil {
|
||||
t.Error(errC)
|
||||
}
|
||||
testEventCopy.IssueCommentEvent.Comment.Body = github.String("assign")
|
||||
res, err := testBot.HandleEvent(ctx, testEventCopy)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "no command found", res)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCreateIssueComment(t *testing.T) {
|
||||
t.Run("failure to create issue comment", func(t *testing.T) {
|
||||
tc := testClient{
|
||||
resp: &github.Response{Response: &http.Response{StatusCode: http.StatusNotFound}},
|
||||
}
|
||||
testBot.issueClient = &tc
|
||||
err := testBot.CreateIssueComment("test", testEvent)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("create issue comment", func(t *testing.T) {
|
||||
tc := testClient{
|
||||
resp: &github.Response{Response: &http.Response{StatusCode: http.StatusOK}},
|
||||
}
|
||||
testBot.issueClient = &tc
|
||||
err := testBot.CreateIssueComment("test", testEvent)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("create issue comment with empty body", func(t *testing.T) {
|
||||
tc := testClient{
|
||||
resp: &github.Response{Response: &http.Response{StatusCode: http.StatusOK}},
|
||||
}
|
||||
testBot.issueClient = &tc
|
||||
err := testBot.CreateIssueComment("", testEvent)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAssignIssueToCommenter(t *testing.T) {
|
||||
t.Run("failure to assign issue to commenter", func(t *testing.T) {
|
||||
tc := testClient{
|
||||
resp: &github.Response{Response: &http.Response{StatusCode: http.StatusNotFound}},
|
||||
}
|
||||
testBot.issueClient = &tc
|
||||
assignee, err := testBot.AssignIssueToCommenter(testEvent)
|
||||
require.Error(t, err)
|
||||
assert.Empty(t, assignee)
|
||||
})
|
||||
|
||||
t.Run("successfully assign issue to commenter", func(t *testing.T) {
|
||||
tc := testClient{
|
||||
resp: &github.Response{Response: &http.Response{StatusCode: http.StatusOK}},
|
||||
}
|
||||
testBot.issueClient = &tc
|
||||
var testEventCopy Event
|
||||
errC := copier.CopyWithOption(&testEventCopy, &testEvent, copier.Option{DeepCopy: true})
|
||||
if errC != nil {
|
||||
t.Error(errC)
|
||||
}
|
||||
testEventCopy.IssueCommentEvent.Issue.Assignees = []*github.User{}
|
||||
assignee, err := testBot.AssignIssueToCommenter(testEventCopy)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "testCommentLogin", assignee)
|
||||
})
|
||||
|
||||
t.Run("attempt to assign a closed issue", func(t *testing.T) {
|
||||
tc := testClient{}
|
||||
testBot.issueClient = &tc
|
||||
var testEventCopy Event
|
||||
errC := copier.CopyWithOption(&testEventCopy, &testEvent, copier.Option{DeepCopy: true})
|
||||
if errC != nil {
|
||||
t.Error(errC)
|
||||
}
|
||||
testEventCopy.IssueCommentEvent.Issue.State = github.String("closed")
|
||||
assignee, err := testBot.AssignIssueToCommenter(testEventCopy)
|
||||
require.Error(t, err)
|
||||
assert.Empty(t, assignee)
|
||||
})
|
||||
|
||||
t.Run("issue already assigned to user", func(t *testing.T) {
|
||||
tc := testClient{}
|
||||
testBot.issueClient = &tc
|
||||
var testEventCopy Event
|
||||
errC := copier.CopyWithOption(&testEventCopy, &testEvent, copier.Option{DeepCopy: true})
|
||||
if errC != nil {
|
||||
t.Error(errC)
|
||||
}
|
||||
testEventCopy.IssueCommentEvent.Issue.Assignees = []*github.User{{Login: github.String("testCommentLogin")}}
|
||||
assignee, err := testBot.AssignIssueToCommenter(testEventCopy)
|
||||
require.Error(t, err)
|
||||
assert.Empty(t, assignee)
|
||||
})
|
||||
|
||||
t.Run("issue already assigned to another user", func(t *testing.T) {
|
||||
tc := testClient{
|
||||
resp: &github.Response{Response: &http.Response{StatusCode: http.StatusOK}},
|
||||
}
|
||||
testBot.issueClient = &tc
|
||||
var testEventCopy Event
|
||||
errC := copier.CopyWithOption(&testEventCopy, &testEvent, copier.Option{DeepCopy: true})
|
||||
if errC != nil {
|
||||
t.Error(errC)
|
||||
}
|
||||
testEventCopy.IssueCommentEvent.Issue.Assignees = []*github.User{{Login: github.String("testCommentLogin2")}}
|
||||
assignee, err := testBot.AssignIssueToCommenter(testEventCopy)
|
||||
require.Error(t, err)
|
||||
assert.Empty(t, assignee)
|
||||
})
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/google/go-github/v55/github"
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
Type string `json:"type"`
|
||||
Path string `json:"path"`
|
||||
IssueCommentEvent *github.IssueCommentEvent `json:"issue_comment_event"`
|
||||
}
|
||||
|
||||
func ProcessEvent(eventType string, eventPath string, data []byte) (e Event, err error) {
|
||||
var issueCommentEvent *github.IssueCommentEvent
|
||||
if eventPath == "" {
|
||||
return Event{}, errors.New("invalid event path")
|
||||
}
|
||||
switch eventType {
|
||||
case "issue_comment":
|
||||
err = json.Unmarshal(data, &issueCommentEvent)
|
||||
if err != nil {
|
||||
return Event{}, err
|
||||
}
|
||||
}
|
||||
e = Event{
|
||||
Type: eventType,
|
||||
Path: eventPath,
|
||||
IssueCommentEvent: issueCommentEvent,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (e *Event) GetIssueAssignees() []string {
|
||||
assignees := make([]string, 0)
|
||||
for _, assignee := range e.IssueCommentEvent.Issue.Assignees {
|
||||
assignees = append(assignees, assignee.GetLogin())
|
||||
}
|
||||
return assignees
|
||||
}
|
||||
|
||||
func (e *Event) GetIssueNumber() int {
|
||||
return e.IssueCommentEvent.Issue.GetNumber()
|
||||
}
|
||||
|
||||
func (e *Event) GetIssueOrg() string {
|
||||
return e.IssueCommentEvent.Repo.Owner.GetLogin()
|
||||
}
|
||||
|
||||
func (e *Event) GetIssueRepo() string {
|
||||
return e.IssueCommentEvent.Repo.GetName()
|
||||
}
|
||||
|
||||
func (e *Event) GetIssueState() string {
|
||||
return e.IssueCommentEvent.Issue.GetState()
|
||||
}
|
||||
|
||||
func (e *Event) GetIssueUser() string {
|
||||
return e.IssueCommentEvent.Comment.User.GetLogin()
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-github/v55/github"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var testEvent = Event{
|
||||
Type: "issue_comment",
|
||||
Path: "test/test",
|
||||
IssueCommentEvent: &github.IssueCommentEvent{
|
||||
Issue: &github.Issue{
|
||||
Assignees: []*github.User{{Login: github.String("testAssignee")}},
|
||||
Number: github.Int(123),
|
||||
State: github.String("testState"),
|
||||
},
|
||||
Repo: &github.Repository{
|
||||
Owner: &github.User{Login: github.String("testOrg")},
|
||||
Name: github.String("testName"),
|
||||
},
|
||||
Comment: &github.IssueComment{
|
||||
User: &github.User{Login: github.String("testCommentLogin")},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestProcessEvent(t *testing.T) {
|
||||
testEventData, err := json.Marshal(testEvent)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to marshal event: %v", err)
|
||||
}
|
||||
t.Run("process event", func(t *testing.T) {
|
||||
event, err := ProcessEvent(testEvent.Type, testEvent.Path, testEventData)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, event)
|
||||
assert.Equal(t, "test/test", event.Path)
|
||||
})
|
||||
|
||||
t.Run("process event with empty path", func(t *testing.T) {
|
||||
event, err := ProcessEvent(testEvent.Type, "", testEventData)
|
||||
require.Error(t, err)
|
||||
assert.Empty(t, event)
|
||||
})
|
||||
|
||||
var randomData bytes.Buffer
|
||||
encoder := gob.NewEncoder(&randomData)
|
||||
encoder.Encode("random_data")
|
||||
|
||||
t.Run("process issue_comment event", func(t *testing.T) {
|
||||
event, err := ProcessEvent(testEvent.Type, testEvent.Path, testEventData)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, event)
|
||||
assert.Equal(t, "issue_comment", event.Type)
|
||||
})
|
||||
|
||||
t.Run("process invalid event", func(t *testing.T) {
|
||||
event, err := ProcessEvent(testEvent.Type, testEvent.Path, randomData.Bytes())
|
||||
require.Error(t, err)
|
||||
assert.Empty(t, event)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetIssueAssignees(t *testing.T) {
|
||||
t.Run("get assignees", func(t *testing.T) {
|
||||
assignees := testEvent.GetIssueAssignees()
|
||||
assert.Len(t, assignees, 1)
|
||||
assert.Equal(t, "testAssignee", assignees[0])
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetIssueNumber(t *testing.T) {
|
||||
t.Run("get issue number", func(t *testing.T) {
|
||||
number := testEvent.GetIssueNumber()
|
||||
assert.Equal(t, 123, number)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetIssueOrg(t *testing.T) {
|
||||
t.Run("get issue org", func(t *testing.T) {
|
||||
org := testEvent.GetIssueOrg()
|
||||
assert.Equal(t, "testOrg", org)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetIssueRepo(t *testing.T) {
|
||||
t.Run("get issue repo", func(t *testing.T) {
|
||||
repo := testEvent.GetIssueRepo()
|
||||
assert.Equal(t, "testName", repo)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetIssueState(t *testing.T) {
|
||||
t.Run("get issue state", func(t *testing.T) {
|
||||
state := testEvent.GetIssueState()
|
||||
assert.Equal(t, "testState", state)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetIssueUser(t *testing.T) {
|
||||
t.Run("get issue user", func(t *testing.T) {
|
||||
user := testEvent.GetIssueUser()
|
||||
assert.Equal(t, "testCommentLogin", user)
|
||||
})
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
module github.com/dapr/go-sdk/.github/workflows/dapr-bot
|
||||
|
||||
go 1.23.3
|
||||
|
||||
require (
|
||||
github.com/google/go-github/v55 v55.0.0
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/ProtonMail/go-crypto v1.0.0 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.18.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
|
@ -1,71 +0,0 @@
|
|||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github/v55 v55.0.0 h1:4pp/1tNMB9X/LuAhs5i0KQAE40NmiR/y6prLNb9x9cg=
|
||||
github.com/google/go-github/v55 v55.0.0/go.mod h1:JLahOTA1DnXzhxEymmFF5PP2tSS9JVNj68mSZNDwskA=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -1,37 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/google/go-github/v55/github"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
githubToken := os.Getenv("GITHUB_TOKEN")
|
||||
if githubToken == "" {
|
||||
log.Fatal("GITHUB_TOKEN is required")
|
||||
}
|
||||
|
||||
ghClient := github.NewClient(nil).WithAuthToken(githubToken)
|
||||
bot := NewBot(ghClient)
|
||||
eventType := os.Getenv("GITHUB_EVENT_NAME")
|
||||
eventPath := os.Getenv("GITHUB_EVENT_PATH")
|
||||
data, err := os.ReadFile(eventPath)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to read event: %v", err)
|
||||
}
|
||||
event, err := ProcessEvent(eventType, eventPath, data)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to process event: %v", err)
|
||||
}
|
||||
log.Printf("processing event: %s", event.Type)
|
||||
|
||||
res, err := bot.HandleEvent(ctx, event)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to handle event: %v", err)
|
||||
}
|
||||
log.Println(res)
|
||||
}
|
|
@ -32,15 +32,15 @@ jobs:
|
|||
FOSSA_API_KEY: b88e1f4287c3108c8751bf106fb46db6 # This is a push-only token that is safe to be exposed.
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Run FOSSA Scan"
|
||||
uses: fossas/fossa-action@v1.3.3 # Use a specific version if locking is preferred
|
||||
uses: fossas/fossa-action@v1.1.0 # Use a specific version if locking is preferred
|
||||
with:
|
||||
api-key: ${{ env.FOSSA_API_KEY }}
|
||||
|
||||
- name: "Run FOSSA Test"
|
||||
uses: fossas/fossa-action@v1.3.3 # Use a specific version if locking is preferred
|
||||
uses: fossas/fossa-action@v1.1.0 # Use a specific version if locking is preferred
|
||||
with:
|
||||
api-key: ${{ env.FOSSA_API_KEY }}
|
||||
run-tests: true
|
||||
|
|
|
@ -3,70 +3,59 @@ name: Release
|
|||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*" # v0.8.1
|
||||
- 'v*' # v0.8.1
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
name: Create Release on Tag
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
- name: Setup
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ^1.19
|
||||
|
||||
- name: Check sdk-version file
|
||||
run: |
|
||||
SDK_VERSION="$(head -n1 version/sdk-version)"
|
||||
SDK_VERSION_GIT="refs/tags/${SDK_VERSION}"
|
||||
if [[ "${SDK_VERSION_GIT}" != "${{ github.ref }}" ]]; then
|
||||
echo "File version/sdk-version (${SDK_VERSION}) needs to be updated to ${{ github.ref }}"
|
||||
exit 1
|
||||
fi
|
||||
shell: bash
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Tidy
|
||||
run: make tidy
|
||||
- name: Check sdk-version file
|
||||
run: |
|
||||
SDK_VERSION="$(head -n1 version/sdk-version)"
|
||||
SDK_VERSION_GIT="refs/tags/${SDK_VERSION}"
|
||||
if [[ "${SDK_VERSION_GIT}" != "${{ github.ref }}" ]]; then
|
||||
echo "File version/sdk-version (${SDK_VERSION}) needs to be updated to ${{ github.ref }}"
|
||||
exit 1
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Test
|
||||
run: make test
|
||||
- name: Tidy
|
||||
run: make tidy
|
||||
|
||||
- name: Version
|
||||
run: |
|
||||
echo "RELEASE_VERSION=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV
|
||||
- name: Test
|
||||
run: make test
|
||||
|
||||
- name: Release Main
|
||||
uses: actions/create-release@v1
|
||||
if: ${{ !contains(github.ref , 'rc') }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
body: Automatic Go Dapr client release
|
||||
draft: false
|
||||
prerelease: false
|
||||
- name: Version
|
||||
run: |
|
||||
echo "RELEASE_VERSION=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV
|
||||
|
||||
- name: Release RC
|
||||
uses: actions/create-release@v1
|
||||
if: ${{ contains(github.ref, 'rc') }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
body: Automatic Go Dapr client release
|
||||
draft: false
|
||||
prerelease: true
|
||||
- name: Release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
body: Automatic Go Dapr client release
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: Notify
|
||||
uses: rjstone/discord-webhook-notify@v1
|
||||
with:
|
||||
severity: info
|
||||
details: Release ${{ github.ref }} published
|
||||
description: Release
|
||||
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
avatarUrl: https://github.githubassets.com/images/modules/logos_page/Octocat.png
|
||||
- name: Notify
|
||||
uses: rjstone/discord-webhook-notify@v1
|
||||
with:
|
||||
severity: info
|
||||
details: Release ${{ github.ref }} published
|
||||
description: Release
|
||||
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
avatarUrl: https://github.githubassets.com/images/modules/logos_page/Octocat.png
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
name: Test Dapr-Bot
|
||||
|
||||
on:
|
||||
push:
|
||||
paths: # Explicitly declare which paths
|
||||
- ".github/workflows/dapr-bot.yml"
|
||||
- ".github/workflows/dapr-bot/*"
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths: # Explicitly declare which paths
|
||||
- ".github/workflows/dapr-bot.yml"
|
||||
- ".github/workflows/dapr-bot/*"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GOLANGCILINT_VER: v1.64.6
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: ./.github/workflows/dapr-bot/go.mod
|
||||
|
||||
- name: Tidy
|
||||
working-directory: ./.github/workflows/dapr-bot
|
||||
run: make tidy
|
||||
|
||||
- name: Test
|
||||
working-directory: ./.github/workflows/dapr-bot
|
||||
run: make test
|
||||
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: ${{ env.GOLANGCILINT_VER }}
|
||||
working-directory: ./.github/workflows/dapr-bot
|
||||
skip-cache: true
|
||||
args: --timeout=10m0s --config ../../../.golangci.yml
|
|
@ -8,21 +8,34 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
name: Test
|
||||
name: Test on ${{ matrix.gover }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
gover:
|
||||
- "1.19"
|
||||
- "1.20"
|
||||
env:
|
||||
GOLANGCILINT_VER: v1.64.6
|
||||
GOVER: ${{ matrix.gover }}
|
||||
GOLANGCILINT_VER: v1.54.2
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
go-version: ${{ env.GOVER }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Tidy
|
||||
run: make tidy
|
||||
|
@ -31,10 +44,10 @@ jobs:
|
|||
run: make test
|
||||
|
||||
- name: Cover
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v3
|
||||
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
uses: golangci/golangci-lint-action@v3.3.1
|
||||
with:
|
||||
version: ${{ env.GOLANGCILINT_VER }}
|
||||
skip-cache: true
|
||||
|
@ -42,5 +55,3 @@ jobs:
|
|||
|
||||
- name: Run go mod tidy check diff
|
||||
run: make modtidy check-diff
|
||||
- name: Run go mod tidy
|
||||
run: make modtidy
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
name: Test Tooling
|
||||
|
||||
on:
|
||||
push:
|
||||
paths: # Explicitly declare which paths
|
||||
- ".github/workflows/test-tooling.yml"
|
||||
- "tools/*"
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths: # Explicitly declare which paths
|
||||
- ".github/workflows/test-tooling.yml"
|
||||
- "tools/*"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Test (${{ matrix.os}}) go ${{ matrix.gover }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- "ubuntu-latest"
|
||||
- "windows-latest"
|
||||
- "macos-latest"
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
GOLANGCILINT_VER: v1.64.6 # Make sure to bump /tools/check-lint-version/main_test.go
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: ./tools/check-lint-version/go.mod
|
||||
|
||||
- name: Tidy
|
||||
working-directory: ./tools/check-lint-version
|
||||
run: make tidy
|
||||
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: ${{ env.GOLANGCILINT_VER }}
|
||||
working-directory: ./tools/check-lint-version
|
||||
skip-cache: true
|
||||
args: --timeout=10m0s --config ../../.golangci.yml
|
||||
|
||||
- name: Install Linter
|
||||
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "$(go env GOPATH)/bin" ${{ env.GOLANGCILINT_VER }}
|
||||
|
||||
- name: Test
|
||||
working-directory: ./tools/check-lint-version
|
||||
run: make test
|
|
@ -4,242 +4,97 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
tags:
|
||||
- v*
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
daprdapr_commit:
|
||||
description: "Dapr/Dapr commit to build custom daprd from"
|
||||
required: false
|
||||
default: ""
|
||||
daprcli_commit:
|
||||
description: "Dapr/CLI commit to build custom dapr CLI from"
|
||||
required: false
|
||||
default: ""
|
||||
repository_dispatch:
|
||||
types: [validate-examples]
|
||||
merge_group:
|
||||
jobs:
|
||||
setup:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PYTHON_VER: 3.7
|
||||
GOVER: "1.20"
|
||||
GOOS: linux
|
||||
GOARCH: amd64
|
||||
GOPROXY: https://proxy.golang.org
|
||||
DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/master/install/install.sh
|
||||
DAPR_CLI_REF: ${{ github.event.inputs.daprcli_commit }}
|
||||
DAPR_REF: ${{ github.event.inputs.daprdapr_commit }}
|
||||
CHECKOUT_REPO: ${{ github.repository }}
|
||||
CHECKOUT_REF: ${{ github.ref }}
|
||||
outputs:
|
||||
DAPR_INSTALL_URL: ${{ env.DAPR_INSTALL_URL }}
|
||||
DAPR_CLI_VER: ${{ steps.outputs.outputs.DAPR_CLI_VER }}
|
||||
DAPR_CLI_REF: ${{ steps.outputs.outputs.DAPR_CLI_REF }}
|
||||
DAPR_RUNTIME_VER: ${{ steps.outputs.outputs.DAPR_RUNTIME_VER }}
|
||||
CHECKOUT_REPO: ${{ steps.outputs.outputs.CHECKOUT_REPO }}
|
||||
CHECKOUT_REF: ${{ steps.outputs.outputs.CHECKOUT_REF }}
|
||||
DAPR_REF: ${{ steps.outputs.outputs.DAPR_REF }}
|
||||
GITHUB_SHA: ${{ steps.outputs.outputs.GITHUB_SHA }}
|
||||
DAPR_CLI_REF: ""
|
||||
DAPR_REF: 770d4e51604f1264d8bb25cedf16ea9f77539394
|
||||
steps:
|
||||
- name: Parse repository_dispatch payload
|
||||
if: github.event_name == 'repository_dispatch'
|
||||
run: |
|
||||
if [ ${{ github.event.client_payload.command }} = "ok-to-test" ]; then
|
||||
echo "CHECKOUT_REPO=${{ github.event.client_payload.pull_head_repo }}" >> $GITHUB_ENV
|
||||
echo "CHECKOUT_REF=${{ github.event.client_payload.pull_head_ref }}" >> $GITHUB_ENV
|
||||
echo "DAPR_REF=master" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Check out code onto GOPATH
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.CHECKOUT_REPO }}
|
||||
ref: ${{ env.CHECKOUT_REF }}
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
|
||||
- name: Run go mod tidy check diff
|
||||
run: make modtidy check-diff
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- name: Determine latest Dapr Runtime version
|
||||
run: |
|
||||
RUNTIME_VERSION=$(curl -s "https://api.github.com/repos/dapr/dapr/releases/latest" | grep '"tag_name"' | cut -d ':' -f2 | tr -d '",v')
|
||||
echo "DAPR_RUNTIME_VER=$RUNTIME_VERSION" >> $GITHUB_ENV
|
||||
echo "Found $RUNTIME_VERSION"
|
||||
|
||||
- name: Determine latest Dapr Cli version
|
||||
run: |
|
||||
CLI_VERSION=$(curl -s "https://api.github.com/repos/dapr/cli/releases/latest" | grep '"tag_name"' | cut -d ':' -f2 | tr -d '",v')
|
||||
echo "DAPR_CLI_VER=$CLI_VERSION" >> $GITHUB_ENV
|
||||
echo "Found $CLI_VERSION"
|
||||
|
||||
- name: Set up Python ${{ env.PYTHON_VER }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VER }}
|
||||
- name: Install Mechanical Markdown
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install mechanical-markdown
|
||||
- name: Run go mod tidy check diff
|
||||
run: make modtidy check-diff
|
||||
- name: Set up Dapr CLI
|
||||
run: wget -q ${{ env.DAPR_INSTALL_URL }} -O - | /bin/bash -s ${{ env.DAPR_CLI_VER }}
|
||||
- name: Set up Go ${{ env.GOVER }}
|
||||
if: env.DAPR_REF != '' || env.DAPR_CLI_REF != ''
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ env.GOVER }}
|
||||
- name: Checkout Dapr CLI repo to override dapr command.
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
if: env.DAPR_CLI_REF != ''
|
||||
with:
|
||||
repository: dapr/cli
|
||||
ref: ${{ env.DAPR_CLI_REF }}
|
||||
path: cli
|
||||
|
||||
- name: Checkout Dapr repo to override daprd.
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
if: env.DAPR_REF != ''
|
||||
with:
|
||||
repository: dapr/dapr
|
||||
ref: ${{ env.DAPR_REF }}
|
||||
path: dapr_runtime
|
||||
|
||||
- name: Build dapr cli with referenced commit.
|
||||
- name: Build and override dapr cli with referenced commit.
|
||||
if: env.DAPR_CLI_REF != ''
|
||||
run: |
|
||||
echo "artifactPath=~/artifacts/$GITHUB_SHA/" >> $GITHUB_ENV
|
||||
cd cli
|
||||
make
|
||||
mkdir -p $HOME/artifacts/$GITHUB_SHA/
|
||||
sudo cp dist/linux_amd64/release/dapr ~/artifacts/$GITHUB_SHA/dapr
|
||||
echo "artifactPath=~/artifacts/$GITHUB_SHA/" >> $GITHUB_ENV
|
||||
echo "DAPR_CLI_REF=$DAPR_CLI_REF" >> $GITHUB_ENV
|
||||
|
||||
- name: Build dapr
|
||||
if: env.DAPR_REF != ''
|
||||
run: |
|
||||
echo "artifactPath=~/artifacts/$GITHUB_SHA/" >> $GITHUB_ENV
|
||||
cd dapr_runtime
|
||||
make
|
||||
mkdir -p $HOME/artifacts/$GITHUB_SHA/
|
||||
echo "artifactPath=~/artifacts/$GITHUB_SHA/" >> $GITHUB_ENV
|
||||
cp ./dist/linux_amd64/release/* ~/artifacts/$GITHUB_SHA/
|
||||
|
||||
- name: Upload dapr-artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
if: env.DAPR_REF != '' || env.DAPR_CLI_REF != ''
|
||||
with:
|
||||
name: dapr-artifacts
|
||||
path: ${{ env.artifactPath }}
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
compression-level: 0
|
||||
|
||||
- name: Outputs
|
||||
id: outputs
|
||||
run: |
|
||||
echo "DAPR_INSTALL_URL=$DAPR_INSTALL_URL"
|
||||
echo "DAPR_CLI_VER=$DAPR_CLI_VER" >> "$GITHUB_OUTPUT"
|
||||
echo "DAPR_CLI_REF=$DAPR_CLI_REF" >> "$GITHUB_OUTPUT"
|
||||
echo "DAPR_RUNTIME_VER=$DAPR_RUNTIME_VER" >> "$GITHUB_OUTPUT"
|
||||
echo "CHECKOUT_REPO=$CHECKOUT_REPO" >> "$GITHUB_OUTPUT"
|
||||
echo "CHECKOUT_REF=$CHECKOUT_REF" >> "$GITHUB_OUTPUT"
|
||||
echo "DAPR_REF=$DAPR_REF" >> "$GITHUB_OUTPUT"
|
||||
echo "GITHUB_SHA=$GITHUB_SHA" >> "$GITHUB_OUTPUT"
|
||||
|
||||
validate-example:
|
||||
needs: setup
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PYTHON_VER: 3.12
|
||||
GOOS: linux
|
||||
GOARCH: amd64
|
||||
GOPROXY: https://proxy.golang.org
|
||||
DAPR_INSTALL_URL: ${{ needs.setup.outputs.DAPR_INSTALL_URL }}
|
||||
DAPR_CLI_VER: ${{ needs.setup.outputs.DAPR_CLI_VER }}
|
||||
DAPR_RUNTIME_VER: ${{ needs.setup.outputs.DAPR_RUNTIME_VER }}
|
||||
DAPR_CLI_REF: ${{ needs.setup.outputs.DAPR_CLI_REF }}
|
||||
DAPR_REF: ${{ needs.setup.outputs.DAPR_REF }}
|
||||
CHECKOUT_REPO: ${{ needs.setup.outputs.CHECKOUT_REPO }}
|
||||
CHECKOUT_REF: ${{ needs.setup.outputs.CHECKOUT_REF }}
|
||||
GITHUB_SHA: ${{ needs.setup.outputs.GITHUB_SHA }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
examples:
|
||||
[
|
||||
"actor",
|
||||
"configuration",
|
||||
"conversation",
|
||||
"crypto",
|
||||
"dist-scheduler",
|
||||
"grpc-service",
|
||||
"hello-world",
|
||||
"pubsub",
|
||||
"streamsub",
|
||||
"service",
|
||||
"socket",
|
||||
"workflow",
|
||||
"workflow-parallel",
|
||||
]
|
||||
steps:
|
||||
- name: Check out code onto GOPATH
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.CHECKOUT_REPO }}
|
||||
ref: ${{ env.CHECKOUT_REF }}
|
||||
|
||||
- name: Make Artifacts destination folder
|
||||
if: env.DAPR_CLI_REF != '' || env.DAPR_REF != ''
|
||||
run: |
|
||||
mkdir -p $HOME/artifacts/$GITHUB_SHA/
|
||||
|
||||
- name: Retrieve dapr-artifacts
|
||||
if: env.DAPR_CLI_REF != '' || env.DAPR_REF != ''
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: dapr-artifacts
|
||||
path: ~/artifacts/${{ env.GITHUB_SHA }}/
|
||||
|
||||
- name: Display artifacts downloaded
|
||||
if: env.DAPR_CLI_REF != '' || env.DAPR_REF != ''
|
||||
run: ls ~/artifacts/$GITHUB_SHA/
|
||||
|
||||
- name: Set up Go
|
||||
id: setup-go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
|
||||
- name: Set up Dapr CLI
|
||||
if: env.DAPR_CLI_VER != ''
|
||||
run: wget -q ${{ env.DAPR_INSTALL_URL }} -O - | /bin/bash -s ${{ env.DAPR_CLI_VER }}
|
||||
|
||||
- name: Override dapr cli with referenced commit.
|
||||
if: env.DAPR_CLI_REF != ''
|
||||
run: |
|
||||
sudo cp $HOME/artifacts/$GITHUB_SHA/dapr /usr/local/bin/dapr
|
||||
|
||||
sudo cp dist/linux_amd64/release/dapr /usr/local/bin/dapr
|
||||
cd ..
|
||||
- name: Initialize Dapr runtime ${{ env.DAPR_RUNTIME_VER }}
|
||||
run: |
|
||||
dapr uninstall --all
|
||||
dapr init --runtime-version ${{ env.DAPR_RUNTIME_VER }}
|
||||
|
||||
- name: Print scheduler logs
|
||||
run: |
|
||||
docker logs dapr_scheduler
|
||||
|
||||
- name: Override daprd with referenced commit.
|
||||
- name: Build and override daprd with referenced commit.
|
||||
if: env.DAPR_REF != ''
|
||||
run: |
|
||||
cd dapr_runtime
|
||||
make
|
||||
mkdir -p $HOME/.dapr/bin/
|
||||
cp $HOME/artifacts/$GITHUB_SHA/daprd $HOME/.dapr/bin/daprd
|
||||
|
||||
- name: Set up Python ${{ env.PYTHON_VER }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VER }}
|
||||
|
||||
- name: Install Mechanical Markdown
|
||||
cp dist/linux_amd64/release/daprd $HOME/.dapr/bin/daprd
|
||||
cd ..
|
||||
- name: Override placement service.
|
||||
if: env.DAPR_REF != ''
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install mechanical-markdown
|
||||
|
||||
- name: Check Example
|
||||
docker stop dapr_placement
|
||||
cd dapr_runtime
|
||||
./dist/linux_amd64/release/placement --healthz-port 9091 &
|
||||
- name: Check Examples
|
||||
run: |
|
||||
cd examples
|
||||
./validate.sh ${{ matrix.examples }}
|
||||
./validate.sh grpc-service
|
||||
./validate.sh configuration
|
||||
./validate.sh hello-world
|
||||
./validate.sh pubsub
|
||||
./validate.sh service
|
||||
./validate.sh actor
|
||||
|
|
123
.golangci.yml
123
.golangci.yml
|
@ -4,7 +4,7 @@ run:
|
|||
concurrency: 4
|
||||
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
timeout: 15m
|
||||
deadline: 10m
|
||||
|
||||
# exit code when at least one issue was found, default is 1
|
||||
issues-exit-code: 1
|
||||
|
@ -13,35 +13,31 @@ run:
|
|||
tests: true
|
||||
|
||||
# list of build tags, all linters use it. Default is empty list.
|
||||
build-tags:
|
||||
- unit
|
||||
- allcomponents
|
||||
- subtlecrypto
|
||||
#build-tags:
|
||||
# - mytag
|
||||
|
||||
# which files to skip: they will be analyzed, but issues from them
|
||||
# won't be reported. Default value is empty list, but there is
|
||||
# no need to include all autogenerated files, we confidently recognize
|
||||
# autogenerated files. If it's not please let us know.
|
||||
# skip-files:
|
||||
# - ".*\\.my\\.go$"
|
||||
# - lib/bad.go
|
||||
|
||||
issues:
|
||||
# which dirs to skip: they won't be analyzed;
|
||||
# can use regexp here: generated.*, regexp is applied on full path;
|
||||
# default value is empty list, but next dirs are always skipped independently
|
||||
# from this option's value:
|
||||
# third_party$, testdata$, examples$, Godeps$, builtin$
|
||||
exclude-dirs:
|
||||
skip-dirs:
|
||||
- ^pkg.*client.*clientset.*versioned.*
|
||||
- ^pkg.*client.*informers.*externalversions.*
|
||||
- ^pkg.*proto.*
|
||||
|
||||
# which files to skip: they will be analyzed, but issues from them
|
||||
# won't be reported. Default value is empty list, but there is
|
||||
# no need to include all autogenerated files, we confidently recognize
|
||||
# autogenerated files. If it's not please let us know.
|
||||
# skip-files:
|
||||
# - ".*\\.my\\.go$"
|
||||
# - lib/bad.go
|
||||
|
||||
# output configuration options
|
||||
output:
|
||||
# colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number"
|
||||
formats:
|
||||
- format: tab
|
||||
format: tab
|
||||
|
||||
# print lines of code with issue, default is true
|
||||
print-issued-lines: true
|
||||
|
@ -61,19 +57,23 @@ linters-settings:
|
|||
# default is false: such cases aren't reported by default.
|
||||
check-blank: false
|
||||
|
||||
exclude-functions:
|
||||
- fmt:.*
|
||||
- io/ioutil:^Read.*
|
||||
# [deprecated] comma-separated list of pairs of the form pkg:regex
|
||||
# the regex is used to ignore names within pkg. (default "fmt:.*").
|
||||
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
|
||||
ignore: fmt:.*,io/ioutil:^Read.*
|
||||
|
||||
# path to a file containing a list of functions to exclude from checking
|
||||
# see https://github.com/kisielk/errcheck#excluding-functions for details
|
||||
# exclude:
|
||||
# exclude:
|
||||
|
||||
funlen:
|
||||
lines: 60
|
||||
statements: 40
|
||||
|
||||
govet:
|
||||
# report about shadowed variables
|
||||
check-shadowing: true
|
||||
|
||||
# settings per analyzer
|
||||
settings:
|
||||
printf: # analyzer name, run `go tool vet help` to see all analyzers
|
||||
|
@ -86,12 +86,28 @@ linters-settings:
|
|||
# enable or disable analyzers by name
|
||||
enable:
|
||||
- atomicalign
|
||||
- shadow
|
||||
enable-all: false
|
||||
disable:
|
||||
- shadow
|
||||
disable-all: false
|
||||
revive:
|
||||
# minimal confidence for issues, default is 0.8
|
||||
confidence: 0.8
|
||||
max-open-files: 2048
|
||||
# enable-all-rules: true
|
||||
rules:
|
||||
- name: cyclomatic
|
||||
severity: warning
|
||||
disabled: false
|
||||
arguments: [20]
|
||||
- name: argument-limit
|
||||
severity: warning
|
||||
disabled: false
|
||||
arguments: [8]
|
||||
- name: if-return
|
||||
severity: warning
|
||||
disabled: false
|
||||
- name: unused-parameter
|
||||
severity: warning
|
||||
disabled: true
|
||||
gofmt:
|
||||
# simplify code: gofmt with `-s` option, true by default
|
||||
simplify: true
|
||||
|
@ -105,6 +121,9 @@ linters-settings:
|
|||
gocognit:
|
||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||
min-complexity: 10
|
||||
maligned:
|
||||
# print struct with more effective memory layout or not, false by default
|
||||
suggest-new: true
|
||||
dupl:
|
||||
# tokens count to trigger issue, 150 by default
|
||||
threshold: 100
|
||||
|
@ -133,9 +152,10 @@ linters-settings:
|
|||
desc: "you must use github.com/cenkalti/backoff/v4"
|
||||
misspell:
|
||||
# Correct spellings using locale preferences for US or UK.
|
||||
# Default is to use a neutral variety of English.
|
||||
# Default is to use a neutral variety of English. (Do not specify a locale value)
|
||||
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
|
||||
# locale: default
|
||||
# locale:
|
||||
|
||||
ignore-words:
|
||||
- someword
|
||||
lll:
|
||||
|
@ -144,6 +164,18 @@ linters-settings:
|
|||
line-length: 120
|
||||
# tab width in spaces. Default to 1.
|
||||
tab-width: 1
|
||||
unused:
|
||||
# treat code as a program (not a library) and report unused exported identifiers; default is false.
|
||||
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
|
||||
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
check-exported: false
|
||||
unparam:
|
||||
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
|
||||
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
|
||||
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
check-exported: false
|
||||
nakedret:
|
||||
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
|
||||
max-func-lines: 30
|
||||
|
@ -161,7 +193,7 @@ linters-settings:
|
|||
# See https://go-critic.github.io/overview#checks-overview
|
||||
# To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run`
|
||||
# By default list of stable checks is used.
|
||||
# enabled-checks:
|
||||
# enabled-checks:
|
||||
|
||||
# Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty
|
||||
disabled-checks:
|
||||
|
@ -209,20 +241,17 @@ linters-settings:
|
|||
allow-assign-and-call: true
|
||||
# Allow multiline assignments to be cuddled. Default is true.
|
||||
allow-multiline-assign: true
|
||||
# Allow case blocks to end with a whitespace.
|
||||
allow-case-traling-whitespace: true
|
||||
# Allow declarations (var) to be cuddled.
|
||||
allow-cuddle-declarations: false
|
||||
# If the number of lines in a case block is equal to or lager than this number,
|
||||
# the case *must* end white a newline.
|
||||
# https://github.com/bombsimon/wsl/blob/master/doc/configuration.md#force-case-trailing-whitespace
|
||||
# Default: 0
|
||||
force-case-trailing-whitespace: 1
|
||||
|
||||
linters:
|
||||
fast: false
|
||||
enable-all: true
|
||||
disable:
|
||||
# TODO Enforce the below linters later
|
||||
- musttag
|
||||
- nosnakecase
|
||||
- dupl
|
||||
- errcheck
|
||||
- funlen
|
||||
|
@ -231,16 +260,26 @@ linters:
|
|||
- gocyclo
|
||||
- gocognit
|
||||
- godox
|
||||
- interfacer
|
||||
- lll
|
||||
- maligned
|
||||
- scopelint
|
||||
- unparam
|
||||
- wsl
|
||||
- gomnd
|
||||
- testpackage
|
||||
- goerr113
|
||||
- nestif
|
||||
- nlreturn
|
||||
- tagliatelle
|
||||
- ifshort
|
||||
- forbidigo
|
||||
- exhaustive
|
||||
- exhaustruct
|
||||
- exhaustivestruct
|
||||
- noctx
|
||||
- gci
|
||||
- golint
|
||||
- tparallel
|
||||
- paralleltest
|
||||
- wrapcheck
|
||||
|
@ -254,6 +293,7 @@ linters:
|
|||
- varnamelen
|
||||
- errorlint
|
||||
- forcetypeassert
|
||||
- ifshort
|
||||
- maintidx
|
||||
- nilnil
|
||||
- predeclared
|
||||
|
@ -266,10 +306,13 @@ linters:
|
|||
- asasalint
|
||||
- rowserrcheck
|
||||
- sqlclosecheck
|
||||
- inamedparam
|
||||
- tagalign
|
||||
- mnd
|
||||
- canonicalheader
|
||||
- err113
|
||||
- fatcontext
|
||||
- forbidigo # TODO: Re-enable and remove fmt.println
|
||||
- structcheck
|
||||
- varcheck
|
||||
- deadcode
|
||||
- golint
|
||||
issues:
|
||||
exclude-rules:
|
||||
- path: .*_test.go
|
||||
linters:
|
||||
- godot
|
||||
|
||||
|
|
17
Makefile
17
Makefile
|
@ -1,6 +1,6 @@
|
|||
RELEASE_VERSION =v1.0.0-rc-3
|
||||
GDOC_PORT =8888
|
||||
GO_COMPAT_VERSION=1.22
|
||||
GO_COMPAT_VERSION=1.19
|
||||
|
||||
.PHONY: all
|
||||
all: help
|
||||
|
@ -11,7 +11,7 @@ tidy: ## Updates the go modules
|
|||
|
||||
.PHONY: test
|
||||
test:
|
||||
CGO_ENABLED=1 go test -count=1 \
|
||||
go test -count=1 \
|
||||
-race \
|
||||
-coverprofile=coverage.txt \
|
||||
-covermode=atomic \
|
||||
|
@ -27,22 +27,11 @@ cover: ## Displays test coverage in the client and service packages
|
|||
go test -coverprofile=cover-client.out ./client && go tool cover -html=cover-client.out
|
||||
go test -coverprofile=cover-grpc.out ./service/grpc && go tool cover -html=cover-grpc.out
|
||||
go test -coverprofile=cover-http.out ./service/http && go tool cover -html=cover-http.out
|
||||
go test -coverprofile=cover-workflow.out ./workflow && go tool cover -html=cover-workflow.out
|
||||
|
||||
.PHONY: lint
|
||||
lint: check-lint ## Lints the entire project
|
||||
lint: ## Lints the entire project
|
||||
golangci-lint run --timeout=3m
|
||||
|
||||
.PHONY: lint-fix
|
||||
lint-fix: check-lint ## Lints the entire project
|
||||
golangci-lint run --timeout=3m --fix
|
||||
|
||||
.PHONY: check-lint
|
||||
check-lint: ## Compares the locally installed linter with the workflow version
|
||||
cd ./tools/check-lint-version && \
|
||||
go mod tidy && \
|
||||
go run main.go
|
||||
|
||||
.PHONY: tag
|
||||
tag: ## Creates release tag
|
||||
git tag $(RELEASE_VERSION)
|
||||
|
|
44
Readme.md
44
Readme.md
|
@ -292,50 +292,6 @@ func main() {
|
|||
}
|
||||
```
|
||||
|
||||
##### Error handling
|
||||
|
||||
Dapr errors are based on [gRPC's richer error model](https://cloud.google.com/apis/design/errors#error_model).
|
||||
The following code shows how to parse and handle the error details:
|
||||
|
||||
```go
|
||||
if err != nil {
|
||||
st := status.Convert(err)
|
||||
|
||||
fmt.Printf("Code: %s\n", st.Code().String())
|
||||
fmt.Printf("Message: %s\n", st.Message())
|
||||
|
||||
for _, detail := range st.Details() {
|
||||
switch t := detail.(type) {
|
||||
case *errdetails.ErrorInfo:
|
||||
// Handle ErrorInfo details
|
||||
fmt.Printf("ErrorInfo:\n- Domain: %s\n- Reason: %s\n- Metadata: %v\n", t.GetDomain(), t.GetReason(), t.GetMetadata())
|
||||
case *errdetails.BadRequest:
|
||||
// Handle BadRequest details
|
||||
fmt.Println("BadRequest:")
|
||||
for _, violation := range t.GetFieldViolations() {
|
||||
fmt.Printf("- Key: %s\n", violation.GetField())
|
||||
fmt.Printf("- The %q field was wrong: %s\n", violation.GetField(), violation.GetDescription())
|
||||
}
|
||||
case *errdetails.ResourceInfo:
|
||||
// Handle ResourceInfo details
|
||||
fmt.Printf("ResourceInfo:\n- Resource type: %s\n- Resource name: %s\n- Owner: %s\n- Description: %s\n",
|
||||
t.GetResourceType(), t.GetResourceName(), t.GetOwner(), t.GetDescription())
|
||||
case *errdetails.Help:
|
||||
// Handle ResourceInfo details
|
||||
fmt.Println("HelpInfo:")
|
||||
for _, link := range t.GetLinks() {
|
||||
fmt.Printf("- Url: %s\n", link.Url)
|
||||
fmt.Printf("- Description: %s\n", link.Description)
|
||||
}
|
||||
|
||||
default:
|
||||
// Add cases for other types of details you expect
|
||||
fmt.Printf("Unhandled error detail type: %v\n", t)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Service (callback)
|
||||
|
||||
In addition to the client capabilities that allow you to call into the Dapr API, the Go SDK also provides `service` package to help you bootstrap Dapr callback services in either gRPC or HTTP. Instructions on how to use it are located [here](./service/Readme.md).
|
||||
|
|
|
@ -72,7 +72,7 @@ func TestContainerInvoke(t *testing.T) {
|
|||
mockCodec.EXPECT().Unmarshal([]byte(param), gomock.Any()).SetArg(1, "param").Return(nil)
|
||||
|
||||
rsp, err := container.Invoke("Invoke", []byte(param))
|
||||
require.Len(t, rsp, 2)
|
||||
require.Equal(t, 2, len(rsp))
|
||||
require.Equal(t, actorErr.Success, err)
|
||||
assert.Equal(t, param, rsp[0].Interface().(string))
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sync"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
@ -30,22 +29,6 @@ import (
|
|||
actorErr "github.com/dapr/go-sdk/actor/error"
|
||||
)
|
||||
|
||||
// ignoredActorMethods is a list of method names that should be ignored during actor method reflection.
|
||||
// It is initialized with the Type() method which is needed to comply with the ServerContext interface.
|
||||
var ignoredActorMethods = []string{"Type"}
|
||||
|
||||
// init initializes the action method exclusion list with methods from ServerImplBaseCtx and ReminderCallee interfaces.
|
||||
func init() {
|
||||
serverImplBaseCtxType := reflect.TypeOf(&actor.ServerImplBaseCtx{})
|
||||
for i := range serverImplBaseCtxType.NumMethod() {
|
||||
ignoredActorMethods = append(ignoredActorMethods, serverImplBaseCtxType.Method(i).Name)
|
||||
}
|
||||
ReminderCallType := reflect.TypeOf((*actor.ReminderCallee)(nil)).Elem()
|
||||
for i := range ReminderCallType.NumMethod() {
|
||||
ignoredActorMethods = append(ignoredActorMethods, ReminderCallType.Method(i).Name)
|
||||
}
|
||||
}
|
||||
|
||||
type ActorManager interface {
|
||||
RegisterActorImplFactory(f actor.Factory)
|
||||
InvokeMethod(actorID, methodName string, request []byte) ([]byte, actorErr.ActorErr)
|
||||
|
@ -265,12 +248,8 @@ type MethodType struct {
|
|||
// suitableMethods returns suitable Rpc methods of typ.
|
||||
func suitableMethods(typ reflect.Type) map[string]*MethodType {
|
||||
methods := make(map[string]*MethodType)
|
||||
for m := range typ.NumMethod() {
|
||||
for m := 0; m < typ.NumMethod(); m++ {
|
||||
method := typ.Method(m)
|
||||
// skip methods from ServerImplBaseCtx struct and ServerContext and ReminderCallee interfaces.
|
||||
if slices.Contains(ignoredActorMethods, method.Name) {
|
||||
continue
|
||||
}
|
||||
if mt, err := suiteMethod(method); err != nil {
|
||||
log.Printf("method %s is illegal, err = %s, just skip it", method.Name, err)
|
||||
} else {
|
||||
|
|
|
@ -256,16 +256,16 @@ func (mr *MockServerContextMockRecorder) Type() *gomock.Call {
|
|||
}
|
||||
|
||||
func (mr *MockServerContextMockRecorder) Invoke(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Invoke", reflect.TypeOf((*MockServerContext)(nil).Invoke), arg0, arg1)
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Invoke", reflect.TypeOf((*MockServerContext)(nil).Invoke), arg0, arg1)
|
||||
}
|
||||
|
||||
func (m *MockServerContext) Invoke(ctx context.Context, input string) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Invoke", ctx, input)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Invoke", ctx, input)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// MockReminderCallee is a mock of ReminderCallee interface.
|
||||
|
|
|
@ -48,7 +48,6 @@ func TestRegisterActorFactoryAndInvokeMethod(t *testing.T) {
|
|||
mockServer.EXPECT().RegisterActorImplFactory(gomock.Any())
|
||||
rt.RegisterActorFactory(actorMock.ActorImplFactory)
|
||||
|
||||
//nolint:usetesting
|
||||
mockServer.EXPECT().InvokeMethod(context.Background(), "mockActorID", "Invoke", []byte("param")).Return([]byte("response"), actorErr.Success)
|
||||
rspData, err := rt.InvokeActorMethod("testActorType", "mockActorID", "Invoke", []byte("param"))
|
||||
|
||||
|
@ -90,7 +89,6 @@ func TestInvokeReminder(t *testing.T) {
|
|||
mockServer.EXPECT().RegisterActorImplFactory(gomock.Any())
|
||||
rt.RegisterActorFactory(actorMock.ActorImplFactory)
|
||||
|
||||
//nolint:usetesting
|
||||
mockServer.EXPECT().InvokeReminder(context.Background(), "mockActorID", "mockReminder", []byte("param")).Return(actorErr.Success)
|
||||
err = rt.InvokeReminder("testActorType", "mockActorID", "mockReminder", []byte("param"))
|
||||
|
||||
|
@ -111,7 +109,6 @@ func TestInvokeTimer(t *testing.T) {
|
|||
mockServer.EXPECT().RegisterActorImplFactory(gomock.Any())
|
||||
rt.RegisterActorFactory(actorMock.ActorImplFactory)
|
||||
|
||||
//nolint:usetesting
|
||||
mockServer.EXPECT().InvokeTimer(context.Background(), "mockActorID", "mockTimer", []byte("param")).Return(actorErr.Success)
|
||||
err = rt.InvokeTimer("testActorType", "mockActorID", "mockTimer", []byte("param"))
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ func TestNewActorStateChange(t *testing.T) {
|
|||
},
|
||||
}
|
||||
for name, test := range tests {
|
||||
test := test
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert.Equal(t, test.want, NewActorStateChange(test.stateName, test.value, test.changeKind, &test.ttl))
|
||||
})
|
||||
|
|
|
@ -20,7 +20,7 @@ import (
|
|||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
anypb "github.com/golang/protobuf/ptypes/any"
|
||||
|
||||
pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
|
||||
"github.com/dapr/go-sdk/actor"
|
||||
|
@ -66,7 +66,7 @@ func (c *GRPCClient) InvokeActor(ctx context.Context, in *InvokeActorRequest) (o
|
|||
Data: in.Data,
|
||||
}
|
||||
|
||||
resp, err := c.protoClient.InvokeActor(ctx, req)
|
||||
resp, err := c.protoClient.InvokeActor(c.withAuthToken(ctx), req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error invoking binding %s/%s: %w", in.ActorType, in.ActorID, err)
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ func (c *GRPCClient) InvokeActor(ctx context.Context, in *InvokeActorRequest) (o
|
|||
out = &InvokeActorResponse{}
|
||||
|
||||
if resp != nil {
|
||||
out.Data = resp.GetData()
|
||||
out.Data = resp.Data
|
||||
}
|
||||
|
||||
return out, nil
|
||||
|
@ -153,7 +153,7 @@ func (c *GRPCClient) RegisterActorReminder(ctx context.Context, in *RegisterActo
|
|||
Data: in.Data,
|
||||
}
|
||||
|
||||
_, err = c.protoClient.RegisterActorReminder(ctx, req)
|
||||
_, err = c.protoClient.RegisterActorReminder(c.withAuthToken(ctx), req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error invoking register actor reminder %s/%s: %w", in.ActorType, in.ActorID, err)
|
||||
}
|
||||
|
@ -187,13 +187,52 @@ func (c *GRPCClient) UnregisterActorReminder(ctx context.Context, in *Unregister
|
|||
Name: in.Name,
|
||||
}
|
||||
|
||||
_, err := c.protoClient.UnregisterActorReminder(ctx, req)
|
||||
_, err := c.protoClient.UnregisterActorReminder(c.withAuthToken(ctx), req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error invoking unregister actor reminder %s/%s: %w", in.ActorType, in.ActorID, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RenameActorReminderRequest struct {
|
||||
OldName string
|
||||
ActorType string
|
||||
ActorID string
|
||||
NewName string
|
||||
}
|
||||
|
||||
// RenameActorReminder would rename the actor reminder.
|
||||
func (c *GRPCClient) RenameActorReminder(ctx context.Context, in *RenameActorReminderRequest) error {
|
||||
if in == nil {
|
||||
return errors.New("actor rename reminder invocation request param required")
|
||||
}
|
||||
if in.ActorType == "" {
|
||||
return errors.New("actor rename reminder invocation actorType required")
|
||||
}
|
||||
if in.ActorID == "" {
|
||||
return errors.New("actor rename reminder invocation actorID required")
|
||||
}
|
||||
if in.OldName == "" {
|
||||
return errors.New("actor rename reminder invocation oldName required")
|
||||
}
|
||||
if in.NewName == "" {
|
||||
return errors.New("actor rename reminder invocation newName required")
|
||||
}
|
||||
|
||||
req := &pb.RenameActorReminderRequest{
|
||||
ActorType: in.ActorType,
|
||||
ActorId: in.ActorID,
|
||||
OldName: in.OldName,
|
||||
NewName: in.NewName,
|
||||
}
|
||||
|
||||
_, err := c.protoClient.RenameActorReminder(c.withAuthToken(ctx), req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error invoking rename actor reminder %s/%s: %w", in.ActorType, in.ActorID, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RegisterActorTimerRequest struct {
|
||||
ActorType string
|
||||
ActorID string
|
||||
|
@ -235,7 +274,7 @@ func (c *GRPCClient) RegisterActorTimer(ctx context.Context, in *RegisterActorTi
|
|||
Callback: in.CallBack,
|
||||
}
|
||||
|
||||
_, err = c.protoClient.RegisterActorTimer(ctx, req)
|
||||
_, err = c.protoClient.RegisterActorTimer(c.withAuthToken(ctx), req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error invoking actor register timer %s/%s: %w", in.ActorType, in.ActorID, err)
|
||||
}
|
||||
|
@ -269,7 +308,7 @@ func (c *GRPCClient) UnregisterActorTimer(ctx context.Context, in *UnregisterAct
|
|||
Name: in.Name,
|
||||
}
|
||||
|
||||
_, err := c.protoClient.UnregisterActorTimer(ctx, req)
|
||||
_, err := c.protoClient.UnregisterActorTimer(c.withAuthToken(ctx), req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error invoking binding %s/%s: %w", in.ActorType, in.ActorID, err)
|
||||
}
|
||||
|
@ -289,7 +328,7 @@ func (c *GRPCClient) implActor(actor actor.Client, serializer codec.Codec) {
|
|||
}
|
||||
|
||||
numField := valueOfActor.NumField()
|
||||
for i := range numField {
|
||||
for i := 0; i < numField; i++ {
|
||||
t := typeOfActor.Field(i)
|
||||
methodName := t.Name
|
||||
if methodName == "Type" {
|
||||
|
@ -312,7 +351,7 @@ func (c *GRPCClient) implActor(actor actor.Client, serializer codec.Codec) {
|
|||
}
|
||||
|
||||
funcOuts := make([]reflect.Type, outNum)
|
||||
for i := range outNum {
|
||||
for i := 0; i < outNum; i++ {
|
||||
funcOuts[i] = t.Type.Out(i)
|
||||
}
|
||||
|
||||
|
@ -413,7 +452,7 @@ func (c *GRPCClient) GetActorState(ctx context.Context, in *GetActorStateRequest
|
|||
if in.KeyName == "" {
|
||||
return nil, errors.New("actor get state invocation keyName required")
|
||||
}
|
||||
rsp, err := c.protoClient.GetActorState(ctx, &pb.GetActorStateRequest{
|
||||
rsp, err := c.protoClient.GetActorState(c.withAuthToken(ctx), &pb.GetActorStateRequest{
|
||||
ActorId: in.ActorID,
|
||||
ActorType: in.ActorType,
|
||||
Key: in.KeyName,
|
||||
|
@ -421,7 +460,7 @@ func (c *GRPCClient) GetActorState(ctx context.Context, in *GetActorStateRequest
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("error invoking actor get state %s/%s: %w", in.ActorType, in.ActorID, err)
|
||||
}
|
||||
return &GetActorStateResponse{Data: rsp.GetData()}, nil
|
||||
return &GetActorStateResponse{Data: rsp.Data}, nil
|
||||
}
|
||||
|
||||
type ActorStateOperation struct {
|
||||
|
@ -457,7 +496,7 @@ func (c *GRPCClient) SaveStateTransactionally(ctx context.Context, actorType, ac
|
|||
Metadata: metadata,
|
||||
})
|
||||
}
|
||||
_, err := c.protoClient.ExecuteActorStateTransaction(ctx, &pb.ExecuteActorStateTransactionRequest{
|
||||
_, err := c.protoClient.ExecuteActorStateTransaction(c.withAuthToken(ctx), &pb.ExecuteActorStateTransactionRequest{
|
||||
ActorType: actorType,
|
||||
ActorId: actorID,
|
||||
Operations: grpcOperations,
|
||||
|
|
|
@ -14,17 +14,16 @@ limitations under the License.
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const testActorType = "test"
|
||||
|
||||
func TestInvokeActor(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
in := &InvokeActorRequest{
|
||||
ActorID: "fn",
|
||||
Method: "mockMethod",
|
||||
|
@ -36,7 +35,7 @@ func TestInvokeActor(t *testing.T) {
|
|||
in.Data = nil
|
||||
out, err := testClient.InvokeActor(ctx, in)
|
||||
in.Data = []byte(`{hello}`)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, out)
|
||||
})
|
||||
|
||||
|
@ -44,7 +43,7 @@ func TestInvokeActor(t *testing.T) {
|
|||
in.Method = ""
|
||||
out, err := testClient.InvokeActor(ctx, in)
|
||||
in.Method = "mockMethod"
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, out)
|
||||
})
|
||||
|
||||
|
@ -52,7 +51,7 @@ func TestInvokeActor(t *testing.T) {
|
|||
in.ActorID = ""
|
||||
out, err := testClient.InvokeActor(ctx, in)
|
||||
in.ActorID = "fn"
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, out)
|
||||
})
|
||||
|
||||
|
@ -60,20 +59,20 @@ func TestInvokeActor(t *testing.T) {
|
|||
in.ActorType = ""
|
||||
out, err := testClient.InvokeActor(ctx, in)
|
||||
in.ActorType = testActorType
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, out)
|
||||
})
|
||||
|
||||
t.Run("invoke actor without empty input", func(t *testing.T) {
|
||||
in = nil
|
||||
out, err := testClient.InvokeActor(ctx, in)
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, out)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRegisterActorReminder(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
in := &RegisterActorReminderRequest{
|
||||
ActorID: "fn",
|
||||
Data: []byte(`{hello}`),
|
||||
|
@ -88,55 +87,55 @@ func TestRegisterActorReminder(t *testing.T) {
|
|||
in.ActorType = ""
|
||||
err := testClient.RegisterActorReminder(ctx, in)
|
||||
in.ActorType = testActorType
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor reminder without id ", func(t *testing.T) {
|
||||
in.ActorID = ""
|
||||
err := testClient.RegisterActorReminder(ctx, in)
|
||||
in.ActorID = "fn"
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor reminder without Name ", func(t *testing.T) {
|
||||
in.Name = ""
|
||||
err := testClient.RegisterActorReminder(ctx, in)
|
||||
in.Name = "mockName"
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor reminder without period ", func(t *testing.T) {
|
||||
in.Period = ""
|
||||
err := testClient.RegisterActorReminder(ctx, in)
|
||||
in.Period = "2s"
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor reminder without dutTime ", func(t *testing.T) {
|
||||
in.DueTime = ""
|
||||
err := testClient.RegisterActorReminder(ctx, in)
|
||||
in.DueTime = "2s"
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor reminder without TTL ", func(t *testing.T) {
|
||||
in.TTL = ""
|
||||
err := testClient.RegisterActorReminder(ctx, in)
|
||||
in.TTL = "20s"
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor reminder ", func(t *testing.T) {
|
||||
require.NoError(t, testClient.RegisterActorReminder(ctx, in))
|
||||
assert.NoError(t, testClient.RegisterActorReminder(ctx, in))
|
||||
})
|
||||
|
||||
t.Run("invoke register actor reminder with empty param", func(t *testing.T) {
|
||||
require.Error(t, testClient.RegisterActorReminder(ctx, nil))
|
||||
assert.Error(t, testClient.RegisterActorReminder(ctx, nil))
|
||||
})
|
||||
}
|
||||
|
||||
func TestRegisterActorTimer(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
in := &RegisterActorTimerRequest{
|
||||
ActorID: "fn",
|
||||
Data: []byte(`{hello}`),
|
||||
|
@ -152,69 +151,69 @@ func TestRegisterActorTimer(t *testing.T) {
|
|||
in.ActorType = ""
|
||||
err := testClient.RegisterActorTimer(ctx, in)
|
||||
in.ActorType = testActorType
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor timer without id ", func(t *testing.T) {
|
||||
in.ActorID = ""
|
||||
err := testClient.RegisterActorTimer(ctx, in)
|
||||
in.ActorID = "fn"
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor timer without Name ", func(t *testing.T) {
|
||||
in.Name = ""
|
||||
err := testClient.RegisterActorTimer(ctx, in)
|
||||
in.Name = "mockName"
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor timer without period ", func(t *testing.T) {
|
||||
in.Period = ""
|
||||
err := testClient.RegisterActorTimer(ctx, in)
|
||||
in.Period = "2s"
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor timer without dutTime ", func(t *testing.T) {
|
||||
in.DueTime = ""
|
||||
err := testClient.RegisterActorTimer(ctx, in)
|
||||
in.DueTime = "4s"
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor timer without TTL ", func(t *testing.T) {
|
||||
in.TTL = ""
|
||||
err := testClient.RegisterActorTimer(ctx, in)
|
||||
in.TTL = "20s"
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor timer without callBack ", func(t *testing.T) {
|
||||
in.CallBack = ""
|
||||
err := testClient.RegisterActorTimer(ctx, in)
|
||||
in.CallBack = "mockFunc"
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor timer without data ", func(t *testing.T) {
|
||||
in.Data = nil
|
||||
err := testClient.RegisterActorTimer(ctx, in)
|
||||
in.Data = []byte(`{hello}`)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor timer", func(t *testing.T) {
|
||||
require.NoError(t, testClient.RegisterActorTimer(ctx, in))
|
||||
assert.NoError(t, testClient.RegisterActorTimer(ctx, in))
|
||||
})
|
||||
|
||||
t.Run("invoke register actor timer with empty param", func(t *testing.T) {
|
||||
require.Error(t, testClient.RegisterActorTimer(ctx, nil))
|
||||
assert.Error(t, testClient.RegisterActorTimer(ctx, nil))
|
||||
})
|
||||
}
|
||||
|
||||
func TestUnregisterActorReminder(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
in := &UnregisterActorReminderRequest{
|
||||
ActorID: "fn",
|
||||
ActorType: testActorType,
|
||||
|
@ -225,41 +224,100 @@ func TestUnregisterActorReminder(t *testing.T) {
|
|||
in.ActorType = ""
|
||||
err := testClient.UnregisterActorReminder(ctx, in)
|
||||
in.ActorType = testActorType
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke unregister actor reminder without id ", func(t *testing.T) {
|
||||
in.ActorID = ""
|
||||
err := testClient.UnregisterActorReminder(ctx, in)
|
||||
in.ActorID = "fn"
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke unregister actor reminder without Name ", func(t *testing.T) {
|
||||
in.Name = ""
|
||||
err := testClient.UnregisterActorReminder(ctx, in)
|
||||
in.Name = "mockName"
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke unregister actor reminder without period ", func(t *testing.T) {
|
||||
in.ActorType = ""
|
||||
err := testClient.UnregisterActorReminder(ctx, in)
|
||||
in.ActorType = testActorType
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke unregister actor reminder ", func(t *testing.T) {
|
||||
require.NoError(t, testClient.UnregisterActorReminder(ctx, in))
|
||||
assert.NoError(t, testClient.UnregisterActorReminder(ctx, in))
|
||||
})
|
||||
|
||||
t.Run("invoke unregister actor reminder with empty param", func(t *testing.T) {
|
||||
require.Error(t, testClient.UnregisterActorReminder(ctx, nil))
|
||||
assert.Error(t, testClient.UnregisterActorReminder(ctx, nil))
|
||||
})
|
||||
}
|
||||
|
||||
func TestRenameActorReminder(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
registerReminderReq := &RegisterActorReminderRequest{
|
||||
ActorID: "fn",
|
||||
Data: []byte(`{hello}`),
|
||||
ActorType: testActorType,
|
||||
Name: "oldName",
|
||||
Period: "2s",
|
||||
DueTime: "4s",
|
||||
TTL: "20s",
|
||||
}
|
||||
|
||||
testClient.RegisterActorReminder(ctx, registerReminderReq)
|
||||
|
||||
renameReminderReq := &RenameActorReminderRequest{
|
||||
ActorID: "fn",
|
||||
ActorType: testActorType,
|
||||
OldName: "oldName",
|
||||
NewName: "newName",
|
||||
}
|
||||
|
||||
t.Run("invoke rename actor reminder without actorType", func(t *testing.T) {
|
||||
renameReminderReq.ActorType = ""
|
||||
err := testClient.RenameActorReminder(ctx, renameReminderReq)
|
||||
renameReminderReq.ActorType = testActorType
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke rename actor reminder without id ", func(t *testing.T) {
|
||||
renameReminderReq.ActorID = ""
|
||||
err := testClient.RenameActorReminder(ctx, renameReminderReq)
|
||||
renameReminderReq.ActorID = "fn"
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke rename actor reminder without oldName ", func(t *testing.T) {
|
||||
renameReminderReq.OldName = ""
|
||||
err := testClient.RenameActorReminder(ctx, renameReminderReq)
|
||||
renameReminderReq.OldName = "oldName"
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke rename actor reminder without newName ", func(t *testing.T) {
|
||||
renameReminderReq.NewName = ""
|
||||
err := testClient.RenameActorReminder(ctx, renameReminderReq)
|
||||
renameReminderReq.NewName = "newName"
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke rename actor reminder ", func(t *testing.T) {
|
||||
assert.NoError(t, testClient.RenameActorReminder(ctx, renameReminderReq))
|
||||
})
|
||||
|
||||
t.Run("invoke rename actor reminder with empty param", func(t *testing.T) {
|
||||
assert.Error(t, testClient.RenameActorReminder(ctx, nil))
|
||||
})
|
||||
}
|
||||
|
||||
func TestUnregisterActorTimer(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
in := &UnregisterActorTimerRequest{
|
||||
ActorID: "fn",
|
||||
ActorType: testActorType,
|
||||
|
@ -270,35 +328,35 @@ func TestUnregisterActorTimer(t *testing.T) {
|
|||
in.ActorType = ""
|
||||
err := testClient.UnregisterActorTimer(ctx, in)
|
||||
in.ActorType = testActorType
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor timer without id ", func(t *testing.T) {
|
||||
in.ActorID = ""
|
||||
err := testClient.UnregisterActorTimer(ctx, in)
|
||||
in.ActorID = "fn"
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor timer without Name ", func(t *testing.T) {
|
||||
in.Name = ""
|
||||
err := testClient.UnregisterActorTimer(ctx, in)
|
||||
in.Name = "mockName"
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor timer without period ", func(t *testing.T) {
|
||||
in.ActorType = ""
|
||||
err := testClient.UnregisterActorTimer(ctx, in)
|
||||
in.ActorType = testActorType
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invoke register actor timer ", func(t *testing.T) {
|
||||
require.NoError(t, testClient.UnregisterActorTimer(ctx, in))
|
||||
assert.NoError(t, testClient.UnregisterActorTimer(ctx, in))
|
||||
})
|
||||
|
||||
t.Run("invoke register actor timer with empty param", func(t *testing.T) {
|
||||
require.Error(t, testClient.UnregisterActorTimer(ctx, nil))
|
||||
assert.Error(t, testClient.UnregisterActorTimer(ctx, nil))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -61,15 +61,15 @@ func (c *GRPCClient) InvokeBinding(ctx context.Context, in *InvokeBindingRequest
|
|||
Metadata: in.Metadata,
|
||||
}
|
||||
|
||||
resp, err := c.protoClient.InvokeBinding(ctx, req)
|
||||
resp, err := c.protoClient.InvokeBinding(c.withAuthToken(ctx), req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error invoking binding %s/%s: %w", in.Name, in.Operation, err)
|
||||
}
|
||||
|
||||
if resp != nil {
|
||||
return &BindingEvent{
|
||||
Data: resp.GetData(),
|
||||
Metadata: resp.GetMetadata(),
|
||||
Data: resp.Data,
|
||||
Metadata: resp.Metadata,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -14,17 +14,16 @@ limitations under the License.
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// go test -timeout 30s ./client -count 1 -run ^TestInvokeBinding$
|
||||
|
||||
func TestInvokeBinding(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
in := &InvokeBindingRequest{
|
||||
Name: "test",
|
||||
Operation: "fn",
|
||||
|
@ -32,19 +31,19 @@ func TestInvokeBinding(t *testing.T) {
|
|||
|
||||
t.Run("output binding without data", func(t *testing.T) {
|
||||
err := testClient.InvokeOutputBinding(ctx, in)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("output binding", func(t *testing.T) {
|
||||
in.Data = []byte("test")
|
||||
err := testClient.InvokeOutputBinding(ctx, in)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("binding without data", func(t *testing.T) {
|
||||
in.Data = nil
|
||||
out, err := testClient.InvokeBinding(ctx, in)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, out)
|
||||
})
|
||||
|
||||
|
@ -52,7 +51,7 @@ func TestInvokeBinding(t *testing.T) {
|
|||
in.Data = []byte("test")
|
||||
in.Metadata = map[string]string{"k1": "v1", "k2": "v2"}
|
||||
out, err := testClient.InvokeBinding(ctx, in)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, out)
|
||||
assert.Equal(t, "test", string(out.Data))
|
||||
})
|
||||
|
|
212
client/client.go
212
client/client.go
|
@ -15,7 +15,6 @@ package client
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -29,13 +28,12 @@ import (
|
|||
|
||||
"github.com/dapr/go-sdk/actor"
|
||||
"github.com/dapr/go-sdk/actor/config"
|
||||
"github.com/dapr/go-sdk/client/internal"
|
||||
"github.com/dapr/go-sdk/version"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
|
||||
pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
|
||||
|
||||
|
@ -46,7 +44,6 @@ import (
|
|||
const (
|
||||
daprPortDefault = "50001"
|
||||
daprPortEnvVarName = "DAPR_GRPC_PORT" /* #nosec */
|
||||
daprGRPCEndpointEnvVarName = "DAPR_GRPC_ENDPOINT"
|
||||
traceparentKey = "traceparent"
|
||||
apiTokenKey = "dapr-api-token" /* #nosec */
|
||||
apiTokenEnvVarName = "DAPR_API_TOKEN" /* #nosec */
|
||||
|
@ -61,18 +58,6 @@ var (
|
|||
defaultClient Client
|
||||
)
|
||||
|
||||
// SetLogger sets the global logger for the Dapr client.
|
||||
// The default logger has a destination of os.Stdout, SetLogger allows you to
|
||||
// optionally specify a custom logger (with a custom destination).
|
||||
// To disable client logging entirely, use a nil argument e.g.: client.SetLogger(nil)
|
||||
func SetLogger(l *log.Logger) {
|
||||
if l == nil {
|
||||
l = log.New(io.Discard, "", 0)
|
||||
}
|
||||
|
||||
logger = l
|
||||
}
|
||||
|
||||
// Client is the interface for Dapr client implementation.
|
||||
//
|
||||
//nolint:interfacebloat
|
||||
|
@ -157,19 +142,9 @@ type Client interface {
|
|||
// SubscribeConfigurationItems can subscribe the change of configuration items by storeName and keys, and return subscription id
|
||||
SubscribeConfigurationItems(ctx context.Context, storeName string, keys []string, handler ConfigurationHandleFunction, opts ...ConfigurationOpt) (string, error)
|
||||
|
||||
// UnsubscribeConfigurationItems stops the subscription with target store's and ID.
|
||||
// Deprecated: Closing the `SubscribeConfigurationItems` stream (closing the given context) will unsubscribe the client and should be used in favor of `UnsubscribeConfigurationItems`.
|
||||
// UnsubscribeConfigurationItems can stop the subscription with target store's and id
|
||||
UnsubscribeConfigurationItems(ctx context.Context, storeName string, id string, opts ...ConfigurationOpt) error
|
||||
|
||||
// Subscribe subscribes to a pubsub topic and streams messages to the returned Subscription.
|
||||
// Subscription must be closed after finishing with subscribing.
|
||||
Subscribe(ctx context.Context, opts SubscriptionOptions) (*Subscription, error)
|
||||
|
||||
// SubscribeWithHandler subscribes to a pubsub topic and calls the given handler on topic events.
|
||||
// The returned cancel function must be called after finishing with subscribing.
|
||||
SubscribeWithHandler(ctx context.Context, opts SubscriptionOptions, handler SubscriptionHandleFunction) (func() error, error)
|
||||
|
||||
// DeleteBulkState deletes content for multiple keys from store.
|
||||
DeleteBulkState(ctx context.Context, storeName string, keys []string, meta map[string]string) error
|
||||
|
||||
|
@ -217,6 +192,9 @@ type Client interface {
|
|||
// UnregisterActorReminder unregisters an actor reminder.
|
||||
UnregisterActorReminder(ctx context.Context, req *UnregisterActorReminderRequest) error
|
||||
|
||||
// RenameActorReminder rename an actor reminder.
|
||||
RenameActorReminder(ctx context.Context, req *RenameActorReminderRequest) error
|
||||
|
||||
// InvokeActor calls a method on an actor.
|
||||
InvokeActor(ctx context.Context, req *InvokeActorRequest) (*InvokeActorResponse, error)
|
||||
|
||||
|
@ -229,57 +207,8 @@ type Client interface {
|
|||
// ImplActorClientStub is to impl user defined actor client stub
|
||||
ImplActorClientStub(actorClientStub actor.Client, opt ...config.Option)
|
||||
|
||||
// StartWorkflowBeta1 starts a workflow.
|
||||
// Deprecated: Please use the workflow client (github.com/dapr/go-sdk/workflow).
|
||||
// These methods for managing workflows are no longer supported and will be removed in the 1.16 release.
|
||||
StartWorkflowBeta1(ctx context.Context, req *StartWorkflowRequest) (*StartWorkflowResponse, error)
|
||||
|
||||
// GetWorkflowBeta1 gets a workflow.
|
||||
// Deprecated: Please use the workflow client (github.com/dapr/go-sdk/workflow).
|
||||
// These methods for managing workflows are no longer supported and will be removed in the 1.16 release.
|
||||
GetWorkflowBeta1(ctx context.Context, req *GetWorkflowRequest) (*GetWorkflowResponse, error)
|
||||
|
||||
// PurgeWorkflowBeta1 purges a workflow.
|
||||
// Deprecated: Please use the workflow client (github.com/dapr/go-sdk/workflow).
|
||||
// These methods for managing workflows are no longer supported and will be removed in the 1.16 release.
|
||||
PurgeWorkflowBeta1(ctx context.Context, req *PurgeWorkflowRequest) error
|
||||
|
||||
// TerminateWorkflowBeta1 terminates a workflow.
|
||||
// Deprecated: Please use the workflow client (github.com/dapr/go-sdk/workflow).
|
||||
// These methods for managing workflows are no longer supported and will be removed in the 1.16 release.
|
||||
TerminateWorkflowBeta1(ctx context.Context, req *TerminateWorkflowRequest) error
|
||||
|
||||
// PauseWorkflowBeta1 pauses a workflow.
|
||||
// Deprecated: Please use the workflow client (github.com/dapr/go-sdk/workflow).
|
||||
// These methods for managing workflows are no longer supported and will be removed in the 1.16 release.
|
||||
PauseWorkflowBeta1(ctx context.Context, req *PauseWorkflowRequest) error
|
||||
|
||||
// ResumeWorkflowBeta1 resumes a workflow.
|
||||
// Deprecated: Please use the workflow client (github.com/dapr/go-sdk/workflow).
|
||||
// These methods for managing workflows are no longer supported and will be removed in the 1.16 release.
|
||||
ResumeWorkflowBeta1(ctx context.Context, req *ResumeWorkflowRequest) error
|
||||
|
||||
// RaiseEventWorkflowBeta1 raises an event for a workflow.
|
||||
// Deprecated: Please use the workflow client (github.com/dapr/go-sdk/workflow).
|
||||
// These methods for managing workflows are no longer supported and will be removed in the 1.16 release.
|
||||
RaiseEventWorkflowBeta1(ctx context.Context, req *RaiseEventWorkflowRequest) error
|
||||
|
||||
// ScheduleJobAlpha1 creates and schedules a job.
|
||||
ScheduleJobAlpha1(ctx context.Context, req *Job) error
|
||||
|
||||
// GetJobAlpha1 returns a scheduled job.
|
||||
GetJobAlpha1(ctx context.Context, name string) (*Job, error)
|
||||
|
||||
// DeleteJobAlpha1 deletes a scheduled job.
|
||||
DeleteJobAlpha1(ctx context.Context, name string) error
|
||||
|
||||
// ConverseAlpha1 interacts with a conversational AI model.
|
||||
ConverseAlpha1(ctx context.Context, request conversationRequest, options ...conversationRequestOption) (*ConversationResponse, error)
|
||||
|
||||
// GrpcClient returns the base grpc client if grpc is used and nil otherwise
|
||||
GrpcClient() pb.DaprClient
|
||||
|
||||
GrpcClientConn() *grpc.ClientConn
|
||||
}
|
||||
|
||||
// NewClient instantiates Dapr client using DAPR_GRPC_PORT environment variable as port.
|
||||
|
@ -292,28 +221,18 @@ type Client interface {
|
|||
// NewClientWithConnection(conn *grpc.ClientConn) Client
|
||||
// NewClientWithSocket(socket string) (client Client, err error)
|
||||
func NewClient() (client Client, err error) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
port := os.Getenv(daprPortEnvVarName)
|
||||
if port == "" {
|
||||
port = daprPortDefault
|
||||
}
|
||||
if defaultClient != nil {
|
||||
return defaultClient, nil
|
||||
}
|
||||
|
||||
addr, ok := os.LookupEnv(daprGRPCEndpointEnvVarName)
|
||||
if ok {
|
||||
client, err = NewClientWithAddress(addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating %q client: %w", daprGRPCEndpointEnvVarName, err)
|
||||
}
|
||||
defaultClient = client
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
if defaultClient != nil {
|
||||
return defaultClient, nil
|
||||
}
|
||||
|
||||
port, ok := os.LookupEnv(daprPortEnvVarName)
|
||||
if !ok {
|
||||
port = daprPortDefault
|
||||
}
|
||||
|
||||
c, err := NewClientWithPort(port)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating default client: %w", err)
|
||||
|
@ -349,39 +268,23 @@ func NewClientWithAddressContext(ctx context.Context, address string) (client Cl
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parsedAddress, err := internal.ParseGRPCEndpoint(address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing address '%s': %w", address, err)
|
||||
}
|
||||
|
||||
at := &authToken{}
|
||||
|
||||
opts := []grpc.DialOption{
|
||||
grpc.WithUserAgent(userAgent()),
|
||||
grpc.WithBlock(), //nolint:staticcheck
|
||||
authTokenUnaryInterceptor(at),
|
||||
authTokenStreamInterceptor(at),
|
||||
}
|
||||
|
||||
if parsedAddress.TLS {
|
||||
opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(new(tls.Config))))
|
||||
} else {
|
||||
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Duration(timeoutSeconds)*time.Second)
|
||||
conn, err := grpc.DialContext( //nolint:staticcheck
|
||||
conn, err := grpc.DialContext(
|
||||
ctx,
|
||||
parsedAddress.Target,
|
||||
opts...,
|
||||
address,
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithUserAgent(userAgent()),
|
||||
grpc.WithBlock(),
|
||||
)
|
||||
cancel()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating connection to '%s': %w", address, err)
|
||||
}
|
||||
if hasToken := os.Getenv(apiTokenEnvVarName); hasToken != "" {
|
||||
logger.Println("client uses API token")
|
||||
}
|
||||
|
||||
return newClientWithConnection(conn, at), nil
|
||||
return NewClientWithConnection(conn), nil
|
||||
}
|
||||
|
||||
func getClientTimeoutSeconds() (int, error) {
|
||||
|
@ -404,82 +307,36 @@ func NewClientWithSocket(socket string) (client Client, err error) {
|
|||
if socket == "" {
|
||||
return nil, errors.New("nil socket")
|
||||
}
|
||||
at := &authToken{}
|
||||
logger.Printf("dapr client initializing for: %s", socket)
|
||||
addr := "unix://" + socket
|
||||
conn, err := grpc.Dial( //nolint:staticcheck
|
||||
conn, err := grpc.Dial(
|
||||
addr,
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithUserAgent(userAgent()),
|
||||
authTokenUnaryInterceptor(at),
|
||||
authTokenStreamInterceptor(at),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating connection to '%s': %w", addr, err)
|
||||
}
|
||||
return newClientWithConnection(conn, at), nil
|
||||
}
|
||||
|
||||
func newClientWithConnection(conn *grpc.ClientConn, authToken *authToken) Client {
|
||||
apiToken := os.Getenv(apiTokenEnvVarName)
|
||||
if apiToken != "" {
|
||||
if hasToken := os.Getenv(apiTokenEnvVarName); hasToken != "" {
|
||||
logger.Println("client uses API token")
|
||||
authToken.set(apiToken)
|
||||
}
|
||||
return &GRPCClient{
|
||||
connection: conn,
|
||||
protoClient: pb.NewDaprClient(conn),
|
||||
authToken: authToken,
|
||||
}
|
||||
return NewClientWithConnection(conn), nil
|
||||
}
|
||||
|
||||
// NewClientWithConnection instantiates Dapr client using specific connection.
|
||||
func NewClientWithConnection(conn *grpc.ClientConn) Client {
|
||||
return newClientWithConnection(conn, &authToken{})
|
||||
}
|
||||
|
||||
type authToken struct {
|
||||
mu sync.RWMutex
|
||||
authToken string
|
||||
}
|
||||
|
||||
func (a *authToken) get() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.authToken
|
||||
}
|
||||
|
||||
func (a *authToken) set(token string) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.authToken = token
|
||||
}
|
||||
|
||||
func authTokenUnaryInterceptor(authToken *authToken) grpc.DialOption {
|
||||
return grpc.WithUnaryInterceptor(func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
token := authToken.get()
|
||||
if token != "" {
|
||||
ctx = metadata.NewOutgoingContext(ctx, metadata.Pairs(apiTokenKey, token))
|
||||
}
|
||||
return invoker(ctx, method, req, reply, cc, opts...)
|
||||
})
|
||||
}
|
||||
|
||||
func authTokenStreamInterceptor(authToken *authToken) grpc.DialOption {
|
||||
return grpc.WithStreamInterceptor(func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
token := authToken.get()
|
||||
if token != "" {
|
||||
ctx = metadata.NewOutgoingContext(ctx, metadata.Pairs(apiTokenKey, token))
|
||||
}
|
||||
return streamer(ctx, desc, cc, method, opts...)
|
||||
})
|
||||
return &GRPCClient{
|
||||
connection: conn,
|
||||
protoClient: pb.NewDaprClient(conn),
|
||||
authToken: os.Getenv(apiTokenEnvVarName),
|
||||
}
|
||||
}
|
||||
|
||||
// GRPCClient is the gRPC implementation of Dapr client.
|
||||
type GRPCClient struct {
|
||||
connection *grpc.ClientConn
|
||||
protoClient pb.DaprClient
|
||||
authToken *authToken
|
||||
authToken string
|
||||
}
|
||||
|
||||
// Close cleans up all resources created by the client.
|
||||
|
@ -493,7 +350,7 @@ func (c *GRPCClient) Close() {
|
|||
// WithAuthToken sets Dapr API token on the instantiated client.
|
||||
// Allows empty string to reset token on existing client.
|
||||
func (c *GRPCClient) WithAuthToken(token string) {
|
||||
c.authToken.set(token)
|
||||
c.authToken = token
|
||||
}
|
||||
|
||||
// WithTraceID adds existing trace ID to the outgoing context.
|
||||
|
@ -506,9 +363,16 @@ func (c *GRPCClient) WithTraceID(ctx context.Context, id string) context.Context
|
|||
return metadata.NewOutgoingContext(ctx, md)
|
||||
}
|
||||
|
||||
func (c *GRPCClient) withAuthToken(ctx context.Context) context.Context {
|
||||
if c.authToken == "" {
|
||||
return ctx
|
||||
}
|
||||
return metadata.NewOutgoingContext(ctx, metadata.Pairs(apiTokenKey, c.authToken))
|
||||
}
|
||||
|
||||
// Shutdown the sidecar.
|
||||
func (c *GRPCClient) Shutdown(ctx context.Context) error {
|
||||
_, err := c.protoClient.Shutdown(ctx, &pb.ShutdownRequest{})
|
||||
_, err := c.protoClient.Shutdown(c.withAuthToken(ctx), &emptypb.Empty{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error shutting down the sidecar: %w", err)
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -32,16 +33,14 @@ import (
|
|||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/test/bufconn"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
|
||||
commonv1pb "github.com/dapr/dapr/pkg/proto/common/v1"
|
||||
pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
testBufSize = 1024 * 1024
|
||||
testSocket = "/tmp/dapr.socket"
|
||||
testWorkflowFailureID = "test_failure_id"
|
||||
testBufSize = 1024 * 1024
|
||||
testSocket = "/tmp/dapr.socket"
|
||||
)
|
||||
|
||||
var testClient Client
|
||||
|
@ -67,22 +66,22 @@ func TestMain(m *testing.M) {
|
|||
func TestNewClient(t *testing.T) {
|
||||
t.Run("return error when unable to reach server", func(t *testing.T) {
|
||||
_, err := NewClientWithPort("1")
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("no arg for with port", func(t *testing.T) {
|
||||
_, err := NewClientWithPort("")
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("no arg for with address", func(t *testing.T) {
|
||||
_, err := NewClientWithAddress("")
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("no arg with socket", func(t *testing.T) {
|
||||
_, err := NewClientWithSocket("")
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("new client closed with token", func(t *testing.T) {
|
||||
|
@ -98,7 +97,7 @@ func TestNewClient(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("new client with trace ID", func(t *testing.T) {
|
||||
_ = testClient.WithTraceID(t.Context(), "test")
|
||||
_ = testClient.WithTraceID(context.Background(), "test")
|
||||
})
|
||||
|
||||
t.Run("new socket client closed with token", func(t *testing.T) {
|
||||
|
@ -120,17 +119,17 @@ func TestNewClient(t *testing.T) {
|
|||
c, err := NewClientWithSocket(testSocket)
|
||||
require.NoError(t, err)
|
||||
defer c.Close()
|
||||
ctx := c.WithTraceID(t.Context(), "")
|
||||
ctx := c.WithTraceID(context.Background(), "")
|
||||
_ = c.WithTraceID(ctx, "test")
|
||||
})
|
||||
}
|
||||
|
||||
func TestShutdown(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("shutdown", func(t *testing.T) {
|
||||
err := testClient.Shutdown(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -151,7 +150,7 @@ func getTestClient(ctx context.Context) (client Client, closer func()) {
|
|||
d := grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) {
|
||||
return l.Dial()
|
||||
})
|
||||
//nolint:staticcheck
|
||||
|
||||
c, err := grpc.DialContext(ctx, "", d, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
logger.Fatalf("failed to dial test context: %v", err)
|
||||
|
@ -202,33 +201,33 @@ func Test_getClientTimeoutSeconds(t *testing.T) {
|
|||
t.Run("empty env var", func(t *testing.T) {
|
||||
t.Setenv(clientTimeoutSecondsEnvVarName, "")
|
||||
got, err := getClientTimeoutSeconds()
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, clientDefaultTimeoutSeconds, got)
|
||||
})
|
||||
|
||||
t.Run("invalid env var", func(t *testing.T) {
|
||||
t.Setenv(clientTimeoutSecondsEnvVarName, "invalid")
|
||||
_, err := getClientTimeoutSeconds()
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("normal env var", func(t *testing.T) {
|
||||
t.Setenv(clientTimeoutSecondsEnvVarName, "7")
|
||||
got, err := getClientTimeoutSeconds()
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 7, got)
|
||||
})
|
||||
|
||||
t.Run("zero env var", func(t *testing.T) {
|
||||
t.Setenv(clientTimeoutSecondsEnvVarName, "0")
|
||||
_, err := getClientTimeoutSeconds()
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("negative env var", func(t *testing.T) {
|
||||
t.Setenv(clientTimeoutSecondsEnvVarName, "-3")
|
||||
_, err := getClientTimeoutSeconds()
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -252,7 +251,7 @@ func (s *testDaprServer) UnlockAlpha1(ctx context.Context, req *pb.UnlockRequest
|
|||
}
|
||||
|
||||
func (s *testDaprServer) InvokeService(ctx context.Context, req *pb.InvokeServiceRequest) (*commonv1pb.InvokeResponse, error) {
|
||||
if req.GetMessage() == nil {
|
||||
if req.Message == nil {
|
||||
return &commonv1pb.InvokeResponse{
|
||||
ContentType: "text/plain",
|
||||
Data: &anypb.Any{
|
||||
|
@ -261,14 +260,14 @@ func (s *testDaprServer) InvokeService(ctx context.Context, req *pb.InvokeServic
|
|||
}, nil
|
||||
}
|
||||
return &commonv1pb.InvokeResponse{
|
||||
ContentType: req.GetMessage().GetContentType(),
|
||||
Data: req.GetMessage().GetData(),
|
||||
ContentType: req.Message.ContentType,
|
||||
Data: req.Message.Data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) GetState(ctx context.Context, req *pb.GetStateRequest) (*pb.GetStateResponse, error) {
|
||||
return &pb.GetStateResponse{
|
||||
Data: s.state[req.GetKey()],
|
||||
Data: s.state[req.Key],
|
||||
Etag: "1",
|
||||
}, nil
|
||||
}
|
||||
|
@ -290,16 +289,16 @@ func (s *testDaprServer) GetBulkState(ctx context.Context, in *pb.GetBulkStateRe
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) SaveState(ctx context.Context, req *pb.SaveStateRequest) (*emptypb.Empty, error) {
|
||||
for _, item := range req.GetStates() {
|
||||
s.state[item.GetKey()] = item.GetValue()
|
||||
func (s *testDaprServer) SaveState(ctx context.Context, req *pb.SaveStateRequest) (*empty.Empty, error) {
|
||||
for _, item := range req.States {
|
||||
s.state[item.Key] = item.Value
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) QueryStateAlpha1(ctx context.Context, req *pb.QueryStateRequest) (*pb.QueryStateResponse, error) {
|
||||
var v map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(req.GetQuery()), &v); err != nil {
|
||||
if err := json.Unmarshal([]byte(req.Query), &v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -307,39 +306,39 @@ func (s *testDaprServer) QueryStateAlpha1(ctx context.Context, req *pb.QueryStat
|
|||
Results: make([]*pb.QueryStateItem, 0, len(s.state)),
|
||||
}
|
||||
for key, value := range s.state {
|
||||
ret.Results = append(ret.GetResults(), &pb.QueryStateItem{Key: key, Data: value})
|
||||
ret.Results = append(ret.Results, &pb.QueryStateItem{Key: key, Data: value})
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) DeleteState(ctx context.Context, req *pb.DeleteStateRequest) (*emptypb.Empty, error) {
|
||||
delete(s.state, req.GetKey())
|
||||
return &emptypb.Empty{}, nil
|
||||
func (s *testDaprServer) DeleteState(ctx context.Context, req *pb.DeleteStateRequest) (*empty.Empty, error) {
|
||||
delete(s.state, req.Key)
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) DeleteBulkState(ctx context.Context, req *pb.DeleteBulkStateRequest) (*emptypb.Empty, error) {
|
||||
for _, item := range req.GetStates() {
|
||||
delete(s.state, item.GetKey())
|
||||
func (s *testDaprServer) DeleteBulkState(ctx context.Context, req *pb.DeleteBulkStateRequest) (*empty.Empty, error) {
|
||||
for _, item := range req.States {
|
||||
delete(s.state, item.Key)
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) ExecuteStateTransaction(ctx context.Context, in *pb.ExecuteStateTransactionRequest) (*emptypb.Empty, error) {
|
||||
func (s *testDaprServer) ExecuteStateTransaction(ctx context.Context, in *pb.ExecuteStateTransactionRequest) (*empty.Empty, error) {
|
||||
for _, op := range in.GetOperations() {
|
||||
item := op.GetRequest()
|
||||
switch opType := op.GetOperationType(); opType {
|
||||
case "upsert":
|
||||
s.state[item.GetKey()] = item.GetValue()
|
||||
s.state[item.Key] = item.Value
|
||||
case "delete":
|
||||
delete(s.state, item.GetKey())
|
||||
delete(s.state, item.Key)
|
||||
default:
|
||||
return &emptypb.Empty{}, fmt.Errorf("invalid operation type: %s", opType)
|
||||
return &empty.Empty{}, fmt.Errorf("invalid operation type: %s", opType)
|
||||
}
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) GetMetadata(ctx context.Context, req *pb.GetMetadataRequest) (metadata *pb.GetMetadataResponse, err error) {
|
||||
func (s *testDaprServer) GetMetadata(ctx context.Context, req *empty.Empty) (metadata *pb.GetMetadataResponse, err error) {
|
||||
resp := &pb.GetMetadataResponse{
|
||||
Id: uuid.NewString(),
|
||||
ActiveActorsCount: []*pb.ActiveActorsCount{},
|
||||
|
@ -350,12 +349,12 @@ func (s *testDaprServer) GetMetadata(ctx context.Context, req *pb.GetMetadataReq
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) SetMetadata(ctx context.Context, req *pb.SetMetadataRequest) (*emptypb.Empty, error) {
|
||||
return &emptypb.Empty{}, nil
|
||||
func (s *testDaprServer) SetMetadata(ctx context.Context, req *pb.SetMetadataRequest) (*empty.Empty, error) {
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) PublishEvent(ctx context.Context, req *pb.PublishEventRequest) (*emptypb.Empty, error) {
|
||||
return &emptypb.Empty{}, nil
|
||||
func (s *testDaprServer) PublishEvent(ctx context.Context, req *pb.PublishEventRequest) (*empty.Empty, error) {
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
// BulkPublishEventAlpha1 mocks the BulkPublishEventAlpha1 API.
|
||||
|
@ -363,14 +362,14 @@ func (s *testDaprServer) PublishEvent(ctx context.Context, req *pb.PublishEventR
|
|||
// It will fail the entire request if an event starts with "failall".
|
||||
func (s *testDaprServer) BulkPublishEventAlpha1(ctx context.Context, req *pb.BulkPublishRequest) (*pb.BulkPublishResponse, error) {
|
||||
failedEntries := make([]*pb.BulkPublishResponseFailedEntry, 0)
|
||||
for _, entry := range req.GetEntries() {
|
||||
if bytes.HasPrefix(entry.GetEvent(), []byte("failall")) {
|
||||
for _, entry := range req.Entries {
|
||||
if bytes.HasPrefix(entry.Event, []byte("failall")) {
|
||||
// fail the entire request
|
||||
return nil, errors.New("failed to publish events")
|
||||
} else if bytes.HasPrefix(entry.GetEvent(), []byte("fail")) {
|
||||
} else if bytes.HasPrefix(entry.Event, []byte("fail")) {
|
||||
// fail this entry
|
||||
failedEntries = append(failedEntries, &pb.BulkPublishResponseFailedEntry{
|
||||
EntryId: entry.GetEntryId(),
|
||||
EntryId: entry.EntryId,
|
||||
Error: "failed to publish events",
|
||||
})
|
||||
}
|
||||
|
@ -379,15 +378,15 @@ func (s *testDaprServer) BulkPublishEventAlpha1(ctx context.Context, req *pb.Bul
|
|||
}
|
||||
|
||||
func (s *testDaprServer) InvokeBinding(ctx context.Context, req *pb.InvokeBindingRequest) (*pb.InvokeBindingResponse, error) {
|
||||
if req.GetData() == nil {
|
||||
if req.Data == nil {
|
||||
return &pb.InvokeBindingResponse{
|
||||
Data: []byte("test"),
|
||||
Metadata: map[string]string{"k1": "v1", "k2": "v2"},
|
||||
}, nil
|
||||
}
|
||||
return &pb.InvokeBindingResponse{
|
||||
Data: req.GetData(),
|
||||
Metadata: req.GetMetadata(),
|
||||
Data: req.Data,
|
||||
Metadata: req.Metadata,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -411,12 +410,16 @@ func (s *testDaprServer) GetBulkSecret(ctx context.Context, req *pb.GetBulkSecre
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) RegisterActorReminder(ctx context.Context, req *pb.RegisterActorReminderRequest) (*emptypb.Empty, error) {
|
||||
return &emptypb.Empty{}, nil
|
||||
func (s *testDaprServer) RegisterActorReminder(ctx context.Context, req *pb.RegisterActorReminderRequest) (*empty.Empty, error) {
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) UnregisterActorReminder(ctx context.Context, req *pb.UnregisterActorReminderRequest) (*emptypb.Empty, error) {
|
||||
return &emptypb.Empty{}, nil
|
||||
func (s *testDaprServer) UnregisterActorReminder(ctx context.Context, req *pb.UnregisterActorReminderRequest) (*empty.Empty, error) {
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) RenameActorReminder(ctx context.Context, req *pb.RenameActorReminderRequest) (*empty.Empty, error) {
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) InvokeActor(context.Context, *pb.InvokeActorRequest) (*pb.InvokeActorResponse, error) {
|
||||
|
@ -425,16 +428,16 @@ func (s *testDaprServer) InvokeActor(context.Context, *pb.InvokeActorRequest) (*
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) RegisterActorTimer(context.Context, *pb.RegisterActorTimerRequest) (*emptypb.Empty, error) {
|
||||
return &emptypb.Empty{}, nil
|
||||
func (s *testDaprServer) RegisterActorTimer(context.Context, *pb.RegisterActorTimerRequest) (*empty.Empty, error) {
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) UnregisterActorTimer(context.Context, *pb.UnregisterActorTimerRequest) (*emptypb.Empty, error) {
|
||||
return &emptypb.Empty{}, nil
|
||||
func (s *testDaprServer) UnregisterActorTimer(context.Context, *pb.UnregisterActorTimerRequest) (*empty.Empty, error) {
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) Shutdown(ctx context.Context, req *pb.ShutdownRequest) (*emptypb.Empty, error) {
|
||||
return &emptypb.Empty{}, nil
|
||||
func (s *testDaprServer) Shutdown(ctx context.Context, req *empty.Empty) (*empty.Empty, error) {
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) GetConfiguration(ctx context.Context, in *pb.GetConfigurationRequest) (*pb.GetConfigurationResponse, error) {
|
||||
|
@ -466,7 +469,7 @@ func (s *testDaprServer) SubscribeConfiguration(in *pb.SubscribeConfigurationReq
|
|||
return err
|
||||
}
|
||||
|
||||
for range 5 {
|
||||
for i := 0; i < 5; i++ {
|
||||
select {
|
||||
case <-stopCh:
|
||||
return nil
|
||||
|
@ -492,98 +495,15 @@ func (s *testDaprServer) SubscribeConfiguration(in *pb.SubscribeConfigurationReq
|
|||
func (s *testDaprServer) UnsubscribeConfiguration(ctx context.Context, in *pb.UnsubscribeConfigurationRequest) (*pb.UnsubscribeConfigurationResponse, error) {
|
||||
s.configurationSubscriptionIDMapLoc.Lock()
|
||||
defer s.configurationSubscriptionIDMapLoc.Unlock()
|
||||
ch, ok := s.configurationSubscriptionID[in.GetId()]
|
||||
ch, ok := s.configurationSubscriptionID[in.Id]
|
||||
if !ok {
|
||||
return &pb.UnsubscribeConfigurationResponse{Ok: true}, nil
|
||||
}
|
||||
close(ch)
|
||||
delete(s.configurationSubscriptionID, in.GetId())
|
||||
delete(s.configurationSubscriptionID, in.Id)
|
||||
return &pb.UnsubscribeConfigurationResponse{Ok: true}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) StartWorkflowBeta1(ctx context.Context, in *pb.StartWorkflowRequest) (*pb.StartWorkflowResponse, error) {
|
||||
if in.GetInstanceId() == testWorkflowFailureID {
|
||||
return nil, errors.New("test failure")
|
||||
}
|
||||
return &pb.StartWorkflowResponse{
|
||||
InstanceId: in.GetInstanceId(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) GetWorkflowBeta1(ctx context.Context, in *pb.GetWorkflowRequest) (*pb.GetWorkflowResponse, error) {
|
||||
if in.GetInstanceId() == testWorkflowFailureID {
|
||||
return nil, errors.New("test failure")
|
||||
}
|
||||
return &pb.GetWorkflowResponse{
|
||||
InstanceId: in.GetInstanceId(),
|
||||
WorkflowName: "TestWorkflowName",
|
||||
RuntimeStatus: "Running",
|
||||
Properties: make(map[string]string),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) PurgeWorkflowBeta1(ctx context.Context, in *pb.PurgeWorkflowRequest) (*emptypb.Empty, error) {
|
||||
if in.GetInstanceId() == testWorkflowFailureID {
|
||||
return nil, errors.New("test failure")
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) TerminateWorkflowBeta1(ctx context.Context, in *pb.TerminateWorkflowRequest) (*emptypb.Empty, error) {
|
||||
if in.GetInstanceId() == testWorkflowFailureID {
|
||||
return nil, errors.New("test failure")
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) PauseWorkflowBeta1(ctx context.Context, in *pb.PauseWorkflowRequest) (*emptypb.Empty, error) {
|
||||
if in.GetInstanceId() == testWorkflowFailureID {
|
||||
return nil, errors.New("test failure")
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) ResumeWorkflowBeta1(ctx context.Context, in *pb.ResumeWorkflowRequest) (*emptypb.Empty, error) {
|
||||
if in.GetInstanceId() == testWorkflowFailureID {
|
||||
return nil, errors.New("test failure")
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) RaiseEventWorkflowBeta1(ctx context.Context, in *pb.RaiseEventWorkflowRequest) (*emptypb.Empty, error) {
|
||||
if in.GetInstanceId() == testWorkflowFailureID {
|
||||
return nil, errors.New("test failure")
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) ScheduleJobAlpha1(ctx context.Context, in *pb.ScheduleJobRequest) (*pb.ScheduleJobResponse, error) {
|
||||
return &pb.ScheduleJobResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) GetJobAlpha1(ctx context.Context, in *pb.GetJobRequest) (*pb.GetJobResponse, error) {
|
||||
var (
|
||||
schedule = "@every 10s"
|
||||
dueTime = "10s"
|
||||
repeats uint32 = 4
|
||||
ttl = "10s"
|
||||
)
|
||||
return &pb.GetJobResponse{
|
||||
Job: &pb.Job{
|
||||
Name: "name",
|
||||
Schedule: &schedule,
|
||||
Repeats: &repeats,
|
||||
DueTime: &dueTime,
|
||||
Ttl: &ttl,
|
||||
Data: nil,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *testDaprServer) DeleteJobAlpha1(ctx context.Context, in *pb.DeleteJobRequest) (*pb.DeleteJobResponse, error) {
|
||||
return &pb.DeleteJobResponse{}, nil
|
||||
}
|
||||
|
||||
func TestGrpcClient(t *testing.T) {
|
||||
protoClient := pb.NewDaprClient(nil)
|
||||
client := &GRPCClient{protoClient: protoClient}
|
||||
|
|
|
@ -50,11 +50,11 @@ func (c *GRPCClient) GetConfigurationItems(ctx context.Context, storeName string
|
|||
}
|
||||
|
||||
configItems := make(map[string]*ConfigurationItem)
|
||||
for k, v := range rsp.GetItems() {
|
||||
for k, v := range rsp.Items {
|
||||
configItems[k] = &ConfigurationItem{
|
||||
Value: v.GetValue(),
|
||||
Version: v.GetVersion(),
|
||||
Metadata: v.GetMetadata(),
|
||||
Value: v.Value,
|
||||
Version: v.Version,
|
||||
Metadata: v.Metadata,
|
||||
}
|
||||
}
|
||||
return configItems, nil
|
||||
|
@ -88,21 +88,21 @@ func (c *GRPCClient) SubscribeConfigurationItems(ctx context.Context, storeName
|
|||
}
|
||||
configurationItems := make(map[string]*ConfigurationItem)
|
||||
|
||||
for k, v := range rsp.GetItems() {
|
||||
for k, v := range rsp.Items {
|
||||
configurationItems[k] = &ConfigurationItem{
|
||||
Value: v.GetValue(),
|
||||
Version: v.GetVersion(),
|
||||
Metadata: v.GetMetadata(),
|
||||
Value: v.Value,
|
||||
Version: v.Version,
|
||||
Metadata: v.Metadata,
|
||||
}
|
||||
}
|
||||
// Get the subscription ID from the first response.
|
||||
if isFirst {
|
||||
subscribeIDChan <- rsp.GetId()
|
||||
subscribeIDChan <- rsp.Id
|
||||
isFirst = false
|
||||
}
|
||||
// Do not invoke handler in case there are no items.
|
||||
if len(configurationItems) > 0 {
|
||||
handler(rsp.GetId(), configurationItems)
|
||||
handler(rsp.Id, configurationItems)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@ -119,7 +119,7 @@ func (c *GRPCClient) UnsubscribeConfigurationItems(ctx context.Context, storeNam
|
|||
if err != nil {
|
||||
return fmt.Errorf("unsubscribe failed with error = %w", err)
|
||||
}
|
||||
if !resp.GetOk() {
|
||||
if !resp.Ok {
|
||||
return fmt.Errorf("unsubscribe error message = %s", resp.GetMessage())
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -15,27 +14,27 @@ const (
|
|||
)
|
||||
|
||||
func TestGetConfigurationItem(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("get configuration item", func(t *testing.T) {
|
||||
resp, err := testClient.GetConfigurationItem(ctx, "example-config", "mykey")
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "mykey"+valueSuffix, resp.Value)
|
||||
})
|
||||
|
||||
t.Run("get configuration item with invalid storeName", func(t *testing.T) {
|
||||
_, err := testClient.GetConfigurationItem(ctx, "", "mykey")
|
||||
require.Error(t, err)
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetConfigurationItems(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
|
||||
keys := []string{"mykey1", "mykey2", "mykey3"}
|
||||
t.Run("Test get configuration items", func(t *testing.T) {
|
||||
resp, err := testClient.GetConfigurationItems(ctx, "example-config", keys)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
for _, k := range keys {
|
||||
assert.Equal(t, k+valueSuffix, resp[k].Value)
|
||||
}
|
||||
|
@ -43,7 +42,7 @@ func TestGetConfigurationItems(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSubscribeConfigurationItems(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
|
||||
var counter, totalCounter uint32
|
||||
counter = 0
|
||||
|
@ -58,7 +57,7 @@ func TestSubscribeConfigurationItems(t *testing.T) {
|
|||
atomic.AddUint32(&totalCounter, 1)
|
||||
}
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
time.Sleep(time.Second*5 + time.Millisecond*500)
|
||||
assert.Equal(t, uint32(5), atomic.LoadUint32(&counter))
|
||||
|
@ -66,7 +65,7 @@ func TestSubscribeConfigurationItems(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestUnSubscribeConfigurationItems(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
|
||||
var counter, totalCounter uint32
|
||||
t.Run("Test unsubscribe configuration items", func(t *testing.T) {
|
||||
|
@ -79,11 +78,11 @@ func TestUnSubscribeConfigurationItems(t *testing.T) {
|
|||
atomic.AddUint32(&totalCounter, 1)
|
||||
}
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
time.Sleep(time.Second * 2)
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
err = testClient.UnsubscribeConfigurationItems(ctx, "example-config", subscribeID)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
time.Sleep(time.Second * 5)
|
||||
assert.Equal(t, uint32(3), atomic.LoadUint32(&counter))
|
||||
|
|
|
@ -1,146 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
runtimev1pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
|
||||
)
|
||||
|
||||
// conversationRequest object - currently unexported as used in a functions option pattern
|
||||
type conversationRequest struct {
|
||||
name string
|
||||
inputs []ConversationInput
|
||||
Parameters map[string]*anypb.Any
|
||||
Metadata map[string]string
|
||||
ContextID *string
|
||||
ScrubPII *bool // Scrub PII from the output
|
||||
Temperature *float64
|
||||
}
|
||||
|
||||
// NewConversationRequest defines a request with a component name and one or more inputs as a slice
|
||||
func NewConversationRequest(llmName string, inputs []ConversationInput) conversationRequest {
|
||||
return conversationRequest{
|
||||
name: llmName,
|
||||
inputs: inputs,
|
||||
}
|
||||
}
|
||||
|
||||
type conversationRequestOption func(request *conversationRequest)
|
||||
|
||||
// ConversationInput defines a single input.
|
||||
type ConversationInput struct {
|
||||
// The content to send to the llm.
|
||||
Content string
|
||||
// The role of the message.
|
||||
Role *string
|
||||
// Whether to Scrub PII from the input
|
||||
ScrubPII *bool
|
||||
}
|
||||
|
||||
// ConversationResponse is the basic response from a conversationRequest.
|
||||
type ConversationResponse struct {
|
||||
ContextID string
|
||||
Outputs []ConversationResult
|
||||
}
|
||||
|
||||
// ConversationResult is the individual
|
||||
type ConversationResult struct {
|
||||
Result string
|
||||
Parameters map[string]*anypb.Any
|
||||
}
|
||||
|
||||
// WithParameters should be used to provide parameters for custom fields.
|
||||
func WithParameters(parameters map[string]*anypb.Any) conversationRequestOption {
|
||||
return func(o *conversationRequest) {
|
||||
o.Parameters = parameters
|
||||
}
|
||||
}
|
||||
|
||||
// WithMetadata used to define metadata to be passed to components.
|
||||
func WithMetadata(metadata map[string]string) conversationRequestOption {
|
||||
return func(o *conversationRequest) {
|
||||
o.Metadata = metadata
|
||||
}
|
||||
}
|
||||
|
||||
// WithContextID to provide a new context or continue an existing one.
|
||||
func WithContextID(id string) conversationRequestOption {
|
||||
return func(o *conversationRequest) {
|
||||
o.ContextID = &id
|
||||
}
|
||||
}
|
||||
|
||||
// WithScrubPII to define whether the outputs should have PII removed.
|
||||
func WithScrubPII(scrub bool) conversationRequestOption {
|
||||
return func(o *conversationRequest) {
|
||||
o.ScrubPII = &scrub
|
||||
}
|
||||
}
|
||||
|
||||
// WithTemperature to specify which way the LLM leans.
|
||||
func WithTemperature(temp float64) conversationRequestOption {
|
||||
return func(o *conversationRequest) {
|
||||
o.Temperature = &temp
|
||||
}
|
||||
}
|
||||
|
||||
// ConverseAlpha1 can invoke an LLM given a request created by the NewConversationRequest function.
|
||||
func (c *GRPCClient) ConverseAlpha1(ctx context.Context, req conversationRequest, options ...conversationRequestOption) (*ConversationResponse, error) {
|
||||
cinputs := make([]*runtimev1pb.ConversationInput, len(req.inputs))
|
||||
for i, in := range req.inputs {
|
||||
cinputs[i] = &runtimev1pb.ConversationInput{
|
||||
Content: in.Content,
|
||||
Role: in.Role,
|
||||
ScrubPII: in.ScrubPII,
|
||||
}
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
if opt != nil {
|
||||
opt(&req)
|
||||
}
|
||||
}
|
||||
|
||||
request := runtimev1pb.ConversationRequest{
|
||||
Name: req.name,
|
||||
ContextID: req.ContextID,
|
||||
Inputs: cinputs,
|
||||
Parameters: req.Parameters,
|
||||
Metadata: req.Metadata,
|
||||
ScrubPII: req.ScrubPII,
|
||||
Temperature: req.Temperature,
|
||||
}
|
||||
|
||||
resp, err := c.protoClient.ConverseAlpha1(ctx, &request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputs := make([]ConversationResult, len(resp.GetOutputs()))
|
||||
for i, o := range resp.GetOutputs() {
|
||||
outputs[i] = ConversationResult{
|
||||
Result: o.GetResult(),
|
||||
Parameters: o.GetParameters(),
|
||||
}
|
||||
}
|
||||
|
||||
return &ConversationResponse{
|
||||
ContextID: resp.GetContextID(),
|
||||
Outputs: outputs,
|
||||
}, nil
|
||||
}
|
|
@ -186,13 +186,13 @@ func (c *GRPCClient) performCryptoOperation(ctx context.Context, stream grpc.Cli
|
|||
// Write the data, if any, into the pipe
|
||||
payload = resProto.GetPayload()
|
||||
if payload != nil {
|
||||
if payload.GetSeq() != expectSeq {
|
||||
pw.CloseWithError(fmt.Errorf("invalid sequence number in chunk: %d (expected: %d)", payload.GetSeq(), expectSeq))
|
||||
if payload.Seq != expectSeq {
|
||||
pw.CloseWithError(fmt.Errorf("invalid sequence number in chunk: %d (expected: %d)", payload.Seq, expectSeq))
|
||||
return
|
||||
}
|
||||
expectSeq++
|
||||
|
||||
_, readErr = pw.Write(payload.GetData())
|
||||
_, readErr = pw.Write(payload.Data)
|
||||
if readErr != nil {
|
||||
pw.CloseWithError(fmt.Errorf("error writing data: %w", readErr))
|
||||
return
|
||||
|
|
|
@ -30,7 +30,7 @@ import (
|
|||
)
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("missing ComponentName", func(t *testing.T) {
|
||||
out, err := testClient.Encrypt(ctx,
|
||||
|
@ -138,7 +138,7 @@ func TestEncrypt(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDecrypt(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("missing ComponentName", func(t *testing.T) {
|
||||
out, err := testClient.Decrypt(ctx,
|
||||
|
@ -236,13 +236,13 @@ func (s *testDaprServer) performCryptoOperation(stream grpc.ServerStream, reqPro
|
|||
|
||||
payload := reqProto.GetPayload()
|
||||
if payload != nil {
|
||||
if payload.GetSeq() != expectSeq {
|
||||
pw.CloseWithError(fmt.Errorf("invalid sequence number: %d (expected: %d)", payload.GetSeq(), expectSeq))
|
||||
if payload.Seq != expectSeq {
|
||||
pw.CloseWithError(fmt.Errorf("invalid sequence number: %d (expected: %d)", payload.Seq, expectSeq))
|
||||
return
|
||||
}
|
||||
expectSeq++
|
||||
|
||||
_, err = pw.Write(payload.GetData())
|
||||
_, err = pw.Write(payload.Data)
|
||||
if err != nil {
|
||||
pw.CloseWithError(err)
|
||||
return
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
/*
|
||||
Copyright 2023 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Parsed represents a parsed gRPC endpoint.
|
||||
type Parsed struct {
|
||||
Target string
|
||||
TLS bool
|
||||
}
|
||||
|
||||
func ParseGRPCEndpoint(endpoint string) (Parsed, error) {
|
||||
target := endpoint
|
||||
if len(target) == 0 {
|
||||
return Parsed{}, errors.New("target is required")
|
||||
}
|
||||
|
||||
var dnsAuthority string
|
||||
var hostname string
|
||||
var tls bool
|
||||
|
||||
urlSplit := strings.Split(target, ":")
|
||||
if len(urlSplit) == 3 && !strings.Contains(target, "://") {
|
||||
target = strings.Replace(target, ":", "://", 1)
|
||||
} else if len(urlSplit) >= 2 && !strings.Contains(target, "://") && schemeKnown(urlSplit[0]) {
|
||||
target = strings.Replace(target, ":", "://", 1)
|
||||
} else {
|
||||
urlSplit = strings.Split(target, "://")
|
||||
if len(urlSplit) == 1 {
|
||||
target = "dns://" + target
|
||||
} else {
|
||||
scheme := urlSplit[0]
|
||||
if !schemeKnown(scheme) {
|
||||
return Parsed{}, fmt.Errorf(("unknown scheme: %q"), scheme)
|
||||
}
|
||||
|
||||
if scheme == "dns" {
|
||||
urlSplit = strings.Split(target, "/")
|
||||
if len(urlSplit) < 4 {
|
||||
return Parsed{}, fmt.Errorf("invalid dns scheme: %q", target)
|
||||
}
|
||||
dnsAuthority = urlSplit[2]
|
||||
target = "dns://" + urlSplit[3]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptarget, err := url.Parse(target)
|
||||
if err != nil {
|
||||
return Parsed{}, err
|
||||
}
|
||||
|
||||
var errs []string
|
||||
for k := range ptarget.Query() {
|
||||
if k != "tls" {
|
||||
errs = append(errs, fmt.Sprintf("unrecognized query parameter: %q", k))
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return Parsed{}, fmt.Errorf("failed to parse target %q: %s", target, strings.Join(errs, "; "))
|
||||
}
|
||||
|
||||
if ptarget.Query().Has("tls") {
|
||||
if ptarget.Scheme == "http" || ptarget.Scheme == "https" {
|
||||
return Parsed{}, errors.New("cannot use tls query parameter with http(s) scheme")
|
||||
}
|
||||
|
||||
qtls := ptarget.Query().Get("tls")
|
||||
if qtls != "true" && qtls != "false" {
|
||||
return Parsed{}, fmt.Errorf("invalid value for tls query parameter: %q", qtls)
|
||||
}
|
||||
|
||||
tls = qtls == "true"
|
||||
}
|
||||
|
||||
scheme := ptarget.Scheme
|
||||
if scheme == "https" {
|
||||
tls = true
|
||||
}
|
||||
if scheme == "http" || scheme == "https" {
|
||||
scheme = "dns"
|
||||
}
|
||||
|
||||
hostname = ptarget.Host
|
||||
|
||||
host, port, err := net.SplitHostPort(hostname)
|
||||
aerr, ok := err.(*net.AddrError)
|
||||
if ok && aerr.Err == "missing port in address" {
|
||||
port = "443"
|
||||
} else if err != nil {
|
||||
return Parsed{}, err
|
||||
} else {
|
||||
hostname = host
|
||||
}
|
||||
|
||||
if len(hostname) == 0 {
|
||||
if scheme == "dns" {
|
||||
hostname = "localhost"
|
||||
} else {
|
||||
hostname = ptarget.Path
|
||||
}
|
||||
}
|
||||
|
||||
switch scheme {
|
||||
case "unix":
|
||||
separator := ":"
|
||||
if strings.HasPrefix(endpoint, "unix://") {
|
||||
separator = "://"
|
||||
}
|
||||
target = scheme + separator + hostname
|
||||
|
||||
case "vsock":
|
||||
target = scheme + ":" + hostname + ":" + port
|
||||
|
||||
case "unix-abstract":
|
||||
target = scheme + ":" + hostname
|
||||
|
||||
case "dns":
|
||||
if len(ptarget.Path) > 0 {
|
||||
return Parsed{}, fmt.Errorf("path is not allowed: %q", ptarget.Path)
|
||||
}
|
||||
|
||||
if strings.Count(hostname, ":") == 7 && !strings.HasPrefix(hostname, "[") && !strings.HasSuffix(hostname, "]") {
|
||||
hostname = "[" + hostname + "]"
|
||||
}
|
||||
if len(dnsAuthority) > 0 {
|
||||
dnsAuthority = "//" + dnsAuthority + "/"
|
||||
}
|
||||
target = scheme + ":" + dnsAuthority + hostname + ":" + port
|
||||
|
||||
default:
|
||||
return Parsed{}, fmt.Errorf("unsupported scheme: %q", scheme)
|
||||
}
|
||||
|
||||
return Parsed{
|
||||
Target: target,
|
||||
TLS: tls,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func schemeKnown(scheme string) bool {
|
||||
for _, s := range []string{
|
||||
"dns",
|
||||
"unix",
|
||||
"unix-abstract",
|
||||
"vsock",
|
||||
"http",
|
||||
"https",
|
||||
} {
|
||||
if scheme == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
|
@ -1,293 +0,0 @@
|
|||
/*
|
||||
Copyright 2023 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
expTarget string
|
||||
expTLS bool
|
||||
expError bool
|
||||
}{
|
||||
"": {
|
||||
expTarget: "",
|
||||
expTLS: false,
|
||||
expError: true,
|
||||
},
|
||||
":5000": {
|
||||
expTarget: "dns:localhost:5000",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
":5000?tls=false": {
|
||||
expTarget: "dns:localhost:5000",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
":5000?tls=true": {
|
||||
expTarget: "dns:localhost:5000",
|
||||
expTLS: true,
|
||||
expError: false,
|
||||
},
|
||||
"myhost": {
|
||||
expTarget: "dns:myhost:443",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"myhost?tls=false": {
|
||||
expTarget: "dns:myhost:443",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"myhost?tls=true": {
|
||||
expTarget: "dns:myhost:443",
|
||||
expTLS: true,
|
||||
expError: false,
|
||||
},
|
||||
"myhost:443": {
|
||||
expTarget: "dns:myhost:443",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"myhost:443?tls=false": {
|
||||
expTarget: "dns:myhost:443",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"myhost:443?tls=true": {
|
||||
expTarget: "dns:myhost:443",
|
||||
expTLS: true,
|
||||
expError: false,
|
||||
},
|
||||
"http://myhost": {
|
||||
expTarget: "dns:myhost:443",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"http://myhost?tls=false": {
|
||||
expTarget: "",
|
||||
expTLS: false,
|
||||
expError: true,
|
||||
},
|
||||
"http://myhost?tls=true": {
|
||||
expTarget: "",
|
||||
expTLS: false,
|
||||
expError: true,
|
||||
},
|
||||
"http://myhost:443": {
|
||||
expTarget: "dns:myhost:443",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"http://myhost:443?tls=false": {
|
||||
expTarget: "",
|
||||
expTLS: false,
|
||||
expError: true,
|
||||
},
|
||||
"http://myhost:443?tls=true": {
|
||||
expTarget: "",
|
||||
expTLS: false,
|
||||
expError: true,
|
||||
},
|
||||
"http://myhost:5000": {
|
||||
expTarget: "dns:myhost:5000",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"http://myhost:5000?tls=false": {
|
||||
expTarget: "",
|
||||
expTLS: false,
|
||||
expError: true,
|
||||
},
|
||||
"http://myhost:5000?tls=true": {
|
||||
expTarget: "",
|
||||
expTLS: false,
|
||||
expError: true,
|
||||
},
|
||||
"https://myhost:443": {
|
||||
expTarget: "dns:myhost:443",
|
||||
expTLS: true,
|
||||
expError: false,
|
||||
},
|
||||
"https://myhost:443/tls=false": {
|
||||
expTarget: "",
|
||||
expTLS: false,
|
||||
expError: true,
|
||||
},
|
||||
"https://myhost:443?tls=true": {
|
||||
expTarget: "",
|
||||
expTLS: false,
|
||||
expError: true,
|
||||
},
|
||||
"dns:myhost": {
|
||||
expTarget: "dns:myhost:443",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"dns:myhost?tls=false": {
|
||||
expTarget: "dns:myhost:443",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"dns:myhost?tls=true": {
|
||||
expTarget: "dns:myhost:443",
|
||||
expTLS: true,
|
||||
expError: false,
|
||||
},
|
||||
"dns://myauthority:53/myhost": {
|
||||
expTarget: "dns://myauthority:53/myhost:443",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"dns://myauthority:53/myhost?tls=false": {
|
||||
expTarget: "dns://myauthority:53/myhost:443",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"dns://myauthority:53/myhost?tls=true": {
|
||||
expTarget: "dns://myauthority:53/myhost:443",
|
||||
expTLS: true,
|
||||
expError: false,
|
||||
},
|
||||
"dns://myhost": {
|
||||
expTarget: "",
|
||||
expTLS: false,
|
||||
expError: true,
|
||||
},
|
||||
"unix:my.sock": {
|
||||
expTarget: "unix:my.sock",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"unix:my.sock?tls=true": {
|
||||
expTarget: "unix:my.sock",
|
||||
expTLS: true,
|
||||
expError: false,
|
||||
},
|
||||
"unix://my.sock": {
|
||||
expTarget: "unix://my.sock",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"unix:///my.sock": {
|
||||
expTarget: "unix:///my.sock",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"unix://my.sock?tls=true": {
|
||||
expTarget: "unix://my.sock",
|
||||
expTLS: true,
|
||||
expError: false,
|
||||
},
|
||||
"unix-abstract:my.sock": {
|
||||
expTarget: "unix-abstract:my.sock",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"unix-abstract:my.sock?tls=false": {
|
||||
expTarget: "unix-abstract:my.sock",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"unix-abstract:my.sock?tls=true": {
|
||||
expTarget: "unix-abstract:my.sock",
|
||||
expTLS: true,
|
||||
expError: false,
|
||||
},
|
||||
"vsock:mycid:5000": {
|
||||
expTarget: "vsock:mycid:5000",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"vsock:mycid:5000?tls=false": {
|
||||
expTarget: "vsock:mycid:5000",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"vsock:mycid:5000?tls=true": {
|
||||
expTarget: "vsock:mycid:5000",
|
||||
expTLS: true,
|
||||
expError: false,
|
||||
},
|
||||
"dns:1.2.3.4:443": {
|
||||
expTarget: "dns:1.2.3.4:443",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"dns:[2001:db8:1f70::999:de8:7648:6e8]:443": {
|
||||
expTarget: "dns:[2001:db8:1f70::999:de8:7648:6e8]:443",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"dns:[2001:db8:1f70::999:de8:7648:6e8]:5000": {
|
||||
expTarget: "dns:[2001:db8:1f70::999:de8:7648:6e8]:5000",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"dns:[2001:db8:1f70::999:de8:7648:6e8]:5000?abc=[]": {
|
||||
expTarget: "",
|
||||
expTLS: false,
|
||||
expError: true,
|
||||
},
|
||||
"dns://myauthority:53/[2001:db8:1f70::999:de8:7648:6e8]": {
|
||||
expTarget: "dns://myauthority:53/[2001:db8:1f70::999:de8:7648:6e8]:443",
|
||||
expTLS: false,
|
||||
expError: false,
|
||||
},
|
||||
"https://[2001:db8:1f70::999:de8:7648:6e8]": {
|
||||
expTarget: "dns:[2001:db8:1f70::999:de8:7648:6e8]:443",
|
||||
expTLS: true,
|
||||
expError: false,
|
||||
},
|
||||
"https://[2001:db8:1f70::999:de8:7648:6e8]:5000": {
|
||||
expTarget: "dns:[2001:db8:1f70::999:de8:7648:6e8]:5000",
|
||||
expTLS: true,
|
||||
expError: false,
|
||||
},
|
||||
"host:5000/v1/dapr": {
|
||||
expTarget: "",
|
||||
expTLS: false,
|
||||
expError: true,
|
||||
},
|
||||
"host:5000/?a=1": {
|
||||
expTarget: "",
|
||||
expTLS: false,
|
||||
expError: true,
|
||||
},
|
||||
"inv-scheme://myhost": {
|
||||
expTarget: "",
|
||||
expTLS: false,
|
||||
expError: true,
|
||||
},
|
||||
"inv-scheme:myhost:5000": {
|
||||
expTarget: "",
|
||||
expTLS: false,
|
||||
expError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for url, tc := range tests {
|
||||
t.Run(url, func(t *testing.T) {
|
||||
parsed, err := ParseGRPCEndpoint(url)
|
||||
assert.Equalf(t, tc.expError, err != nil, "%v", err)
|
||||
assert.Equal(t, tc.expTarget, parsed.Target)
|
||||
assert.Equal(t, tc.expTLS, parsed.TLS)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
anypb "github.com/golang/protobuf/ptypes/any"
|
||||
|
||||
v1 "github.com/dapr/dapr/pkg/proto/common/v1"
|
||||
pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
|
||||
|
@ -39,14 +39,14 @@ func (c *GRPCClient) invokeServiceWithRequest(ctx context.Context, req *pb.Invok
|
|||
return nil, errors.New("nil request")
|
||||
}
|
||||
|
||||
resp, err := c.protoClient.InvokeService(ctx, req)
|
||||
resp, err := c.protoClient.InvokeService(c.withAuthToken(ctx), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// allow for service to not return any value
|
||||
if resp != nil && resp.GetData() != nil {
|
||||
out = resp.GetData().GetValue()
|
||||
out = resp.GetData().Value
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -14,10 +14,9 @@ limitations under the License.
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
v1 "github.com/dapr/dapr/pkg/proto/common/v1"
|
||||
|
@ -38,7 +37,7 @@ type _testStructwithSlices struct {
|
|||
}
|
||||
|
||||
func TestInvokeMethodWithContent(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
data := "ping"
|
||||
|
||||
t.Run("with content", func(t *testing.T) {
|
||||
|
@ -47,7 +46,7 @@ func TestInvokeMethodWithContent(t *testing.T) {
|
|||
Data: []byte(data),
|
||||
}
|
||||
resp, err := testClient.InvokeMethodWithContent(ctx, "test", "fn", "post", content)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, string(resp), data)
|
||||
})
|
||||
|
@ -58,28 +57,28 @@ func TestInvokeMethodWithContent(t *testing.T) {
|
|||
Data: []byte(data),
|
||||
}
|
||||
resp, err := testClient.InvokeMethodWithContent(ctx, "test", "fn?foo=bar&url=http://dapr.io", "get", content)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, string(resp), data)
|
||||
})
|
||||
|
||||
t.Run("without content", func(t *testing.T) {
|
||||
resp, err := testClient.InvokeMethod(ctx, "test", "fn", "get")
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, resp)
|
||||
})
|
||||
|
||||
t.Run("without service ID", func(t *testing.T) {
|
||||
_, err := testClient.InvokeMethod(ctx, "", "fn", "get")
|
||||
require.Error(t, err)
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
t.Run("without method", func(t *testing.T) {
|
||||
_, err := testClient.InvokeMethod(ctx, "test", "", "get")
|
||||
require.Error(t, err)
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
t.Run("without verb", func(t *testing.T) {
|
||||
_, err := testClient.InvokeMethod(ctx, "test", "fn", "")
|
||||
require.Error(t, err)
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
t.Run("from struct with text", func(t *testing.T) {
|
||||
testdata := _testCustomContentwithText{
|
||||
|
@ -87,7 +86,7 @@ func TestInvokeMethodWithContent(t *testing.T) {
|
|||
Key2: "value2",
|
||||
}
|
||||
_, err := testClient.InvokeMethodWithCustomContent(ctx, "test", "fn", "post", "text/plain", testdata)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("from struct with text and numbers", func(t *testing.T) {
|
||||
|
@ -96,7 +95,7 @@ func TestInvokeMethodWithContent(t *testing.T) {
|
|||
Key2: 2500,
|
||||
}
|
||||
_, err := testClient.InvokeMethodWithCustomContent(ctx, "test", "fn", "post", "text/plain", testdata)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("from struct with slices", func(t *testing.T) {
|
||||
|
@ -105,7 +104,7 @@ func TestInvokeMethodWithContent(t *testing.T) {
|
|||
Key2: []int{25, 40, 600},
|
||||
}
|
||||
_, err := testClient.InvokeMethodWithCustomContent(ctx, "test", "fn", "post", "text/plain", testdata)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -113,27 +112,27 @@ func TestVerbParsing(t *testing.T) {
|
|||
t.Run("valid lower case", func(t *testing.T) {
|
||||
v := queryAndVerbToHTTPExtension("", "post")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, v1.HTTPExtension_POST, v.GetVerb())
|
||||
assert.Empty(t, v.GetQuerystring())
|
||||
assert.Equal(t, v1.HTTPExtension_POST, v.Verb)
|
||||
assert.Len(t, v.Querystring, 0)
|
||||
})
|
||||
|
||||
t.Run("valid upper case", func(t *testing.T) {
|
||||
v := queryAndVerbToHTTPExtension("", "GET")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, v1.HTTPExtension_GET, v.GetVerb())
|
||||
assert.Equal(t, v1.HTTPExtension_GET, v.Verb)
|
||||
})
|
||||
|
||||
t.Run("invalid verb", func(t *testing.T) {
|
||||
v := queryAndVerbToHTTPExtension("", "BAD")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, v1.HTTPExtension_NONE, v.GetVerb())
|
||||
assert.Equal(t, v1.HTTPExtension_NONE, v.Verb)
|
||||
})
|
||||
|
||||
t.Run("valid query", func(t *testing.T) {
|
||||
v := queryAndVerbToHTTPExtension("foo=bar&url=http://dapr.io", "post")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, v1.HTTPExtension_POST, v.GetVerb())
|
||||
assert.Equal(t, "foo=bar&url=http://dapr.io", v.GetQuerystring())
|
||||
assert.Equal(t, v1.HTTPExtension_POST, v.Verb)
|
||||
assert.Equal(t, "foo=bar&url=http://dapr.io", v.Querystring)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ func (c *GRPCClient) TryLockAlpha1(ctx context.Context, storeName string, reques
|
|||
}
|
||||
|
||||
return &LockResponse{
|
||||
Success: resp.GetSuccess(),
|
||||
Success: resp.Success,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ func (c *GRPCClient) UnlockAlpha1(ctx context.Context, storeName string, request
|
|||
}
|
||||
|
||||
return &UnlockResponse{
|
||||
StatusCode: int32(resp.GetStatus()),
|
||||
Status: pb.UnlockResponse_Status_name[int32(resp.GetStatus())],
|
||||
StatusCode: int32(resp.Status),
|
||||
Status: pb.UnlockResponse_Status_name[int32(resp.Status)],
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -14,10 +14,9 @@ limitations under the License.
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
|
||||
|
@ -28,18 +27,18 @@ const (
|
|||
)
|
||||
|
||||
func TestLock(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("try lock invalid store name", func(t *testing.T) {
|
||||
r, err := testClient.TryLockAlpha1(ctx, "", &LockRequest{})
|
||||
assert.Nil(t, r)
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("try lock invalid request", func(t *testing.T) {
|
||||
r, err := testClient.TryLockAlpha1(ctx, testLockStore, nil)
|
||||
assert.Nil(t, r)
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("try lock", func(t *testing.T) {
|
||||
|
@ -49,7 +48,7 @@ func TestLock(t *testing.T) {
|
|||
ExpiryInSeconds: 5,
|
||||
})
|
||||
assert.NotNil(t, r)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, r.Success)
|
||||
})
|
||||
|
||||
|
@ -59,13 +58,13 @@ func TestLock(t *testing.T) {
|
|||
ResourceID: "resource1",
|
||||
})
|
||||
assert.Nil(t, r)
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("unlock invalid request", func(t *testing.T) {
|
||||
r, err := testClient.UnlockAlpha1(ctx, "testLockStore", nil)
|
||||
assert.Nil(t, r)
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("unlock", func(t *testing.T) {
|
||||
|
@ -74,7 +73,7 @@ func TestLock(t *testing.T) {
|
|||
ResourceID: "resource1",
|
||||
})
|
||||
assert.NotNil(t, r)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, pb.UnlockResponse_SUCCESS.String(), r.Status)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
|
||||
pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
|
||||
)
|
||||
|
||||
|
@ -52,53 +54,53 @@ type MetadataHTTPEndpoint struct {
|
|||
|
||||
// GetMetadata returns the metadata of the sidecar
|
||||
func (c *GRPCClient) GetMetadata(ctx context.Context) (metadata *GetMetadataResponse, err error) {
|
||||
resp, err := c.protoClient.GetMetadata(ctx, &pb.GetMetadataRequest{})
|
||||
resp, err := c.protoClient.GetMetadata(ctx, &emptypb.Empty{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error invoking service: %w", err)
|
||||
}
|
||||
if resp != nil {
|
||||
activeActorsCount := make([]*MetadataActiveActorsCount, len(resp.GetActorRuntime().GetActiveActors()))
|
||||
for i, a := range resp.GetActorRuntime().GetActiveActors() {
|
||||
activeActorsCount[i] = &MetadataActiveActorsCount{
|
||||
Type: a.GetType(),
|
||||
Count: a.GetCount(),
|
||||
activeActorsCount := make([]*MetadataActiveActorsCount, len(resp.ActiveActorsCount))
|
||||
for a := range resp.ActiveActorsCount {
|
||||
activeActorsCount[a] = &MetadataActiveActorsCount{
|
||||
Type: resp.ActiveActorsCount[a].Type,
|
||||
Count: resp.ActiveActorsCount[a].Count,
|
||||
}
|
||||
}
|
||||
registeredComponents := make([]*MetadataRegisteredComponents, len(resp.GetRegisteredComponents()))
|
||||
for i, r := range resp.GetRegisteredComponents() {
|
||||
registeredComponents[i] = &MetadataRegisteredComponents{
|
||||
Name: r.GetName(),
|
||||
Type: r.GetType(),
|
||||
Version: r.GetVersion(),
|
||||
Capabilities: r.GetCapabilities(),
|
||||
registeredComponents := make([]*MetadataRegisteredComponents, len(resp.RegisteredComponents))
|
||||
for r := range resp.RegisteredComponents {
|
||||
registeredComponents[r] = &MetadataRegisteredComponents{
|
||||
Name: resp.RegisteredComponents[r].Name,
|
||||
Type: resp.RegisteredComponents[r].Type,
|
||||
Version: resp.RegisteredComponents[r].Version,
|
||||
Capabilities: resp.RegisteredComponents[r].Capabilities,
|
||||
}
|
||||
}
|
||||
subscriptions := make([]*MetadataSubscription, len(resp.GetSubscriptions()))
|
||||
for i, s := range resp.GetSubscriptions() {
|
||||
subscriptions := make([]*MetadataSubscription, len(resp.Subscriptions))
|
||||
for s := range resp.Subscriptions {
|
||||
rules := &PubsubSubscriptionRules{}
|
||||
for _, r := range s.GetRules().GetRules() {
|
||||
for r := range resp.Subscriptions[s].Rules.Rules {
|
||||
rules.Rules = append(rules.Rules, &PubsubSubscriptionRule{
|
||||
Match: r.GetMatch(),
|
||||
Path: r.GetPath(),
|
||||
Match: resp.Subscriptions[s].Rules.Rules[r].Match,
|
||||
Path: resp.Subscriptions[s].Rules.Rules[r].Path,
|
||||
})
|
||||
}
|
||||
|
||||
subscriptions[i] = &MetadataSubscription{
|
||||
PubsubName: s.GetPubsubName(),
|
||||
Topic: s.GetTopic(),
|
||||
Metadata: s.GetMetadata(),
|
||||
subscriptions[s] = &MetadataSubscription{
|
||||
PubsubName: resp.Subscriptions[s].PubsubName,
|
||||
Topic: resp.Subscriptions[s].Topic,
|
||||
Metadata: resp.Subscriptions[s].Metadata,
|
||||
Rules: rules,
|
||||
DeadLetterTopic: s.GetDeadLetterTopic(),
|
||||
DeadLetterTopic: resp.Subscriptions[s].DeadLetterTopic,
|
||||
}
|
||||
}
|
||||
httpEndpoints := make([]*MetadataHTTPEndpoint, len(resp.GetHttpEndpoints()))
|
||||
for i, e := range resp.GetHttpEndpoints() {
|
||||
httpEndpoints[i] = &MetadataHTTPEndpoint{
|
||||
Name: e.GetName(),
|
||||
httpEndpoints := make([]*MetadataHTTPEndpoint, len(resp.HttpEndpoints))
|
||||
for e := range resp.HttpEndpoints {
|
||||
httpEndpoints[e] = &MetadataHTTPEndpoint{
|
||||
Name: resp.HttpEndpoints[e].Name,
|
||||
}
|
||||
}
|
||||
metadata = &GetMetadataResponse{
|
||||
ID: resp.GetId(),
|
||||
ID: resp.Id,
|
||||
ActiveActorsCount: activeActorsCount,
|
||||
RegisteredComponents: registeredComponents,
|
||||
ExtendedMetadata: resp.GetExtendedMetadata(),
|
||||
|
|
|
@ -1,30 +1,29 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Test GetMetadata returns
|
||||
func TestGetMetadata(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
t.Run("get meta", func(t *testing.T) {
|
||||
metadata, err := testClient.GetMetadata(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, metadata)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetMetadata(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
t.Run("set meta", func(t *testing.T) {
|
||||
err := testClient.SetMetadata(ctx, "test_key", "test_value")
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
metadata, err := testClient.GetMetadata(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test_value", metadata.ExtendedMetadata["test_key"])
|
||||
})
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ func (c *GRPCClient) PublishEvent(ctx context.Context, pubsubName, topicName str
|
|||
}
|
||||
}
|
||||
|
||||
_, err := c.protoClient.PublishEvent(ctx, request)
|
||||
_, err := c.protoClient.PublishEvent(c.withAuthToken(ctx), request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error publishing event unto %s topic: %w", topicName, err)
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ func PublishEventWithMetadata(metadata map[string]string) PublishEventOption {
|
|||
// PublishEventWithRawPayload can be passed as option to PublishEvent to set rawPayload metadata.
|
||||
func PublishEventWithRawPayload() PublishEventOption {
|
||||
return func(e *pb.PublishEventRequest) {
|
||||
if e.GetMetadata() == nil {
|
||||
if e.Metadata == nil {
|
||||
e.Metadata = map[string]string{rawPayload: trueValue}
|
||||
} else {
|
||||
e.Metadata[rawPayload] = trueValue
|
||||
|
@ -156,7 +156,7 @@ func (c *GRPCClient) PublishEvents(ctx context.Context, pubsubName, topicName st
|
|||
failedEvents = append(failedEvents, event)
|
||||
continue
|
||||
}
|
||||
eventMap[entry.GetEntryId()] = event
|
||||
eventMap[entry.EntryId] = event
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,7 @@ func (c *GRPCClient) PublishEvents(ctx context.Context, pubsubName, topicName st
|
|||
o(request)
|
||||
}
|
||||
|
||||
res, err := c.protoClient.BulkPublishEventAlpha1(ctx, request)
|
||||
res, err := c.protoClient.BulkPublishEventAlpha1(c.withAuthToken(ctx), request)
|
||||
// If there is an error, all events failed to publish.
|
||||
if err != nil {
|
||||
return PublishEventsResponse{
|
||||
|
@ -178,11 +178,11 @@ func (c *GRPCClient) PublishEvents(ctx context.Context, pubsubName, topicName st
|
|||
}
|
||||
}
|
||||
|
||||
for _, failedEntry := range res.GetFailedEntries() {
|
||||
event, ok := eventMap[failedEntry.GetEntryId()]
|
||||
for _, failedEntry := range res.FailedEntries {
|
||||
event, ok := eventMap[failedEntry.EntryId]
|
||||
if !ok {
|
||||
// This should never happen.
|
||||
failedEvents = append(failedEvents, failedEntry.GetEntryId())
|
||||
failedEvents = append(failedEvents, failedEntry.EntryId)
|
||||
}
|
||||
failedEvents = append(failedEvents, event)
|
||||
}
|
||||
|
@ -224,12 +224,12 @@ func createBulkPublishRequestEntry(data interface{}) (*pb.BulkPublishRequestEntr
|
|||
return &pb.BulkPublishRequestEntry{}, fmt.Errorf("error serializing input struct: %w", err)
|
||||
}
|
||||
|
||||
if isCloudEvent(entry.GetEvent()) {
|
||||
if isCloudEvent(entry.Event) {
|
||||
entry.ContentType = "application/cloudevents+json"
|
||||
}
|
||||
}
|
||||
|
||||
if entry.GetEntryId() == "" {
|
||||
if entry.EntryId == "" {
|
||||
entry.EntryId = uuid.New().String()
|
||||
}
|
||||
|
||||
|
@ -239,7 +239,7 @@ func createBulkPublishRequestEntry(data interface{}) (*pb.BulkPublishRequestEntr
|
|||
// PublishEventsWithContentType can be passed as option to PublishEvents to explicitly set the same Content-Type for all events.
|
||||
func PublishEventsWithContentType(contentType string) PublishEventsOption {
|
||||
return func(r *pb.BulkPublishRequest) {
|
||||
for _, entry := range r.GetEntries() {
|
||||
for _, entry := range r.Entries {
|
||||
entry.ContentType = contentType
|
||||
}
|
||||
}
|
||||
|
@ -255,7 +255,7 @@ func PublishEventsWithMetadata(metadata map[string]string) PublishEventsOption {
|
|||
// PublishEventsWithRawPayload can be passed as option to PublishEvents to set rawPayload request metadata.
|
||||
func PublishEventsWithRawPayload() PublishEventsOption {
|
||||
return func(r *pb.BulkPublishRequest) {
|
||||
if r.GetMetadata() == nil {
|
||||
if r.Metadata == nil {
|
||||
r.Metadata = map[string]string{rawPayload: trueValue}
|
||||
} else {
|
||||
r.Metadata[rawPayload] = trueValue
|
||||
|
|
|
@ -14,10 +14,9 @@ limitations under the License.
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -38,21 +37,21 @@ type _testCustomContentwithSlices struct {
|
|||
|
||||
// go test -timeout 30s ./client -count 1 -run ^TestPublishEvent$
|
||||
func TestPublishEvent(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("with data", func(t *testing.T) {
|
||||
err := testClient.PublishEvent(ctx, "messages", "test", []byte("ping"))
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("without data", func(t *testing.T) {
|
||||
err := testClient.PublishEvent(ctx, "messages", "test", nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("with empty topic name", func(t *testing.T) {
|
||||
err := testClient.PublishEvent(ctx, "messages", "", []byte("ping"))
|
||||
require.Error(t, err)
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
|
||||
t.Run("from struct with text", func(t *testing.T) {
|
||||
|
@ -61,7 +60,7 @@ func TestPublishEvent(t *testing.T) {
|
|||
Key2: "value2",
|
||||
}
|
||||
err := testClient.PublishEventfromCustomContent(ctx, "messages", "test", testdata)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("from struct with text and numbers", func(t *testing.T) {
|
||||
|
@ -70,7 +69,7 @@ func TestPublishEvent(t *testing.T) {
|
|||
Key2: 2500,
|
||||
}
|
||||
err := testClient.PublishEventfromCustomContent(ctx, "messages", "test", testdata)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("from struct with slices", func(t *testing.T) {
|
||||
|
@ -79,27 +78,27 @@ func TestPublishEvent(t *testing.T) {
|
|||
Key2: []int{25, 40, 600},
|
||||
}
|
||||
err := testClient.PublishEventfromCustomContent(ctx, "messages", "test", testdata)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("error serializing JSON", func(t *testing.T) {
|
||||
err := testClient.PublishEventfromCustomContent(ctx, "messages", "test", make(chan struct{}))
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("raw payload", func(t *testing.T) {
|
||||
err := testClient.PublishEvent(ctx, "messages", "test", []byte("ping"), PublishEventWithRawPayload())
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
// go test -timeout 30s ./client -count 1 -run ^TestPublishEvents$
|
||||
func TestPublishEvents(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("without pubsub name", func(t *testing.T) {
|
||||
res := testClient.PublishEvents(ctx, "", "test", []interface{}{"ping", "pong"})
|
||||
require.Error(t, res.Error)
|
||||
assert.Error(t, res.Error)
|
||||
assert.Len(t, res.FailedEvents, 2)
|
||||
assert.Contains(t, res.FailedEvents, "ping")
|
||||
assert.Contains(t, res.FailedEvents, "pong")
|
||||
|
@ -107,7 +106,7 @@ func TestPublishEvents(t *testing.T) {
|
|||
|
||||
t.Run("without topic name", func(t *testing.T) {
|
||||
res := testClient.PublishEvents(ctx, "messages", "", []interface{}{"ping", "pong"})
|
||||
require.Error(t, res.Error)
|
||||
assert.Error(t, res.Error)
|
||||
assert.Len(t, res.FailedEvents, 2)
|
||||
assert.Contains(t, res.FailedEvents, "ping")
|
||||
assert.Contains(t, res.FailedEvents, "pong")
|
||||
|
@ -115,14 +114,14 @@ func TestPublishEvents(t *testing.T) {
|
|||
|
||||
t.Run("with data", func(t *testing.T) {
|
||||
res := testClient.PublishEvents(ctx, "messages", "test", []interface{}{"ping", "pong"})
|
||||
require.NoError(t, res.Error)
|
||||
assert.Empty(t, res.FailedEvents)
|
||||
assert.Nil(t, res.Error)
|
||||
assert.Len(t, res.FailedEvents, 0)
|
||||
})
|
||||
|
||||
t.Run("without data", func(t *testing.T) {
|
||||
res := testClient.PublishEvents(ctx, "messages", "test", nil)
|
||||
require.NoError(t, res.Error)
|
||||
assert.Empty(t, res.FailedEvents)
|
||||
assert.Nil(t, res.Error)
|
||||
assert.Len(t, res.FailedEvents, 0)
|
||||
})
|
||||
|
||||
t.Run("with struct data", func(t *testing.T) {
|
||||
|
@ -156,47 +155,47 @@ func TestPublishEvents(t *testing.T) {
|
|||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
res := testClient.PublishEvents(ctx, "messages", "test", []interface{}{tc.data})
|
||||
require.NoError(t, res.Error)
|
||||
assert.Empty(t, res.FailedEvents)
|
||||
assert.Nil(t, res.Error)
|
||||
assert.Len(t, res.FailedEvents, 0)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("error serializing one event", func(t *testing.T) {
|
||||
res := testClient.PublishEvents(ctx, "messages", "test", []interface{}{make(chan struct{}), "pong"})
|
||||
require.Error(t, res.Error)
|
||||
assert.Error(t, res.Error)
|
||||
assert.Len(t, res.FailedEvents, 1)
|
||||
assert.IsType(t, make(chan struct{}), res.FailedEvents[0])
|
||||
})
|
||||
|
||||
t.Run("with raw payload", func(t *testing.T) {
|
||||
res := testClient.PublishEvents(ctx, "messages", "test", []interface{}{"ping", "pong"}, PublishEventsWithRawPayload())
|
||||
require.NoError(t, res.Error)
|
||||
assert.Empty(t, res.FailedEvents)
|
||||
assert.Nil(t, res.Error)
|
||||
assert.Len(t, res.FailedEvents, 0)
|
||||
})
|
||||
|
||||
t.Run("with metadata", func(t *testing.T) {
|
||||
res := testClient.PublishEvents(ctx, "messages", "test", []interface{}{"ping", "pong"}, PublishEventsWithMetadata(map[string]string{"key": "value"}))
|
||||
require.NoError(t, res.Error)
|
||||
assert.Empty(t, res.FailedEvents)
|
||||
assert.Nil(t, res.Error)
|
||||
assert.Len(t, res.FailedEvents, 0)
|
||||
})
|
||||
|
||||
t.Run("with custom content type", func(t *testing.T) {
|
||||
res := testClient.PublishEvents(ctx, "messages", "test", []interface{}{"ping", "pong"}, PublishEventsWithContentType("text/plain"))
|
||||
require.NoError(t, res.Error)
|
||||
assert.Empty(t, res.FailedEvents)
|
||||
assert.Nil(t, res.Error)
|
||||
assert.Len(t, res.FailedEvents, 0)
|
||||
})
|
||||
|
||||
t.Run("with events that will fail some events", func(t *testing.T) {
|
||||
res := testClient.PublishEvents(ctx, "messages", "test", []interface{}{"ping", "pong", "fail-ping"})
|
||||
require.Error(t, res.Error)
|
||||
assert.Error(t, res.Error)
|
||||
assert.Len(t, res.FailedEvents, 1)
|
||||
assert.Contains(t, res.FailedEvents, "fail-ping")
|
||||
})
|
||||
|
||||
t.Run("with events that will fail the entire request", func(t *testing.T) {
|
||||
res := testClient.PublishEvents(ctx, "messages", "test", []interface{}{"ping", "pong", "failall-ping"})
|
||||
require.Error(t, res.Error)
|
||||
assert.Error(t, res.Error)
|
||||
assert.Len(t, res.FailedEvents, 3)
|
||||
assert.Contains(t, res.FailedEvents, "ping")
|
||||
assert.Contains(t, res.FailedEvents, "pong")
|
||||
|
@ -276,11 +275,11 @@ func TestCreateBulkPublishRequestEntry(t *testing.T) {
|
|||
t.Run(tc.name, func(t *testing.T) {
|
||||
entry, err := createBulkPublishRequestEntry(tc.data)
|
||||
if tc.expectedError {
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.expectedEvent, entry.GetEvent())
|
||||
assert.Equal(t, tc.expectedContentType, entry.GetContentType())
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, tc.expectedEvent, entry.Event)
|
||||
assert.Equal(t, tc.expectedContentType, entry.ContentType)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -293,9 +292,9 @@ func TestCreateBulkPublishRequestEntry(t *testing.T) {
|
|||
EntryID: "123",
|
||||
Metadata: map[string]string{"key": "value"},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "123", entry.GetEntryId())
|
||||
assert.Equal(t, map[string]string{"key": "value"}, entry.GetMetadata())
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "123", entry.EntryId)
|
||||
assert.Equal(t, map[string]string{"key": "value"}, entry.Metadata)
|
||||
})
|
||||
|
||||
t.Run("should set random uuid as entryID when not provided", func(t *testing.T) {
|
||||
|
@ -319,12 +318,12 @@ func TestCreateBulkPublishRequestEntry(t *testing.T) {
|
|||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
entry, err := createBulkPublishRequestEntry(tc.data)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, entry.GetEntryId())
|
||||
assert.Nil(t, entry.GetMetadata())
|
||||
assert.Nil(t, err)
|
||||
assert.NotEmpty(t, entry.EntryId)
|
||||
assert.Nil(t, entry.Metadata)
|
||||
|
||||
_, err = uuid.Parse(entry.GetEntryId())
|
||||
require.NoError(t, err)
|
||||
_, err = uuid.Parse(entry.EntryId)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
Copyright 2021 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
|
||||
)
|
||||
|
||||
type Job struct {
|
||||
Name string
|
||||
Schedule string // Optional
|
||||
Repeats uint32 // Optional
|
||||
DueTime string // Optional
|
||||
TTL string // Optional
|
||||
Data *anypb.Any
|
||||
}
|
||||
|
||||
// ScheduleJobAlpha1 raises and schedules a job.
|
||||
func (c *GRPCClient) ScheduleJobAlpha1(ctx context.Context, job *Job) error {
|
||||
// TODO: Assert job fields are defined: Name, Data
|
||||
jobRequest := &pb.Job{
|
||||
Name: job.Name,
|
||||
Data: job.Data,
|
||||
}
|
||||
|
||||
if job.Schedule != "" {
|
||||
jobRequest.Schedule = &job.Schedule
|
||||
}
|
||||
|
||||
if job.Repeats != 0 {
|
||||
jobRequest.Repeats = &job.Repeats
|
||||
}
|
||||
|
||||
if job.DueTime != "" {
|
||||
jobRequest.DueTime = &job.DueTime
|
||||
}
|
||||
|
||||
if job.TTL != "" {
|
||||
jobRequest.Ttl = &job.TTL
|
||||
}
|
||||
_, err := c.protoClient.ScheduleJobAlpha1(ctx, &pb.ScheduleJobRequest{
|
||||
Job: jobRequest,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// GetJobAlpha1 retrieves a scheduled job.
|
||||
func (c *GRPCClient) GetJobAlpha1(ctx context.Context, name string) (*Job, error) {
|
||||
// TODO: Name validation
|
||||
resp, err := c.protoClient.GetJobAlpha1(ctx, &pb.GetJobRequest{
|
||||
Name: name,
|
||||
})
|
||||
log.Println(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Job{
|
||||
Name: resp.GetJob().GetName(),
|
||||
Schedule: resp.GetJob().GetSchedule(),
|
||||
Repeats: resp.GetJob().GetRepeats(),
|
||||
DueTime: resp.GetJob().GetDueTime(),
|
||||
TTL: resp.GetJob().GetTtl(),
|
||||
Data: resp.GetJob().GetData(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteJobAlpha1 deletes a scheduled job.
|
||||
func (c *GRPCClient) DeleteJobAlpha1(ctx context.Context, name string) error {
|
||||
// TODO: Name validation
|
||||
_, err := c.protoClient.DeleteJobAlpha1(ctx, &pb.DeleteJobRequest{
|
||||
Name: name,
|
||||
})
|
||||
return err
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
Copyright 2021 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
func TestSchedulingAlpha1(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
t.Run("schedule job - valid", func(t *testing.T) {
|
||||
err := testClient.ScheduleJobAlpha1(ctx, &Job{
|
||||
Name: "test",
|
||||
Schedule: "test",
|
||||
Data: &anypb.Any{},
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("get job - valid", func(t *testing.T) {
|
||||
expected := &Job{
|
||||
Name: "name",
|
||||
Schedule: "@every 10s",
|
||||
Repeats: 4,
|
||||
DueTime: "10s",
|
||||
TTL: "10s",
|
||||
Data: nil,
|
||||
}
|
||||
|
||||
resp, err := testClient.GetJobAlpha1(ctx, "name")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expected, resp)
|
||||
})
|
||||
|
||||
t.Run("delete job - valid", func(t *testing.T) {
|
||||
err := testClient.DeleteJobAlpha1(ctx, "name")
|
||||
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
|
@ -36,7 +36,7 @@ func (c *GRPCClient) GetSecret(ctx context.Context, storeName, key string, meta
|
|||
Metadata: meta,
|
||||
}
|
||||
|
||||
resp, err := c.protoClient.GetSecret(ctx, req)
|
||||
resp, err := c.protoClient.GetSecret(c.withAuthToken(ctx), req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error invoking service: %w", err)
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ func (c *GRPCClient) GetBulkSecret(ctx context.Context, storeName string, meta m
|
|||
Metadata: meta,
|
||||
}
|
||||
|
||||
resp, err := c.protoClient.GetBulkSecret(ctx, req)
|
||||
resp, err := c.protoClient.GetBulkSecret(c.withAuthToken(ctx), req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error invoking service: %w", err)
|
||||
}
|
||||
|
@ -67,10 +67,10 @@ func (c *GRPCClient) GetBulkSecret(ctx context.Context, storeName string, meta m
|
|||
if resp != nil {
|
||||
data = map[string]map[string]string{}
|
||||
|
||||
for secretName, secretResponse := range resp.GetData() {
|
||||
for secretName, secretResponse := range resp.Data {
|
||||
data[secretName] = map[string]string{}
|
||||
|
||||
for k, v := range secretResponse.GetSecrets() {
|
||||
for k, v := range secretResponse.Secrets {
|
||||
data[secretName][k] = v
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,62 +14,61 @@ limitations under the License.
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// go test -timeout 30s ./client -count 1 -run ^TestGetSecret$
|
||||
func TestGetSecret(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("without store", func(t *testing.T) {
|
||||
out, err := testClient.GetSecret(ctx, "", "key1", nil)
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, out)
|
||||
})
|
||||
|
||||
t.Run("without key", func(t *testing.T) {
|
||||
out, err := testClient.GetSecret(ctx, "store", "", nil)
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, out)
|
||||
})
|
||||
|
||||
t.Run("without meta", func(t *testing.T) {
|
||||
out, err := testClient.GetSecret(ctx, "store", "key1", nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, out)
|
||||
})
|
||||
|
||||
t.Run("with meta", func(t *testing.T) {
|
||||
in := map[string]string{"k1": "v1", "k2": "v2"}
|
||||
out, err := testClient.GetSecret(ctx, "store", "key1", in)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, out)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetBulkSecret(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("without store", func(t *testing.T) {
|
||||
out, err := testClient.GetBulkSecret(ctx, "", nil)
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, out)
|
||||
})
|
||||
|
||||
t.Run("without meta", func(t *testing.T) {
|
||||
out, err := testClient.GetBulkSecret(ctx, "store", nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, out)
|
||||
})
|
||||
|
||||
t.Run("with meta", func(t *testing.T) {
|
||||
in := map[string]string{"k1": "v1", "k2": "v2"}
|
||||
out, err := testClient.GetBulkSecret(ctx, "store", in)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, out)
|
||||
})
|
||||
}
|
||||
|
|
107
client/state.go
107
client/state.go
|
@ -17,10 +17,9 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
"github.com/golang/protobuf/ptypes/duration"
|
||||
|
||||
v1 "github.com/dapr/dapr/pkg/proto/common/v1"
|
||||
pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
|
||||
|
@ -47,30 +46,17 @@ const (
|
|||
StateOperationTypeUpsert OperationType = 1
|
||||
// StateOperationTypeDelete represents delete operation type value.
|
||||
StateOperationTypeDelete OperationType = 2
|
||||
|
||||
// EventualType represents the eventual type value.
|
||||
EventualType = "eventual"
|
||||
// StrongType represents the strong type value.
|
||||
StrongType = "strong"
|
||||
// FirstWriteType represents the first write type value.
|
||||
FirstWriteType = "first-write"
|
||||
// LastWriteType represents the last write type value.
|
||||
LastWriteType = "last-write"
|
||||
// UpsertType represents the upsert type value.
|
||||
UpsertType = "upsert"
|
||||
// DeleteType represents the delete type value.
|
||||
DeleteType = "delete"
|
||||
// UndefinedType represents undefined type value.
|
||||
UndefinedType = "undefined"
|
||||
)
|
||||
|
||||
type (
|
||||
// StateConsistency is the consistency enum type.
|
||||
StateConsistency int32
|
||||
StateConsistency int
|
||||
// StateConcurrency is the concurrency enum type.
|
||||
StateConcurrency int32
|
||||
StateConcurrency int
|
||||
// OperationType is the operation enum type.
|
||||
OperationType int32
|
||||
OperationType int
|
||||
)
|
||||
|
||||
// GetPBConsistency get consistency pb value.
|
||||
|
@ -87,8 +73,8 @@ func (s StateConcurrency) GetPBConcurrency() v1.StateOptions_StateConcurrency {
|
|||
func (o OperationType) String() string {
|
||||
names := [...]string{
|
||||
UndefinedType,
|
||||
UpsertType,
|
||||
DeleteType,
|
||||
"upsert",
|
||||
"delete",
|
||||
}
|
||||
if o < StateOperationTypeUpsert || o > StateOperationTypeDelete {
|
||||
return UndefinedType
|
||||
|
@ -101,10 +87,10 @@ func (o OperationType) String() string {
|
|||
func (s StateConsistency) String() string {
|
||||
names := [...]string{
|
||||
UndefinedType,
|
||||
EventualType,
|
||||
StrongType,
|
||||
"strong",
|
||||
"eventual",
|
||||
}
|
||||
if s < StateConsistencyEventual || s > StateConsistencyStrong {
|
||||
if s < StateConsistencyStrong || s > StateConsistencyEventual {
|
||||
return UndefinedType
|
||||
}
|
||||
|
||||
|
@ -115,8 +101,8 @@ func (s StateConsistency) String() string {
|
|||
func (s StateConcurrency) String() string {
|
||||
names := [...]string{
|
||||
UndefinedType,
|
||||
FirstWriteType,
|
||||
LastWriteType,
|
||||
"first-write",
|
||||
"last-write",
|
||||
}
|
||||
if s < StateConcurrencyFirstWrite || s > StateConcurrencyLastWrite {
|
||||
return UndefinedType
|
||||
|
@ -249,19 +235,13 @@ func copyStateOptionDefault() *StateOptions {
|
|||
}
|
||||
}
|
||||
|
||||
func toProtoDuration(d time.Duration) *durationpb.Duration {
|
||||
func toProtoDuration(d time.Duration) *duration.Duration {
|
||||
nanos := d.Nanoseconds()
|
||||
secs := nanos / 1e9
|
||||
nanos -= secs * 1e9
|
||||
|
||||
// conversion check - gosec ignored below for conversion
|
||||
if nanos <= int64(math.MinInt32) && nanos >= int64(math.MaxInt32) {
|
||||
panic("integer overflow converting duration to proto")
|
||||
}
|
||||
|
||||
return &durationpb.Duration{
|
||||
return &duration.Duration{
|
||||
Seconds: secs,
|
||||
Nanos: int32(nanos), //nolint:gosec
|
||||
Nanos: int32(nanos),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,7 +268,7 @@ func (c *GRPCClient) ExecuteStateTransaction(ctx context.Context, storeName stri
|
|||
StoreName: storeName,
|
||||
Operations: items,
|
||||
}
|
||||
_, err := c.protoClient.ExecuteStateTransaction(ctx, req)
|
||||
_, err := c.protoClient.ExecuteStateTransaction(c.withAuthToken(ctx), req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error executing state transaction: %w", err)
|
||||
}
|
||||
|
@ -337,10 +317,10 @@ func (c *GRPCClient) SaveBulkState(ctx context.Context, storeName string, items
|
|||
|
||||
for _, si := range items {
|
||||
item := toProtoSaveStateItem(si)
|
||||
req.States = append(req.GetStates(), item)
|
||||
req.States = append(req.States, item)
|
||||
}
|
||||
|
||||
_, err := c.protoClient.SaveState(ctx, req)
|
||||
_, err := c.protoClient.SaveState(c.withAuthToken(ctx), req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error saving state: %w", err)
|
||||
}
|
||||
|
@ -364,22 +344,22 @@ func (c *GRPCClient) GetBulkState(ctx context.Context, storeName string, keys []
|
|||
Parallelism: parallelism,
|
||||
}
|
||||
|
||||
results, err := c.protoClient.GetBulkState(ctx, req)
|
||||
results, err := c.protoClient.GetBulkState(c.withAuthToken(ctx), req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting state: %w", err)
|
||||
}
|
||||
|
||||
if results == nil || results.GetItems() == nil {
|
||||
if results == nil || results.Items == nil {
|
||||
return items, nil
|
||||
}
|
||||
|
||||
for _, r := range results.GetItems() {
|
||||
for _, r := range results.Items {
|
||||
item := &BulkStateItem{
|
||||
Key: r.GetKey(),
|
||||
Etag: r.GetEtag(),
|
||||
Value: r.GetData(),
|
||||
Metadata: r.GetMetadata(),
|
||||
Error: r.GetError(),
|
||||
Key: r.Key,
|
||||
Etag: r.Etag,
|
||||
Value: r.Data,
|
||||
Metadata: r.Metadata,
|
||||
Error: r.Error,
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
|
@ -389,8 +369,7 @@ func (c *GRPCClient) GetBulkState(ctx context.Context, storeName string, keys []
|
|||
|
||||
// GetState retrieves state from specific store using default consistency option.
|
||||
func (c *GRPCClient) GetState(ctx context.Context, storeName, key string, meta map[string]string) (item *StateItem, err error) {
|
||||
i, err := c.GetStateWithConsistency(ctx, storeName, key, meta, StateConsistencyStrong)
|
||||
return i, err
|
||||
return c.GetStateWithConsistency(ctx, storeName, key, meta, StateConsistencyStrong)
|
||||
}
|
||||
|
||||
// GetStateWithConsistency retrieves state from specific store using provided state consistency.
|
||||
|
@ -406,16 +385,16 @@ func (c *GRPCClient) GetStateWithConsistency(ctx context.Context, storeName, key
|
|||
Metadata: meta,
|
||||
}
|
||||
|
||||
result, err := c.protoClient.GetState(ctx, req)
|
||||
result, err := c.protoClient.GetState(c.withAuthToken(ctx), req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting state: %w", err)
|
||||
}
|
||||
|
||||
return &StateItem{
|
||||
Etag: result.GetEtag(),
|
||||
Etag: result.Etag,
|
||||
Key: key,
|
||||
Value: result.GetData(),
|
||||
Metadata: result.GetMetadata(),
|
||||
Value: result.Data,
|
||||
Metadata: result.Metadata,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -432,21 +411,21 @@ func (c *GRPCClient) QueryStateAlpha1(ctx context.Context, storeName, query stri
|
|||
Query: query,
|
||||
Metadata: meta,
|
||||
}
|
||||
resp, err := c.protoClient.QueryStateAlpha1(ctx, req)
|
||||
resp, err := c.protoClient.QueryStateAlpha1(c.withAuthToken(ctx), req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error querying state: %w", err)
|
||||
}
|
||||
|
||||
ret := &QueryResponse{
|
||||
Results: make([]QueryItem, len(resp.GetResults())),
|
||||
Token: resp.GetToken(),
|
||||
Metadata: resp.GetMetadata(),
|
||||
Results: make([]QueryItem, len(resp.Results)),
|
||||
Token: resp.Token,
|
||||
Metadata: resp.Metadata,
|
||||
}
|
||||
for i, item := range resp.GetResults() {
|
||||
ret.Results[i].Key = item.GetKey()
|
||||
ret.Results[i].Value = item.GetData()
|
||||
ret.Results[i].Etag = item.GetEtag()
|
||||
ret.Results[i].Error = item.GetError()
|
||||
for i, item := range resp.Results {
|
||||
ret.Results[i].Key = item.Key
|
||||
ret.Results[i].Value = item.Data
|
||||
ret.Results[i].Etag = item.Etag
|
||||
ret.Results[i].Error = item.Error
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
|
@ -476,7 +455,7 @@ func (c *GRPCClient) DeleteStateWithETag(ctx context.Context, storeName, key str
|
|||
}
|
||||
}
|
||||
|
||||
_, err := c.protoClient.DeleteState(ctx, req)
|
||||
_, err := c.protoClient.DeleteState(c.withAuthToken(ctx), req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting state: %w", err)
|
||||
}
|
||||
|
@ -491,7 +470,7 @@ func (c *GRPCClient) DeleteBulkState(ctx context.Context, storeName string, keys
|
|||
}
|
||||
|
||||
items := make([]*DeleteStateItem, 0, len(keys))
|
||||
for i := range keys {
|
||||
for i := 0; i < len(keys); i++ {
|
||||
item := &DeleteStateItem{
|
||||
Key: keys[i],
|
||||
Metadata: meta,
|
||||
|
@ -509,7 +488,7 @@ func (c *GRPCClient) DeleteBulkStateItems(ctx context.Context, storeName string,
|
|||
}
|
||||
|
||||
states := make([]*v1.StateItem, 0, len(items))
|
||||
for i := range items {
|
||||
for i := 0; i < len(items); i++ {
|
||||
item := items[i]
|
||||
if err := hasRequiredStateArgs(storeName, item.Key); err != nil {
|
||||
return fmt.Errorf("missing required arguments: %w", err)
|
||||
|
@ -532,7 +511,7 @@ func (c *GRPCClient) DeleteBulkStateItems(ctx context.Context, storeName string,
|
|||
StoreName: storeName,
|
||||
States: states,
|
||||
}
|
||||
_, err := c.protoClient.DeleteBulkState(ctx, req)
|
||||
_, err := c.protoClient.DeleteBulkState(c.withAuthToken(ctx), req)
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -14,11 +14,10 @@ limitations under the License.
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
v1 "github.com/dapr/dapr/pkg/proto/common/v1"
|
||||
|
@ -30,37 +29,19 @@ const (
|
|||
)
|
||||
|
||||
func TestTypes(t *testing.T) {
|
||||
t.Run("test operation types", func(t *testing.T) {
|
||||
var a OperationType = -1
|
||||
assert.Equal(t, UndefinedType, a.String())
|
||||
a = 1
|
||||
assert.Equal(t, UpsertType, a.String())
|
||||
a = 2
|
||||
assert.Equal(t, DeleteType, a.String())
|
||||
})
|
||||
t.Run("test state concurrency type", func(t *testing.T) {
|
||||
var b StateConcurrency = -1
|
||||
assert.Equal(t, UndefinedType, b.String())
|
||||
b = 1
|
||||
assert.Equal(t, FirstWriteType, b.String())
|
||||
b = 2
|
||||
assert.Equal(t, LastWriteType, b.String())
|
||||
})
|
||||
t.Run("test state consistency type", func(t *testing.T) {
|
||||
var c StateConsistency = -1
|
||||
assert.Equal(t, UndefinedType, c.String())
|
||||
c = 1
|
||||
assert.Equal(t, EventualType, c.String())
|
||||
c = 2
|
||||
assert.Equal(t, StrongType, c.String())
|
||||
})
|
||||
var op OperationType = -1
|
||||
assert.Equal(t, UndefinedType, op.String())
|
||||
var c StateConcurrency = -1
|
||||
assert.Equal(t, UndefinedType, c.String())
|
||||
var d StateConsistency = -1
|
||||
assert.Equal(t, UndefinedType, d.String())
|
||||
}
|
||||
|
||||
func TestDurationConverter(t *testing.T) {
|
||||
d := 10 * time.Second
|
||||
pd := toProtoDuration(d)
|
||||
assert.NotNil(t, pd)
|
||||
assert.Equal(t, int64(10), pd.GetSeconds())
|
||||
assert.Equal(t, pd.Seconds, int64(10))
|
||||
}
|
||||
|
||||
func TestStateOptionsConverter(t *testing.T) {
|
||||
|
@ -70,25 +51,25 @@ func TestStateOptionsConverter(t *testing.T) {
|
|||
}
|
||||
p := toProtoStateOptions(s)
|
||||
assert.NotNil(t, p)
|
||||
assert.Equal(t, v1.StateOptions_CONCURRENCY_LAST_WRITE, p.GetConcurrency())
|
||||
assert.Equal(t, v1.StateOptions_CONSISTENCY_STRONG, p.GetConsistency())
|
||||
assert.Equal(t, p.Concurrency, v1.StateOptions_CONCURRENCY_LAST_WRITE)
|
||||
assert.Equal(t, p.Consistency, v1.StateOptions_CONSISTENCY_STRONG)
|
||||
}
|
||||
|
||||
// go test -timeout 30s ./client -count 1 -run ^TestSaveState$
|
||||
func TestSaveState(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
data := testData
|
||||
store := testStore
|
||||
key := "key1"
|
||||
|
||||
t.Run("save data", func(t *testing.T) {
|
||||
err := testClient.SaveState(ctx, store, key, []byte(data), nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("get saved data", func(t *testing.T) {
|
||||
item, err := testClient.GetState(ctx, store, key, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, item)
|
||||
assert.NotEmpty(t, item.Etag)
|
||||
assert.Equal(t, item.Key, key)
|
||||
|
@ -97,7 +78,7 @@ func TestSaveState(t *testing.T) {
|
|||
|
||||
t.Run("get saved data with consistency", func(t *testing.T) {
|
||||
item, err := testClient.GetStateWithConsistency(ctx, store, key, nil, StateConsistencyStrong)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, item)
|
||||
assert.NotEmpty(t, item.Etag)
|
||||
assert.Equal(t, item.Key, key)
|
||||
|
@ -106,39 +87,39 @@ func TestSaveState(t *testing.T) {
|
|||
|
||||
t.Run("save data with version", func(t *testing.T) {
|
||||
err := testClient.SaveStateWithETag(ctx, store, key, []byte(data), "1", nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("delete data", func(t *testing.T) {
|
||||
err := testClient.DeleteState(ctx, store, key, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
// go test -timeout 30s ./client -count 1 -run ^TestDeleteState$
|
||||
func TestDeleteState(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
data := testData
|
||||
store := testStore
|
||||
key := "key1"
|
||||
|
||||
t.Run("delete not exist data", func(t *testing.T) {
|
||||
err := testClient.DeleteState(ctx, store, key, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
t.Run("delete not exist data with etag and meta", func(t *testing.T) {
|
||||
err := testClient.DeleteStateWithETag(ctx, store, key, &ETag{Value: "100"}, map[string]string{"meta1": "value1"},
|
||||
&StateOptions{Concurrency: StateConcurrencyFirstWrite, Consistency: StateConsistencyEventual})
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("save data", func(t *testing.T) {
|
||||
err := testClient.SaveState(ctx, store, key, []byte(data), nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
t.Run("confirm data saved", func(t *testing.T) {
|
||||
item, err := testClient.GetState(ctx, store, key, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, item)
|
||||
assert.NotEmpty(t, item.Etag)
|
||||
assert.Equal(t, item.Key, key)
|
||||
|
@ -147,11 +128,11 @@ func TestDeleteState(t *testing.T) {
|
|||
|
||||
t.Run("delete exist data", func(t *testing.T) {
|
||||
err := testClient.DeleteState(ctx, store, key, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
t.Run("confirm data deleted", func(t *testing.T) {
|
||||
item, err := testClient.GetState(ctx, store, key, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, item)
|
||||
assert.NotEmpty(t, item.Etag)
|
||||
assert.Equal(t, item.Key, key)
|
||||
|
@ -161,11 +142,11 @@ func TestDeleteState(t *testing.T) {
|
|||
t.Run("save data again with etag, meta", func(t *testing.T) {
|
||||
meta := map[string]string{"meta1": "value1"}
|
||||
err := testClient.SaveStateWithETag(ctx, store, key, []byte(data), "1", meta, WithConsistency(StateConsistencyEventual), WithConcurrency(StateConcurrencyFirstWrite))
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
t.Run("confirm data saved", func(t *testing.T) {
|
||||
item, err := testClient.GetStateWithConsistency(ctx, store, key, map[string]string{"meta1": "value1"}, StateConsistencyEventual)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, item)
|
||||
assert.NotEmpty(t, item.Etag)
|
||||
assert.Equal(t, item.Key, key)
|
||||
|
@ -175,11 +156,11 @@ func TestDeleteState(t *testing.T) {
|
|||
t.Run("delete exist data with etag and meta", func(t *testing.T) {
|
||||
err := testClient.DeleteStateWithETag(ctx, store, key, &ETag{Value: "100"}, map[string]string{"meta1": "value1"},
|
||||
&StateOptions{Concurrency: StateConcurrencyFirstWrite, Consistency: StateConsistencyEventual})
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
t.Run("confirm data deleted", func(t *testing.T) {
|
||||
item, err := testClient.GetStateWithConsistency(ctx, store, key, map[string]string{"meta1": "value1"}, StateConsistencyEventual)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, item)
|
||||
assert.NotEmpty(t, item.Etag)
|
||||
assert.Equal(t, item.Key, key)
|
||||
|
@ -188,14 +169,14 @@ func TestDeleteState(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDeleteBulkState(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
data := testData
|
||||
store := testStore
|
||||
keys := []string{"key1", "key2", "key3"}
|
||||
|
||||
t.Run("delete not exist data", func(t *testing.T) {
|
||||
err := testClient.DeleteBulkState(ctx, store, keys, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("delete not exist data with stateIem", func(t *testing.T) {
|
||||
|
@ -211,48 +192,7 @@ func TestDeleteBulkState(t *testing.T) {
|
|||
})
|
||||
}
|
||||
err := testClient.DeleteBulkStateItems(ctx, store, items)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("delete bulk state item (empty) store", func(t *testing.T) { // save data
|
||||
// save data
|
||||
items := make([]*SetStateItem, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
items = append(items, &SetStateItem{
|
||||
Key: key,
|
||||
Value: []byte(data),
|
||||
Metadata: map[string]string{},
|
||||
Etag: &ETag{Value: "1"},
|
||||
Options: &StateOptions{
|
||||
Concurrency: StateConcurrencyFirstWrite,
|
||||
Consistency: StateConsistencyEventual,
|
||||
},
|
||||
})
|
||||
}
|
||||
err := testClient.SaveBulkState(ctx, store, items...)
|
||||
require.NoError(t, err)
|
||||
|
||||
// confirm data saved
|
||||
getItems, err := testClient.GetBulkState(ctx, store, keys, nil, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(keys), len(getItems))
|
||||
|
||||
// delete
|
||||
deleteItems := make([]*DeleteStateItem, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
deleteItems = append(deleteItems, &DeleteStateItem{
|
||||
Key: key,
|
||||
Metadata: map[string]string{},
|
||||
Etag: &ETag{Value: "1"},
|
||||
Options: &StateOptions{
|
||||
Concurrency: StateConcurrencyFirstWrite,
|
||||
Consistency: StateConsistencyEventual,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
err = testClient.DeleteBulkStateItems(ctx, "", deleteItems)
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("delete exist data", func(t *testing.T) {
|
||||
|
@ -271,21 +211,21 @@ func TestDeleteBulkState(t *testing.T) {
|
|||
})
|
||||
}
|
||||
err := testClient.SaveBulkState(ctx, store, items...)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// confirm data saved
|
||||
getItems, err := testClient.GetBulkState(ctx, store, keys, nil, 1)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(keys), len(getItems))
|
||||
|
||||
// delete
|
||||
err = testClient.DeleteBulkState(ctx, store, keys, nil)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// confirm data deleted
|
||||
getItems, err = testClient.GetBulkState(ctx, store, keys, nil, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, getItems)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(getItems))
|
||||
})
|
||||
|
||||
t.Run("delete exist data with stateItem", func(t *testing.T) {
|
||||
|
@ -304,11 +244,11 @@ func TestDeleteBulkState(t *testing.T) {
|
|||
})
|
||||
}
|
||||
err := testClient.SaveBulkState(ctx, store, items...)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// confirm data saved
|
||||
getItems, err := testClient.GetBulkState(ctx, store, keys, nil, 1)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(keys), len(getItems))
|
||||
|
||||
// delete
|
||||
|
@ -325,18 +265,18 @@ func TestDeleteBulkState(t *testing.T) {
|
|||
})
|
||||
}
|
||||
err = testClient.DeleteBulkStateItems(ctx, store, deleteItems)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// confirm data deleted
|
||||
getItems, err = testClient.GetBulkState(ctx, store, keys, nil, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, getItems)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(getItems))
|
||||
})
|
||||
}
|
||||
|
||||
// go test -timeout 30s ./client -count 1 -run ^TestStateTransactions$
|
||||
func TestStateTransactions(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
data := `{ "message": "test" }`
|
||||
store := testStore
|
||||
meta := map[string]string{}
|
||||
|
@ -356,16 +296,16 @@ func TestStateTransactions(t *testing.T) {
|
|||
|
||||
t.Run("exec inserts", func(t *testing.T) {
|
||||
err := testClient.ExecuteStateTransaction(ctx, store, meta, adds)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("exec upserts", func(t *testing.T) {
|
||||
items, err := testClient.GetBulkState(ctx, store, keys, nil, 10)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, items)
|
||||
assert.Len(t, items, len(keys))
|
||||
|
||||
upserts := make([]*StateOperation, 0)
|
||||
upsers := make([]*StateOperation, 0)
|
||||
for _, item := range items {
|
||||
op := &StateOperation{
|
||||
Type: StateOperationTypeUpsert,
|
||||
|
@ -377,15 +317,15 @@ func TestStateTransactions(t *testing.T) {
|
|||
Value: item.Value,
|
||||
},
|
||||
}
|
||||
upserts = append(upserts, op)
|
||||
upsers = append(upsers, op)
|
||||
}
|
||||
err = testClient.ExecuteStateTransaction(ctx, store, meta, upserts)
|
||||
require.NoError(t, err)
|
||||
err = testClient.ExecuteStateTransaction(ctx, store, meta, upsers)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("get and validate inserts", func(t *testing.T) {
|
||||
items, err := testClient.GetBulkState(ctx, store, keys, nil, 10)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, items)
|
||||
assert.Len(t, items, len(keys))
|
||||
assert.Equal(t, data, string(items[0].Value))
|
||||
|
@ -397,19 +337,19 @@ func TestStateTransactions(t *testing.T) {
|
|||
|
||||
t.Run("exec deletes", func(t *testing.T) {
|
||||
err := testClient.ExecuteStateTransaction(ctx, store, meta, adds)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("ensure deletes", func(t *testing.T) {
|
||||
items, err := testClient.GetBulkState(ctx, store, keys, nil, 3)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, items)
|
||||
assert.Empty(t, items)
|
||||
assert.Len(t, items, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestQueryState(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
data := testData
|
||||
store := testStore
|
||||
key1 := "key1"
|
||||
|
@ -417,39 +357,28 @@ func TestQueryState(t *testing.T) {
|
|||
|
||||
t.Run("save data", func(t *testing.T) {
|
||||
err := testClient.SaveState(ctx, store, key1, []byte(data), nil)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
err = testClient.SaveState(ctx, store, key2, []byte(data), nil)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("error query", func(t *testing.T) {
|
||||
_, err := testClient.QueryStateAlpha1(ctx, "", "", nil)
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
_, err = testClient.QueryStateAlpha1(ctx, store, "", nil)
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
_, err = testClient.QueryStateAlpha1(ctx, store, "bad syntax", nil)
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("query data", func(t *testing.T) {
|
||||
query := `{}`
|
||||
resp, err := testClient.QueryStateAlpha1(ctx, store, query, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, resp.Results, 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(resp.Results))
|
||||
for _, item := range resp.Results {
|
||||
assert.True(t, item.Key == key1 || item.Key == key2)
|
||||
assert.Equal(t, []byte(data), item.Value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestHasRequiredStateArgs(t *testing.T) {
|
||||
t.Run("empty store should error", func(t *testing.T) {
|
||||
err := hasRequiredStateArgs("", "key")
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("empty key should error", func(t *testing.T) {
|
||||
err := hasRequiredStateArgs("storeName", "")
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,299 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"mime"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
|
||||
"github.com/dapr/go-sdk/service/common"
|
||||
)
|
||||
|
||||
type SubscriptionHandleFunction func(event *common.TopicEvent) common.SubscriptionResponseStatus
|
||||
|
||||
type SubscriptionOptions struct {
|
||||
PubsubName string
|
||||
Topic string
|
||||
DeadLetterTopic *string
|
||||
Metadata map[string]string
|
||||
}
|
||||
|
||||
type Subscription struct {
|
||||
ctx context.Context
|
||||
stream pb.Dapr_SubscribeTopicEventsAlpha1Client
|
||||
|
||||
// lock locks concurrent writes to subscription stream.
|
||||
lock sync.Mutex
|
||||
closed atomic.Bool
|
||||
|
||||
createStream func(ctx context.Context, opts SubscriptionOptions) (pb.Dapr_SubscribeTopicEventsAlpha1Client, error)
|
||||
opts SubscriptionOptions
|
||||
}
|
||||
|
||||
type SubscriptionMessage struct {
|
||||
*common.TopicEvent
|
||||
sub *Subscription
|
||||
}
|
||||
|
||||
func (c *GRPCClient) Subscribe(ctx context.Context, opts SubscriptionOptions) (*Subscription, error) {
|
||||
stream, err := c.subscribeInitialRequest(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := &Subscription{
|
||||
ctx: ctx,
|
||||
stream: stream,
|
||||
createStream: c.subscribeInitialRequest,
|
||||
opts: opts,
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (c *GRPCClient) SubscribeWithHandler(ctx context.Context, opts SubscriptionOptions, handler SubscriptionHandleFunction) (func() error, error) {
|
||||
s, err := c.Subscribe(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer s.Close()
|
||||
|
||||
for {
|
||||
msg, err := s.Receive()
|
||||
if err != nil {
|
||||
if !s.closed.Load() {
|
||||
logger.Printf("Error receiving messages from subscription pubsub=%s topic=%s, closing subscription: %s",
|
||||
opts.PubsubName, opts.Topic, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := msg.respondStatus(handler(msg.TopicEvent)); err != nil {
|
||||
logger.Printf("Error responding to topic with event status pubsub=%s topic=%s message_id=%s: %s",
|
||||
opts.PubsubName, opts.Topic, msg.ID, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}()
|
||||
|
||||
return s.Close, nil
|
||||
}
|
||||
|
||||
func (s *Subscription) Close() error {
|
||||
if !s.closed.CompareAndSwap(false, true) {
|
||||
return errors.New("subscription already closed")
|
||||
}
|
||||
|
||||
return s.stream.CloseSend()
|
||||
}
|
||||
|
||||
func (s *Subscription) Receive() (*SubscriptionMessage, error) {
|
||||
for {
|
||||
resp, err := s.stream.Recv()
|
||||
if err != nil {
|
||||
select {
|
||||
case <-s.ctx.Done():
|
||||
return nil, errors.New("subscription context closed")
|
||||
default:
|
||||
// proceed to check the gRPC status error
|
||||
}
|
||||
|
||||
st, ok := status.FromError(err)
|
||||
if !ok {
|
||||
// not a grpc status error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch st.Code() {
|
||||
case codes.Unavailable, codes.Unknown:
|
||||
logger.Printf("gRPC error while reading from stream: %s (code=%v)",
|
||||
st.Message(), st.Code())
|
||||
// close the current stream and reconnect
|
||||
if s.closed.Load() {
|
||||
return nil, errors.New("subscription is permanently closed; cannot reconnect")
|
||||
}
|
||||
if err := s.closeStreamOnly(); err != nil {
|
||||
logger.Printf("error closing current stream: %v", err)
|
||||
}
|
||||
|
||||
newStream, nerr := s.createStream(s.ctx, s.opts)
|
||||
if nerr != nil {
|
||||
return nil, errors.New("re-subscribe failed")
|
||||
}
|
||||
|
||||
s.lock.Lock()
|
||||
s.stream = newStream
|
||||
s.lock.Unlock()
|
||||
|
||||
// try receiving again
|
||||
continue
|
||||
|
||||
case codes.Canceled:
|
||||
return nil, errors.New("stream canceled")
|
||||
|
||||
default:
|
||||
return nil, errors.New("subscription recv error")
|
||||
}
|
||||
}
|
||||
|
||||
event := resp.GetEventMessage()
|
||||
data := any(event.GetData())
|
||||
if len(event.GetData()) > 0 {
|
||||
mediaType, _, err := mime.ParseMediaType(event.GetDataContentType())
|
||||
if err == nil {
|
||||
var v interface{}
|
||||
switch mediaType {
|
||||
case "application/json":
|
||||
if err := json.Unmarshal(event.GetData(), &v); err == nil {
|
||||
data = v
|
||||
}
|
||||
case "text/plain":
|
||||
// Assume UTF-8 encoded string.
|
||||
data = string(event.GetData())
|
||||
default:
|
||||
if strings.HasPrefix(mediaType, "application/") &&
|
||||
strings.HasSuffix(mediaType, "+json") {
|
||||
if err := json.Unmarshal(event.GetData(), &v); err == nil {
|
||||
data = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
topicEvent := &common.TopicEvent{
|
||||
ID: event.GetId(),
|
||||
Source: event.GetSource(),
|
||||
Type: event.GetType(),
|
||||
SpecVersion: event.GetSpecVersion(),
|
||||
DataContentType: event.GetDataContentType(),
|
||||
Data: data,
|
||||
RawData: event.GetData(),
|
||||
Topic: event.GetTopic(),
|
||||
PubsubName: event.GetPubsubName(),
|
||||
}
|
||||
|
||||
return &SubscriptionMessage{
|
||||
sub: s,
|
||||
TopicEvent: topicEvent,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SubscriptionMessage) Success() error {
|
||||
return s.respond(pb.TopicEventResponse_SUCCESS)
|
||||
}
|
||||
|
||||
func (s *SubscriptionMessage) Retry() error {
|
||||
return s.respond(pb.TopicEventResponse_RETRY)
|
||||
}
|
||||
|
||||
func (s *SubscriptionMessage) Drop() error {
|
||||
return s.respond(pb.TopicEventResponse_DROP)
|
||||
}
|
||||
|
||||
func (s *SubscriptionMessage) respondStatus(status common.SubscriptionResponseStatus) error {
|
||||
var statuspb pb.TopicEventResponse_TopicEventResponseStatus
|
||||
switch status {
|
||||
case common.SubscriptionResponseStatusSuccess:
|
||||
statuspb = pb.TopicEventResponse_SUCCESS
|
||||
case common.SubscriptionResponseStatusRetry:
|
||||
statuspb = pb.TopicEventResponse_RETRY
|
||||
case common.SubscriptionResponseStatusDrop:
|
||||
statuspb = pb.TopicEventResponse_DROP
|
||||
default:
|
||||
return fmt.Errorf("unknown status, expected one of %s, %s, %s: %s",
|
||||
common.SubscriptionResponseStatusSuccess, common.SubscriptionResponseStatusRetry,
|
||||
common.SubscriptionResponseStatusDrop, status)
|
||||
}
|
||||
|
||||
return s.respond(statuspb)
|
||||
}
|
||||
|
||||
func (s *SubscriptionMessage) respond(status pb.TopicEventResponse_TopicEventResponseStatus) error {
|
||||
s.sub.lock.Lock()
|
||||
defer s.sub.lock.Unlock()
|
||||
|
||||
return s.sub.stream.Send(&pb.SubscribeTopicEventsRequestAlpha1{
|
||||
SubscribeTopicEventsRequestType: &pb.SubscribeTopicEventsRequestAlpha1_EventProcessed{
|
||||
EventProcessed: &pb.SubscribeTopicEventsRequestProcessedAlpha1{
|
||||
Id: s.ID,
|
||||
Status: &pb.TopicEventResponse{Status: status},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (c *GRPCClient) subscribeInitialRequest(ctx context.Context, opts SubscriptionOptions) (pb.Dapr_SubscribeTopicEventsAlpha1Client, error) {
|
||||
if len(opts.PubsubName) == 0 {
|
||||
return nil, errors.New("pubsub name required")
|
||||
}
|
||||
|
||||
if len(opts.Topic) == 0 {
|
||||
return nil, errors.New("topic required")
|
||||
}
|
||||
|
||||
stream, err := c.protoClient.SubscribeTopicEventsAlpha1(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = stream.Send(&pb.SubscribeTopicEventsRequestAlpha1{
|
||||
SubscribeTopicEventsRequestType: &pb.SubscribeTopicEventsRequestAlpha1_InitialRequest{
|
||||
InitialRequest: &pb.SubscribeTopicEventsRequestInitialAlpha1{
|
||||
PubsubName: opts.PubsubName, Topic: opts.Topic,
|
||||
Metadata: opts.Metadata, DeadLetterTopic: opts.DeadLetterTopic,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Join(err, stream.CloseSend())
|
||||
}
|
||||
|
||||
resp, err := stream.Recv()
|
||||
if err != nil {
|
||||
return nil, errors.Join(err, stream.CloseSend())
|
||||
}
|
||||
|
||||
switch resp.GetSubscribeTopicEventsResponseType().(type) {
|
||||
case *pb.SubscribeTopicEventsResponseAlpha1_InitialResponse:
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected initial response from server : %v", resp)
|
||||
}
|
||||
|
||||
return stream, nil
|
||||
}
|
||||
|
||||
func (s *Subscription) closeStreamOnly() error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if s.stream != nil {
|
||||
return s.stream.CloseSend()
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -21,8 +21,6 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
@ -103,7 +101,7 @@ func createUnresponsiveServer(network string, unresponsiveServerAddress string)
|
|||
}
|
||||
|
||||
func createNonBlockingClient(ctx context.Context, serverAddr string) (client Client, err error) {
|
||||
conn, err := grpc.DialContext( //nolint:staticcheck
|
||||
conn, err := grpc.DialContext(
|
||||
ctx,
|
||||
serverAddr,
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
|
@ -116,44 +114,44 @@ func createNonBlockingClient(ctx context.Context, serverAddr string) (client Cli
|
|||
}
|
||||
|
||||
func TestGrpcWaitHappyCase(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
|
||||
err := testClient.Wait(ctx, waitTimeout)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGrpcWaitUnresponsiveTcpServer(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
|
||||
server, err := createUnresponsiveTCPServer()
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
defer server.Close()
|
||||
|
||||
clientConnectionTimeoutCtx, cancel := context.WithTimeout(ctx, connectionTimeout)
|
||||
defer cancel()
|
||||
client, err := createNonBlockingClient(clientConnectionTimeoutCtx, server.address)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = client.Wait(ctx, waitTimeout)
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, errWaitTimedOut, err)
|
||||
assert.GreaterOrEqual(t, atomic.LoadUint64(&server.nClientsSeen), uint64(1))
|
||||
}
|
||||
|
||||
func TestGrpcWaitUnresponsiveUnixServer(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
ctx := context.Background()
|
||||
|
||||
server, err := createUnresponsiveUnixServer()
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
defer server.Close()
|
||||
|
||||
clientConnectionTimeoutCtx, cancel := context.WithTimeout(ctx, connectionTimeout)
|
||||
defer cancel()
|
||||
client, err := createNonBlockingClient(clientConnectionTimeoutCtx, "unix://"+server.address)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = client.Wait(ctx, waitTimeout)
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, errWaitTimedOut, err)
|
||||
assert.GreaterOrEqual(t, atomic.LoadUint64(&server.nClientsSeen), uint64(1))
|
||||
}
|
||||
|
|
|
@ -1,268 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultWorkflowComponent = "dapr"
|
||||
)
|
||||
|
||||
type StartWorkflowRequest struct {
|
||||
InstanceID string // Optional instance identifier
|
||||
WorkflowComponent string
|
||||
WorkflowName string
|
||||
Options map[string]string // Optional metadata
|
||||
Input any // Optional input
|
||||
SendRawInput bool // Set to True in order to disable serialization on the input
|
||||
}
|
||||
|
||||
type StartWorkflowResponse struct {
|
||||
InstanceID string
|
||||
}
|
||||
|
||||
type GetWorkflowRequest struct {
|
||||
InstanceID string
|
||||
WorkflowComponent string
|
||||
}
|
||||
|
||||
type GetWorkflowResponse struct {
|
||||
InstanceID string
|
||||
WorkflowName string
|
||||
CreatedAt time.Time
|
||||
LastUpdatedAt time.Time
|
||||
RuntimeStatus string
|
||||
Properties map[string]string
|
||||
}
|
||||
|
||||
type PurgeWorkflowRequest struct {
|
||||
InstanceID string
|
||||
WorkflowComponent string
|
||||
}
|
||||
|
||||
type TerminateWorkflowRequest struct {
|
||||
InstanceID string
|
||||
WorkflowComponent string
|
||||
}
|
||||
|
||||
type PauseWorkflowRequest struct {
|
||||
InstanceID string
|
||||
WorkflowComponent string
|
||||
}
|
||||
|
||||
type ResumeWorkflowRequest struct {
|
||||
InstanceID string
|
||||
WorkflowComponent string
|
||||
}
|
||||
|
||||
type RaiseEventWorkflowRequest struct {
|
||||
InstanceID string
|
||||
WorkflowComponent string
|
||||
EventName string
|
||||
EventData any
|
||||
SendRawData bool // Set to True in order to disable serialization on the data
|
||||
}
|
||||
|
||||
// StartWorkflowBeta1 starts a workflow using the beta1 spec.
|
||||
func (c *GRPCClient) StartWorkflowBeta1(ctx context.Context, req *StartWorkflowRequest) (*StartWorkflowResponse, error) {
|
||||
if req.InstanceID == "" {
|
||||
req.InstanceID = uuid.New().String()
|
||||
}
|
||||
if req.WorkflowComponent == "" {
|
||||
req.WorkflowComponent = DefaultWorkflowComponent
|
||||
}
|
||||
if req.WorkflowName == "" {
|
||||
return nil, errors.New("failed to start workflow: WorkflowName must be supplied")
|
||||
}
|
||||
|
||||
var input []byte
|
||||
var err error
|
||||
if req.SendRawInput {
|
||||
var ok bool
|
||||
if input, ok = req.Input.([]byte); !ok {
|
||||
return nil, errors.New("failed to start workflow: sendrawinput is true however, input is not a byte slice")
|
||||
}
|
||||
} else {
|
||||
input, err = marshalInput(req.Input)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to start workflow: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := c.protoClient.StartWorkflowBeta1(ctx, &pb.StartWorkflowRequest{
|
||||
InstanceId: req.InstanceID,
|
||||
WorkflowComponent: req.WorkflowComponent,
|
||||
WorkflowName: req.WorkflowName,
|
||||
Options: req.Options,
|
||||
Input: input,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to start workflow instance: %v", err)
|
||||
}
|
||||
return &StartWorkflowResponse{
|
||||
InstanceID: resp.GetInstanceId(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetWorkflowBeta1 gets the status of a workflow using the beta1 spec.
|
||||
func (c *GRPCClient) GetWorkflowBeta1(ctx context.Context, req *GetWorkflowRequest) (*GetWorkflowResponse, error) {
|
||||
if req.InstanceID == "" {
|
||||
return nil, errors.New("failed to get workflow status: InstanceID must be supplied")
|
||||
}
|
||||
if req.WorkflowComponent == "" {
|
||||
req.WorkflowComponent = DefaultWorkflowComponent
|
||||
}
|
||||
resp, err := c.protoClient.GetWorkflowBeta1(ctx, &pb.GetWorkflowRequest{
|
||||
InstanceId: req.InstanceID,
|
||||
WorkflowComponent: req.WorkflowComponent,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get workflow status: %v", err)
|
||||
}
|
||||
return &GetWorkflowResponse{
|
||||
InstanceID: resp.GetInstanceId(),
|
||||
WorkflowName: resp.GetWorkflowName(),
|
||||
CreatedAt: resp.GetCreatedAt().AsTime(),
|
||||
LastUpdatedAt: resp.GetLastUpdatedAt().AsTime(),
|
||||
RuntimeStatus: resp.GetRuntimeStatus(),
|
||||
Properties: resp.GetProperties(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PurgeWorkflowBeta1 removes all metadata relating to a specific workflow using the beta1 spec.
|
||||
func (c *GRPCClient) PurgeWorkflowBeta1(ctx context.Context, req *PurgeWorkflowRequest) error {
|
||||
if req.InstanceID == "" {
|
||||
return errors.New("failed to purge workflow: InstanceID must be supplied")
|
||||
}
|
||||
if req.WorkflowComponent == "" {
|
||||
req.WorkflowComponent = DefaultWorkflowComponent
|
||||
}
|
||||
_, err := c.protoClient.PurgeWorkflowBeta1(ctx, &pb.PurgeWorkflowRequest{
|
||||
InstanceId: req.InstanceID,
|
||||
WorkflowComponent: req.WorkflowComponent,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to purge workflow: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TerminateWorkflowBeta1 stops a workflow using the beta1 spec.
|
||||
func (c *GRPCClient) TerminateWorkflowBeta1(ctx context.Context, req *TerminateWorkflowRequest) error {
|
||||
if req.InstanceID == "" {
|
||||
return errors.New("failed to terminate workflow: InstanceID must be supplied")
|
||||
}
|
||||
if req.WorkflowComponent == "" {
|
||||
req.WorkflowComponent = DefaultWorkflowComponent
|
||||
}
|
||||
_, err := c.protoClient.TerminateWorkflowBeta1(ctx, &pb.TerminateWorkflowRequest{
|
||||
InstanceId: req.InstanceID,
|
||||
WorkflowComponent: req.WorkflowComponent,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to terminate workflow: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PauseWorkflowBeta1 pauses a workflow that can be resumed later using the beta1 spec.
|
||||
func (c *GRPCClient) PauseWorkflowBeta1(ctx context.Context, req *PauseWorkflowRequest) error {
|
||||
if req.InstanceID == "" {
|
||||
return errors.New("failed to pause workflow: InstanceID must be supplied")
|
||||
}
|
||||
if req.WorkflowComponent == "" {
|
||||
req.WorkflowComponent = DefaultWorkflowComponent
|
||||
}
|
||||
_, err := c.protoClient.PauseWorkflowBeta1(ctx, &pb.PauseWorkflowRequest{
|
||||
InstanceId: req.InstanceID,
|
||||
WorkflowComponent: req.WorkflowComponent,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to pause workflow: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResumeWorkflowBeta1 resumes a paused workflow using the beta1 spec.
|
||||
func (c *GRPCClient) ResumeWorkflowBeta1(ctx context.Context, req *ResumeWorkflowRequest) error {
|
||||
if req.InstanceID == "" {
|
||||
return errors.New("failed to resume workflow: InstanceID must be supplied")
|
||||
}
|
||||
if req.WorkflowComponent == "" {
|
||||
req.WorkflowComponent = DefaultWorkflowComponent
|
||||
}
|
||||
_, err := c.protoClient.ResumeWorkflowBeta1(ctx, &pb.ResumeWorkflowRequest{
|
||||
InstanceId: req.InstanceID,
|
||||
WorkflowComponent: req.WorkflowComponent,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to resume workflow: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RaiseEventWorkflowBeta1 raises an event on a workflow using the beta1 spec.
|
||||
func (c *GRPCClient) RaiseEventWorkflowBeta1(ctx context.Context, req *RaiseEventWorkflowRequest) error {
|
||||
if req.InstanceID == "" {
|
||||
return errors.New("failed to raise event on workflow: InstanceID must be supplied")
|
||||
}
|
||||
if req.WorkflowComponent == "" {
|
||||
req.WorkflowComponent = DefaultWorkflowComponent
|
||||
}
|
||||
if req.EventName == "" {
|
||||
return errors.New("failed to raise event on workflow: EventName must be supplied")
|
||||
}
|
||||
var eventData []byte
|
||||
var err error
|
||||
if req.SendRawData {
|
||||
var ok bool
|
||||
if eventData, ok = req.EventData.([]byte); !ok {
|
||||
return errors.New("failed to raise event on workflow: SendRawData is true however, eventData is not a byte slice")
|
||||
}
|
||||
} else {
|
||||
eventData, err = marshalInput(req.EventData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to raise an event on workflow: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = c.protoClient.RaiseEventWorkflowBeta1(ctx, &pb.RaiseEventWorkflowRequest{
|
||||
InstanceId: req.InstanceID,
|
||||
WorkflowComponent: req.WorkflowComponent,
|
||||
EventName: req.EventName,
|
||||
EventData: eventData,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to raise event on workflow: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func marshalInput(input any) (data []byte, err error) {
|
||||
if input == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return json.Marshal(input)
|
||||
}
|
|
@ -1,374 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package client
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMarshalInput(t *testing.T) {
|
||||
var input any
|
||||
t.Run("string", func(t *testing.T) {
|
||||
input = "testString"
|
||||
data, err := marshalInput(input)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte{0x22, 0x74, 0x65, 0x73, 0x74, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22}, data)
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkflowBeta1(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
// 1: StartWorkflow
|
||||
t.Run("start workflow - valid (without id)", func(t *testing.T) {
|
||||
resp, err := testClient.StartWorkflowBeta1(ctx, &StartWorkflowRequest{
|
||||
InstanceID: "",
|
||||
WorkflowComponent: "dapr",
|
||||
WorkflowName: "TestWorkflow",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp.InstanceID)
|
||||
})
|
||||
t.Run("start workflow - valid (with id)", func(t *testing.T) {
|
||||
resp, err := testClient.StartWorkflowBeta1(ctx, &StartWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "dapr",
|
||||
WorkflowName: "TestWorkflow",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "TestID", resp.InstanceID)
|
||||
})
|
||||
t.Run("start workflow - valid (without component name)", func(t *testing.T) {
|
||||
resp, err := testClient.StartWorkflowBeta1(ctx, &StartWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "",
|
||||
WorkflowName: "TestWorkflow",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "TestID", resp.InstanceID)
|
||||
})
|
||||
t.Run("start workflow - rpc failure", func(t *testing.T) {
|
||||
resp, err := testClient.StartWorkflowBeta1(ctx, &StartWorkflowRequest{
|
||||
InstanceID: testWorkflowFailureID,
|
||||
WorkflowComponent: "dapr",
|
||||
WorkflowName: "TestWorkflow",
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, resp)
|
||||
})
|
||||
t.Run("start workflow - grpc failure", func(t *testing.T) {
|
||||
resp, err := testClient.StartWorkflowBeta1(ctx, &StartWorkflowRequest{
|
||||
InstanceID: "",
|
||||
WorkflowComponent: "dapr",
|
||||
WorkflowName: "",
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, resp)
|
||||
})
|
||||
t.Run("start workflow - cannot serialize input", func(t *testing.T) {
|
||||
resp, err := testClient.StartWorkflowBeta1(ctx, &StartWorkflowRequest{
|
||||
InstanceID: "",
|
||||
WorkflowComponent: "dapr",
|
||||
WorkflowName: "TestWorkflow",
|
||||
Input: math.NaN(),
|
||||
SendRawInput: false,
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, resp)
|
||||
})
|
||||
t.Run("start workflow - raw input", func(t *testing.T) {
|
||||
resp, err := testClient.StartWorkflowBeta1(ctx, &StartWorkflowRequest{
|
||||
InstanceID: "",
|
||||
WorkflowComponent: "dapr",
|
||||
WorkflowName: "TestWorkflow",
|
||||
Input: []byte("stringtest"),
|
||||
SendRawInput: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
})
|
||||
|
||||
t.Run("start workflow - raw input (invalid)", func(t *testing.T) {
|
||||
resp, err := testClient.StartWorkflowBeta1(ctx, &StartWorkflowRequest{
|
||||
InstanceID: "",
|
||||
WorkflowComponent: "dapr",
|
||||
WorkflowName: "TestWorkflow",
|
||||
Input: "test string",
|
||||
SendRawInput: true,
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, resp)
|
||||
})
|
||||
|
||||
// 2: GetWorkflow
|
||||
t.Run("get workflow", func(t *testing.T) {
|
||||
resp, err := testClient.GetWorkflowBeta1(ctx, &GetWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "dapr",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
})
|
||||
|
||||
t.Run("get workflow - valid", func(t *testing.T) {
|
||||
resp, err := testClient.GetWorkflowBeta1(ctx, &GetWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "dapr",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
})
|
||||
|
||||
t.Run("get workflow - valid (without component)", func(t *testing.T) {
|
||||
resp, err := testClient.GetWorkflowBeta1(ctx, &GetWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
})
|
||||
|
||||
t.Run("get workflow - invalid id", func(t *testing.T) {
|
||||
resp, err := testClient.GetWorkflowBeta1(ctx, &GetWorkflowRequest{
|
||||
InstanceID: "",
|
||||
WorkflowComponent: "dapr",
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, resp)
|
||||
})
|
||||
|
||||
t.Run("get workflow - grpc fail", func(t *testing.T) {
|
||||
resp, err := testClient.GetWorkflowBeta1(ctx, &GetWorkflowRequest{
|
||||
InstanceID: testWorkflowFailureID,
|
||||
WorkflowComponent: "dapr",
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, resp)
|
||||
})
|
||||
|
||||
// 3: PauseWorkflow
|
||||
t.Run("pause workflow", func(t *testing.T) {
|
||||
err := testClient.PauseWorkflowBeta1(ctx, &PauseWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "dapr",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("pause workflow - valid (without component)", func(t *testing.T) {
|
||||
err := testClient.PauseWorkflowBeta1(ctx, &PauseWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("pause workflow invalid instanceid", func(t *testing.T) {
|
||||
err := testClient.PauseWorkflowBeta1(ctx, &PauseWorkflowRequest{
|
||||
InstanceID: "",
|
||||
WorkflowComponent: "dapr",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("pause workflow", func(t *testing.T) {
|
||||
err := testClient.PauseWorkflowBeta1(ctx, &PauseWorkflowRequest{
|
||||
InstanceID: testWorkflowFailureID,
|
||||
WorkflowComponent: "dapr",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
// 4: ResumeWorkflow
|
||||
t.Run("resume workflow", func(t *testing.T) {
|
||||
err := testClient.ResumeWorkflowBeta1(ctx, &ResumeWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "dapr",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("resume workflow - valid (without component)", func(t *testing.T) {
|
||||
err := testClient.ResumeWorkflowBeta1(ctx, &ResumeWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("resume workflow - invalid instanceid", func(t *testing.T) {
|
||||
err := testClient.ResumeWorkflowBeta1(ctx, &ResumeWorkflowRequest{
|
||||
InstanceID: "",
|
||||
WorkflowComponent: "dapr",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("resume workflow - grpc fail", func(t *testing.T) {
|
||||
err := testClient.ResumeWorkflowBeta1(ctx, &ResumeWorkflowRequest{
|
||||
InstanceID: testWorkflowFailureID,
|
||||
WorkflowComponent: "dapr",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
// 5: TerminateWorkflow
|
||||
t.Run("terminate workflow", func(t *testing.T) {
|
||||
err := testClient.TerminateWorkflowBeta1(ctx, &TerminateWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "dapr",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("terminate workflow - valid (without component)", func(t *testing.T) {
|
||||
err := testClient.TerminateWorkflowBeta1(ctx, &TerminateWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("terminate workflow - invalid instanceid", func(t *testing.T) {
|
||||
err := testClient.TerminateWorkflowBeta1(ctx, &TerminateWorkflowRequest{
|
||||
InstanceID: "",
|
||||
WorkflowComponent: "dapr",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("terminate workflow - grpc failure", func(t *testing.T) {
|
||||
err := testClient.TerminateWorkflowBeta1(ctx, &TerminateWorkflowRequest{
|
||||
InstanceID: testWorkflowFailureID,
|
||||
WorkflowComponent: "dapr",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
// 6: RaiseEventWorkflow
|
||||
t.Run("raise event workflow", func(t *testing.T) {
|
||||
err := testClient.RaiseEventWorkflowBeta1(ctx, &RaiseEventWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "dapr",
|
||||
EventName: "TestEvent",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("raise event workflow - valid (without component)", func(t *testing.T) {
|
||||
err := testClient.RaiseEventWorkflowBeta1(ctx, &RaiseEventWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "",
|
||||
EventName: "TestEvent",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("raise event workflow - invalid instanceid", func(t *testing.T) {
|
||||
err := testClient.RaiseEventWorkflowBeta1(ctx, &RaiseEventWorkflowRequest{
|
||||
InstanceID: "",
|
||||
WorkflowComponent: "dapr",
|
||||
EventName: "TestEvent",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("raise event workflow - invalid eventname", func(t *testing.T) {
|
||||
err := testClient.RaiseEventWorkflowBeta1(ctx, &RaiseEventWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "dapr",
|
||||
EventName: "",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("raise event workflow - grpc failure", func(t *testing.T) {
|
||||
err := testClient.RaiseEventWorkflowBeta1(ctx, &RaiseEventWorkflowRequest{
|
||||
InstanceID: testWorkflowFailureID,
|
||||
WorkflowComponent: "dapr",
|
||||
EventName: "TestEvent",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("raise event workflow - cannot serialize input", func(t *testing.T) {
|
||||
err := testClient.RaiseEventWorkflowBeta1(ctx, &RaiseEventWorkflowRequest{
|
||||
InstanceID: testWorkflowFailureID,
|
||||
WorkflowComponent: "dapr",
|
||||
EventName: "TestEvent",
|
||||
EventData: math.NaN(),
|
||||
SendRawData: false,
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("raise event workflow - raw input", func(t *testing.T) {
|
||||
err := testClient.RaiseEventWorkflowBeta1(ctx, &RaiseEventWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "dapr",
|
||||
EventName: "TestEvent",
|
||||
EventData: []byte("teststring"),
|
||||
SendRawData: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("raise event workflow - raw input (invalid)", func(t *testing.T) {
|
||||
err := testClient.RaiseEventWorkflowBeta1(ctx, &RaiseEventWorkflowRequest{
|
||||
InstanceID: testWorkflowFailureID,
|
||||
WorkflowComponent: "dapr",
|
||||
EventName: "TestEvent",
|
||||
EventData: "test string",
|
||||
SendRawData: true,
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
// 7: PurgeWorkflow
|
||||
t.Run("purge workflow", func(t *testing.T) {
|
||||
err := testClient.PurgeWorkflowBeta1(ctx, &PurgeWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "dapr",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("purge workflow - valid (without component)", func(t *testing.T) {
|
||||
err := testClient.PurgeWorkflowBeta1(ctx, &PurgeWorkflowRequest{
|
||||
InstanceID: "TestID",
|
||||
WorkflowComponent: "",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("purge workflow - invalid instanceid", func(t *testing.T) {
|
||||
err := testClient.PurgeWorkflowBeta1(ctx, &PurgeWorkflowRequest{
|
||||
InstanceID: "",
|
||||
WorkflowComponent: "dapr",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("purge workflow - grpc failure", func(t *testing.T) {
|
||||
err := testClient.PurgeWorkflowBeta1(ctx, &PurgeWorkflowRequest{
|
||||
InstanceID: testWorkflowFailureID,
|
||||
WorkflowComponent: "dapr",
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
|
@ -20,48 +20,6 @@ The Dapr client package allows you to interact with other Dapr applications from
|
|||
```go
|
||||
import "github.com/dapr/go-sdk/client"
|
||||
```
|
||||
## Error handling
|
||||
Dapr errors are based on [gRPC's richer error model](https://cloud.google.com/apis/design/errors#error_model).
|
||||
The following code shows an example of how you can parse and handle the error details:
|
||||
|
||||
```go
|
||||
if err != nil {
|
||||
st := status.Convert(err)
|
||||
|
||||
fmt.Printf("Code: %s\n", st.Code().String())
|
||||
fmt.Printf("Message: %s\n", st.Message())
|
||||
|
||||
for _, detail := range st.Details() {
|
||||
switch t := detail.(type) {
|
||||
case *errdetails.ErrorInfo:
|
||||
// Handle ErrorInfo details
|
||||
fmt.Printf("ErrorInfo:\n- Domain: %s\n- Reason: %s\n- Metadata: %v\n", t.GetDomain(), t.GetReason(), t.GetMetadata())
|
||||
case *errdetails.BadRequest:
|
||||
// Handle BadRequest details
|
||||
fmt.Println("BadRequest:")
|
||||
for _, violation := range t.GetFieldViolations() {
|
||||
fmt.Printf("- Key: %s\n", violation.GetField())
|
||||
fmt.Printf("- The %q field was wrong: %s\n", violation.GetField(), violation.GetDescription())
|
||||
}
|
||||
case *errdetails.ResourceInfo:
|
||||
// Handle ResourceInfo details
|
||||
fmt.Printf("ResourceInfo:\n- Resource type: %s\n- Resource name: %s\n- Owner: %s\n- Description: %s\n",
|
||||
t.GetResourceType(), t.GetResourceName(), t.GetOwner(), t.GetDescription())
|
||||
case *errdetails.Help:
|
||||
// Handle ResourceInfo details
|
||||
fmt.Println("HelpInfo:")
|
||||
for _, link := range t.GetLinks() {
|
||||
fmt.Printf("- Url: %s\n", link.Url)
|
||||
fmt.Printf("- Description: %s\n", link.Description)
|
||||
}
|
||||
|
||||
default:
|
||||
// Add cases for other types of details you expect
|
||||
fmt.Printf("Unhandled error detail type: %v\n", t)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Building blocks
|
||||
|
||||
|
@ -88,91 +46,6 @@ resp, err = client.InvokeMethodWithContent(ctx, "app-id", "method-name", "post",
|
|||
|
||||
For a full guide on service invocation, visit [How-To: Invoke a service]({{< ref howto-invoke-discover-services.md >}}).
|
||||
|
||||
### Workflows
|
||||
|
||||
Workflows and their activities can be authored and managed using the Dapr Go SDK like so:
|
||||
|
||||
```go
|
||||
import (
|
||||
...
|
||||
"github.com/dapr/go-sdk/workflow"
|
||||
...
|
||||
)
|
||||
|
||||
func ExampleWorkflow(ctx *workflow.WorkflowContext) (any, error) {
|
||||
var output string
|
||||
input := "world"
|
||||
|
||||
if err := ctx.CallActivity(ExampleActivity, workflow.ActivityInput(input)).Await(&output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Print output - "hello world"
|
||||
fmt.Println(output)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func ExampleActivity(ctx workflow.ActivityContext) (any, error) {
|
||||
var input int
|
||||
if err := ctx.GetInput(&input); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("hello %s", input), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Create a workflow worker
|
||||
w, err := workflow.NewWorker()
|
||||
if err != nil {
|
||||
log.Fatalf("error creating worker: %v", err)
|
||||
}
|
||||
|
||||
// Register the workflow
|
||||
w.RegisterWorkflow(ExampleWorkflow)
|
||||
|
||||
// Register the activity
|
||||
w.RegisterActivity(ExampleActivity)
|
||||
|
||||
// Start workflow runner
|
||||
if err := w.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Create a workflow client
|
||||
wfClient, err := workflow.NewClient()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Start a new workflow
|
||||
id, err := wfClient.ScheduleNewWorkflow(context.Background(), "ExampleWorkflow")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Wait for the workflow to complete
|
||||
metadata, err := wfClient.WaitForWorkflowCompletion(ctx, id)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Print workflow status post-completion
|
||||
fmt.Println(metadata.RuntimeStatus)
|
||||
|
||||
// Shutdown Worker
|
||||
w.Shutdown()
|
||||
}
|
||||
```
|
||||
|
||||
- For a more comprehensive guide on workflows visit these How-To guides:
|
||||
- [How-To: Author a workflow]({{< ref howto-author-workflow.md >}}).
|
||||
- [How-To: Manage a workflow]({{< ref howto-manage-workflow.md >}}).
|
||||
- Visit the Go SDK Examples to jump into complete examples:
|
||||
- [Workflow Example](https://github.com/dapr/go-sdk/tree/main/examples/workflow)
|
||||
- [Workflow - Parallelised](https://github.com/dapr/go-sdk/tree/main/examples/workflow-parallel)
|
||||
|
||||
### State Management
|
||||
|
||||
For simple use-cases, Dapr client provides easy to use `Save`, `Get`, `Delete` methods:
|
||||
|
@ -330,49 +203,6 @@ if res.Error != nil {
|
|||
|
||||
For a full guide on pub/sub, visit [How-To: Publish & subscribe]({{< ref howto-publish-subscribe.md >}}).
|
||||
|
||||
### Workflow
|
||||
|
||||
You can create [workflows]({{< ref workflow-overview.md >}}) using the Go SDK. For example, start with a simple workflow activity:
|
||||
|
||||
```go
|
||||
func TestActivity(ctx workflow.ActivityContext) (any, error) {
|
||||
var input int
|
||||
if err := ctx.GetInput(&input); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Do something here
|
||||
return "result", nil
|
||||
}
|
||||
```
|
||||
|
||||
Write a simple workflow function:
|
||||
|
||||
```go
|
||||
func TestWorkflow(ctx *workflow.WorkflowContext) (any, error) {
|
||||
var input int
|
||||
if err := ctx.GetInput(&input); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var output string
|
||||
if err := ctx.CallActivity(TestActivity, workflow.ActivityInput(input)).Await(&output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ctx.WaitForExternalEvent("testEvent", time.Second*60).Await(&output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := ctx.CreateTimer(time.Second).Await(nil); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
```
|
||||
|
||||
Then compose your application that will use the workflow you've created. [Refer to the How-To: Author workflows guide]({{< ref howto-author-workflow.md >}}) for a full walk-through.
|
||||
|
||||
Try out the [Go SDK workflow example.](https://github.com/dapr/go-sdk/blob/main/examples/workflow)
|
||||
|
||||
### Output Bindings
|
||||
|
||||
|
||||
|
@ -474,7 +304,7 @@ opt := map[string]string{
|
|||
secret, err := client.GetSecret(ctx, "store-name", "secret-name", opt)
|
||||
```
|
||||
|
||||
### Authentication
|
||||
#### Authentication
|
||||
|
||||
By default, Dapr relies on the network boundary to limit access to its API. If however the target Dapr API is configured with token-based authentication, users can configure the Go Dapr client with that token in two ways:
|
||||
|
||||
|
@ -574,7 +404,7 @@ To encrypt:
|
|||
|
||||
```go
|
||||
// Encrypt the data using Dapr
|
||||
out, err := client.Encrypt(context.Background(), rf, dapr.EncryptOptions{
|
||||
out, err := sdkClient.Encrypt(context.Background(), rf, dapr.EncryptOptions{
|
||||
// These are the 3 required parameters
|
||||
ComponentName: "mycryptocomponent",
|
||||
KeyName: "mykey",
|
||||
|
@ -589,7 +419,7 @@ To decrypt:
|
|||
|
||||
```go
|
||||
// Decrypt the data using Dapr
|
||||
out, err := client.Decrypt(context.Background(), rf, dapr.EncryptOptions{
|
||||
out, err := sdkClient.Decrypt(context.Background(), rf, dapr.EncryptOptions{
|
||||
// Only required option is the component name
|
||||
ComponentName: "mycryptocomponent",
|
||||
})
|
||||
|
@ -598,4 +428,4 @@ out, err := client.Decrypt(context.Background(), rf, dapr.EncryptOptions{
|
|||
For a full guide on cryptography, visit [How-To: Use the cryptography APIs]({{< ref howto-cryptography.md >}}).
|
||||
|
||||
## Related links
|
||||
[Go SDK Examples](https://github.com/dapr/go-sdk/tree/main/examples)
|
||||
[Go SDK Examples](https://github.com/dapr/go-sdk/tree/main/examples)
|
|
@ -83,35 +83,6 @@ if err != nil {
|
|||
}
|
||||
```
|
||||
|
||||
You can also create a custom type that implements the `TopicEventSubscriber` interface to handle your events:
|
||||
|
||||
```go
|
||||
type EventHandler struct {
|
||||
// any data or references that your event handler needs.
|
||||
}
|
||||
|
||||
func (h *EventHandler) Handle(ctx context.Context, e *common.TopicEvent) (retry bool, err error) {
|
||||
log.Printf("event - PubsubName:%s, Topic:%s, ID:%s, Data: %v", e.PubsubName, e.Topic, e.ID, e.Data)
|
||||
// do something with the event
|
||||
return true, nil
|
||||
}
|
||||
```
|
||||
|
||||
The `EventHandler` can then be added using the `AddTopicEventSubscriber` method:
|
||||
|
||||
```go
|
||||
sub := &common.Subscription{
|
||||
PubsubName: "messages",
|
||||
Topic: "topic1",
|
||||
}
|
||||
eventHandler := &EventHandler{
|
||||
// initialize any fields
|
||||
}
|
||||
if err := s.AddTopicEventSubscriber(sub, eventHandler); err != nil {
|
||||
log.Fatalf("error adding topic subscription: %v", err)
|
||||
}
|
||||
```
|
||||
|
||||
### Service Invocation Handler
|
||||
To handle service invocations you will need to add at least one service invocation handler before starting the service:
|
||||
|
||||
|
|
|
@ -78,35 +78,6 @@ if err != nil {
|
|||
}
|
||||
```
|
||||
|
||||
You can also create a custom type that implements the `TopicEventSubscriber` interface to handle your events:
|
||||
|
||||
```go
|
||||
type EventHandler struct {
|
||||
// any data or references that your event handler needs.
|
||||
}
|
||||
|
||||
func (h *EventHandler) Handle(ctx context.Context, e *common.TopicEvent) (retry bool, err error) {
|
||||
log.Printf("event - PubsubName:%s, Topic:%s, ID:%s, Data: %v", e.PubsubName, e.Topic, e.ID, e.Data)
|
||||
// do something with the event
|
||||
return true, nil
|
||||
}
|
||||
```
|
||||
|
||||
The `EventHandler` can then be added using the `AddTopicEventSubscriber` method:
|
||||
|
||||
```go
|
||||
sub := &common.Subscription{
|
||||
PubsubName: "messages",
|
||||
Topic: "topic1",
|
||||
}
|
||||
eventHandler := &EventHandler{
|
||||
// initialize any fields
|
||||
}
|
||||
if err := s.AddTopicEventSubscriber(sub, eventHandler); err != nil {
|
||||
log.Fatalf("error adding topic subscription: %v", err)
|
||||
}
|
||||
```
|
||||
|
||||
### Service Invocation Handler
|
||||
To handle service invocations you will need to add at least one service invocation handler before starting the service:
|
||||
|
||||
|
|
|
@ -17,11 +17,10 @@ expected_stdout_lines:
|
|||
- '== APP == get post request = laurence'
|
||||
- '== APP == get req = hello'
|
||||
- '== APP == get req = hello'
|
||||
- '== APP == receive reminder = testReminderName state = "hello"'
|
||||
- '== APP == receive reminder = testReminderName state = "hello"'
|
||||
- '== APP == receive reminder = testReminderName state = "hello" duetime = 5s period = 5s'
|
||||
- '== APP == receive reminder = testReminderName state = "hello" duetime = 5s period = 5s'
|
||||
background: true
|
||||
sleep: 30
|
||||
timeout_seconds: 60
|
||||
-->
|
||||
|
||||
```bash
|
||||
|
@ -55,7 +54,6 @@ expected_stdout_lines:
|
|||
|
||||
background: true
|
||||
sleep: 40
|
||||
timeout_seconds: 60
|
||||
-->
|
||||
|
||||
```bash
|
||||
|
@ -69,11 +67,20 @@ dapr run --app-id actor-client \
|
|||
|
||||
### Cleanup
|
||||
|
||||
<!-- STEP
|
||||
expected_stdout_lines:
|
||||
- '✅ app stopped successfully: actor-serving'
|
||||
expected_stderr_lines:
|
||||
name: Shutdown dapr
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr stop --app-id actor-serving
|
||||
(lsof -i:8080 | grep main) | awk '{print $2}' | xargs kill
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Result
|
||||
- client side
|
||||
```
|
||||
|
@ -99,6 +106,6 @@ dapr stop --app-id actor-serving
|
|||
== APP == get post request = laurence
|
||||
== APP == get req = hello
|
||||
== APP == get req = hello
|
||||
== APP == receive reminder = testReminderName state = "hello"
|
||||
== APP == receive reminder = testReminderName state = "hello"
|
||||
```
|
||||
== APP == receive reminder = testReminderName state = "hello" duetime = 5s period = 5s
|
||||
== APP == receive reminder = testReminderName state = "hello" duetime = 5s period = 5s
|
||||
```
|
|
@ -0,0 +1,24 @@
|
|||
module github.com/dapr/go-sdk/examples/actor
|
||||
|
||||
go 1.19
|
||||
|
||||
// Needed to validate SDK changes in CI/CD
|
||||
replace github.com/dapr/go-sdk => ../../
|
||||
|
||||
require (
|
||||
github.com/dapr/go-sdk v0.0.0-00010101000000-000000000000
|
||||
github.com/google/uuid v1.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-chi/chi/v5 v5.0.8 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
golang.org/x/net v0.5.0 // indirect
|
||||
golang.org/x/sys v0.4.0 // indirect
|
||||
golang.org/x/text v0.6.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf // indirect
|
||||
google.golang.org/grpc v1.51.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
|
@ -0,0 +1,40 @@
|
|||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
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/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf h1:/JqRexUvugu6JURQ0O7RfV1EnvgrOxUV4tSjuAv0Sr0=
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
|
||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -124,7 +124,7 @@ func (t *TestActor) IncrementAndGet(ctx context.Context, stateKey string) (*api.
|
|||
}
|
||||
|
||||
func (t *TestActor) ReminderCall(reminderName string, state []byte, dueTime string, period string) {
|
||||
fmt.Println("receive reminder = ", reminderName, " state = ", string(state))
|
||||
fmt.Println("receive reminder = ", reminderName, " state = ", string(state), "duetime = ", dueTime, "period = ", period)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
|
|
@ -13,20 +13,19 @@ name: Run Configuration Client
|
|||
output_match_mode: substring
|
||||
match_order: none
|
||||
expected_stdout_lines:
|
||||
- '== APP == got config key = mykey, value = myConfigValue'
|
||||
- '== APP == got config key = mySubscribeKey1, value = mySubscribeValue1'
|
||||
- '== APP == got config key = mySubscribeKey2, value = mySubscribeValue1'
|
||||
- '== APP == got config key = mySubscribeKey3, value = mySubscribeValue1'
|
||||
- '== APP == got config key = mySubscribeKey1, value = mySubscribeValue2'
|
||||
- '== APP == got config key = mySubscribeKey2, value = mySubscribeValue2'
|
||||
- '== APP == got config key = mySubscribeKey3, value = mySubscribeValue2'
|
||||
- '== APP == got config key = mySubscribeKey1, value = mySubscribeValue3'
|
||||
- '== APP == got config key = mySubscribeKey2, value = mySubscribeValue3'
|
||||
- '== APP == got config key = mySubscribeKey3, value = mySubscribeValue3'
|
||||
- '== APP == get config = myConfigValue'
|
||||
- '== APP == get updated config key = mySubscribeKey1, value = mySubscribeValue1'
|
||||
- '== APP == get updated config key = mySubscribeKey2, value = mySubscribeValue1'
|
||||
- '== APP == get updated config key = mySubscribeKey3, value = mySubscribeValue1'
|
||||
- '== APP == get updated config key = mySubscribeKey1, value = mySubscribeValue2'
|
||||
- '== APP == get updated config key = mySubscribeKey2, value = mySubscribeValue2'
|
||||
- '== APP == get updated config key = mySubscribeKey3, value = mySubscribeValue2'
|
||||
- '== APP == get updated config key = mySubscribeKey1, value = mySubscribeValue3'
|
||||
- '== APP == get updated config key = mySubscribeKey2, value = mySubscribeValue3'
|
||||
- '== APP == get updated config key = mySubscribeKey3, value = mySubscribeValue3'
|
||||
- '== APP == dapr configuration subscribe finished.'
|
||||
background: false
|
||||
sleep: 40
|
||||
timeout_seconds: 60
|
||||
-->
|
||||
|
||||
```bash
|
||||
|
@ -48,25 +47,17 @@ dapr run --app-id configuration-api\
|
|||
The subscription event order may out of order.
|
||||
|
||||
```
|
||||
got config key = mykey, value = myConfigValue
|
||||
|
||||
got config key = mySubscribeKey1, value = mySubscribeValue1
|
||||
got config key = mySubscribeKey2, value = mySubscribeValue1
|
||||
got config key = mySubscribeKey3, value = mySubscribeValue1
|
||||
got config key = mySubscribeKey1, value = mySubscribeValue2
|
||||
got config key = mySubscribeKey2, value = mySubscribeValue2
|
||||
got config key = mySubscribeKey3, value = mySubscribeValue2
|
||||
got config key = mySubscribeKey1, value = mySubscribeValue3
|
||||
got config key = mySubscribeKey2, value = mySubscribeValue3
|
||||
got config key = mySubscribeKey3, value = mySubscribeValue3
|
||||
got config key = mySubscribeKey1, value = mySubscribeValue4
|
||||
got config key = mySubscribeKey2, value = mySubscribeValue4
|
||||
got config key = mySubscribeKey3, value = mySubscribeValue4
|
||||
got config key = mySubscribeKey1, value = mySubscribeValue5
|
||||
got config key = mySubscribeKey2, value = mySubscribeValue5
|
||||
got config key = mySubscribeKey3, value = mySubscribeValue5
|
||||
get config = myConfigValue
|
||||
get updated config key = mySubscribeKey1, value = mySubscribeValue1
|
||||
get updated config key = mySubscribeKey2, value = mySubscribeValue1
|
||||
get updated config key = mySubscribeKey3, value = mySubscribeValue1
|
||||
get updated config key = mySubscribeKey1, value = mySubscribeValue2
|
||||
get updated config key = mySubscribeKey2, value = mySubscribeValue2
|
||||
get updated config key = mySubscribeKey3, value = mySubscribeValue2
|
||||
get updated config key = mySubscribeKey1, value = mySubscribeValue3
|
||||
get updated config key = mySubscribeKey2, value = mySubscribeValue3
|
||||
get updated config key = mySubscribeKey3, value = mySubscribeValue3
|
||||
dapr configuration subscribe finished.
|
||||
dapr configuration unsubscribed
|
||||
✅ Exited App successfully
|
||||
|
||||
```
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
module github.com/dapr/go-sdk/examples/configuration
|
||||
|
||||
go 1.19
|
||||
|
||||
// Needed to validate SDK changes in CI/CD
|
||||
replace github.com/dapr/go-sdk => ../../
|
||||
|
||||
require (
|
||||
github.com/dapr/go-sdk v0.0.0-00010101000000-000000000000
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
google.golang.org/grpc v1.55.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
|
@ -0,0 +1,49 @@
|
|||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
||||
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/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
dapr "github.com/dapr/go-sdk/client"
|
||||
|
@ -12,7 +11,7 @@ import (
|
|||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
func addItems(wg *sync.WaitGroup) {
|
||||
func init() {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:6379",
|
||||
}
|
||||
|
@ -20,10 +19,10 @@ func addItems(wg *sync.WaitGroup) {
|
|||
// set config value
|
||||
client.Set(context.Background(), "mykey", "myConfigValue", -1)
|
||||
ticker := time.NewTicker(time.Second)
|
||||
wg.Add(3 * 5)
|
||||
go func() {
|
||||
for i := 0; i < 5; i++ {
|
||||
<-ticker.C
|
||||
// update config value
|
||||
client.Set(context.Background(), "mySubscribeKey1", "mySubscribeValue"+strconv.Itoa(i+1), -1)
|
||||
client.Set(context.Background(), "mySubscribeKey2", "mySubscribeValue"+strconv.Itoa(i+1), -1)
|
||||
client.Set(context.Background(), "mySubscribeKey3", "mySubscribeValue"+strconv.Itoa(i+1), -1)
|
||||
|
@ -33,8 +32,6 @@ func addItems(wg *sync.WaitGroup) {
|
|||
}
|
||||
|
||||
func main() {
|
||||
var wg sync.WaitGroup
|
||||
addItems(&wg)
|
||||
ctx := context.Background()
|
||||
client, err := dapr.NewClient()
|
||||
if err != nil {
|
||||
|
@ -45,27 +42,25 @@ func main() {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("got config key = mykey, value = %s \n", (*items).Value)
|
||||
fmt.Printf("get config = %s\n", (*items).Value)
|
||||
|
||||
ctx, f := context.WithTimeout(ctx, 60*time.Second)
|
||||
md := metadata.Pairs("dapr-app-id", "configuration-api")
|
||||
ctx = metadata.NewOutgoingContext(ctx, md)
|
||||
defer f()
|
||||
subscribeID, err := client.SubscribeConfigurationItems(ctx, "example-config", []string{"mySubscribeKey1", "mySubscribeKey2", "mySubscribeKey3"}, func(id string, items map[string]*dapr.ConfigurationItem) {
|
||||
wg.Done()
|
||||
for k, v := range items {
|
||||
fmt.Printf("got config key = %s, value = %s \n", k, v.Value)
|
||||
fmt.Printf("get updated config key = %s, value = %s \n", k, v.Value)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
wg.Wait()
|
||||
time.Sleep(time.Second*3 + time.Millisecond*500)
|
||||
|
||||
// dapr configuration unsubscribe called.
|
||||
if err := client.UnsubscribeConfigurationItems(ctx, "example-config", subscribeID); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("dapr configuration unsubscribed")
|
||||
time.Sleep(time.Second)
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
# Dapr Conversation Example with go-sdk
|
||||
|
||||
## Step
|
||||
|
||||
### Prepare
|
||||
|
||||
- Dapr installed
|
||||
|
||||
### Run Conversation Example
|
||||
|
||||
<!-- STEP
|
||||
name: Run Conversation
|
||||
output_match_mode: substring
|
||||
expected_stdout_lines:
|
||||
- '== APP == conversation output: hello world'
|
||||
|
||||
background: true
|
||||
sleep: 60
|
||||
timeout_seconds: 60
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr run --app-id conversation \
|
||||
--dapr-grpc-port 50001 \
|
||||
--log-level debug \
|
||||
--resources-path ./config \
|
||||
-- go run ./main.go
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Result
|
||||
|
||||
```
|
||||
- '== APP == conversation output: hello world'
|
||||
```
|
|
@ -1,7 +0,0 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: echo
|
||||
spec:
|
||||
type: conversation.echo
|
||||
version: v1
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
dapr "github.com/dapr/go-sdk/client"
|
||||
)
|
||||
|
||||
func main() {
|
||||
client, err := dapr.NewClient()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
input := dapr.ConversationInput{
|
||||
Content: "hello world",
|
||||
// Role: nil, // Optional
|
||||
// ScrubPII: nil, // Optional
|
||||
}
|
||||
|
||||
fmt.Printf("conversation input: %s\n", input.Content)
|
||||
|
||||
var conversationComponent = "echo"
|
||||
|
||||
request := dapr.NewConversationRequest(conversationComponent, []dapr.ConversationInput{input})
|
||||
|
||||
resp, err := client.ConverseAlpha1(context.Background(), request)
|
||||
if err != nil {
|
||||
log.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("conversation output: %s\n", resp.Outputs[0].Result)
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
# Dapr Crypto Example with go-sdk
|
||||
|
||||
## Steps
|
||||
|
||||
### Prepare
|
||||
|
||||
- Dapr installed
|
||||
|
||||
> In order to run this sample, make sure that OpenSSL is available on your system.
|
||||
|
||||
### Running
|
||||
|
||||
1. This sample requires a private RSA key and a 256-bit symmetric (AES) key. We will generate them using OpenSSL:
|
||||
|
||||
<!-- STEP
|
||||
name: Generate crypto
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
background: false
|
||||
sleep: 5
|
||||
timeout_seconds: 30
|
||||
-->
|
||||
|
||||
```bash
|
||||
mkdir -p keys
|
||||
# Generate a private RSA key, 4096-bit keys
|
||||
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out keys/rsa-private-key.pem
|
||||
# Generate a 256-bit key for AES
|
||||
openssl rand -out keys/symmetric-key-256 32
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
2. Run the Go service app with Dapr:
|
||||
|
||||
<!-- STEP
|
||||
name: Run crypto example
|
||||
expected_stdout_lines:
|
||||
- '== APP == Encrypted the message, got 856 bytes'
|
||||
- '== APP == Decrypted the message, got 24 bytes'
|
||||
- '== APP == The secret is "passw0rd"'
|
||||
- '== APP == Wrote encrypted data to encrypted.out'
|
||||
- '== APP == Wrote decrypted data to decrypted.out.jpg'
|
||||
- "Exited App successfully"
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
sleep: 30
|
||||
timeout_seconds: 90
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr run --app-id crypto --resources-path ./components/ -- go run .
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
### Cleanup
|
||||
|
||||
`ctrl + c` to stop execution
|
||||
|
||||
```bash
|
||||
dapr stop --app-id crypto
|
||||
(lsof -i:8080 | grep crypto) | awk '{print $2}' | xargs kill
|
||||
```
|
||||
|
||||
## Result
|
||||
|
||||
```shell
|
||||
== APP == Encrypted the message, got 856 bytes
|
||||
== APP == Decrypted the message, got 24 bytes
|
||||
== APP == The secret is "passw0rd"
|
||||
== APP == Wrote encrypted data to encrypted.out
|
||||
== APP == Wrote decrypted data to decrypted.out.jpg
|
||||
```
|
|
@ -1,11 +0,0 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: localstorage
|
||||
spec:
|
||||
type: crypto.dapr.localstorage
|
||||
version: v1
|
||||
metadata:
|
||||
- name: path
|
||||
# Path is relative to the folder where the example is located
|
||||
value: ./keys
|
Binary file not shown.
Before Width: | Height: | Size: 5.7 MiB |
|
@ -1,162 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
dapr "github.com/dapr/go-sdk/client"
|
||||
)
|
||||
|
||||
const (
|
||||
// Name of the crypto component to use
|
||||
CryptoComponentName = "localstorage"
|
||||
// Name of the RSA private key to use
|
||||
RSAKeyName = "rsa-private-key.pem"
|
||||
// Name of the symmetric (AES) key to use
|
||||
SymmetricKeyName = "symmetric-key-256"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a new Dapr SDK client
|
||||
client, err := dapr.NewClient()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to initialize the Dapr client: %v", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// Step 1: encrypt a string using the RSA key, then decrypt it and show the output in the terminal
|
||||
encryptDecryptString(client)
|
||||
|
||||
// Step 2: encrypt a large file and then decrypt it, using the AES key
|
||||
encryptDecryptFile(client)
|
||||
}
|
||||
|
||||
func encryptDecryptString(client dapr.Client) {
|
||||
const message = `The secret is "passw0rd"`
|
||||
|
||||
// Encrypt the message
|
||||
encStream, err := client.Encrypt(context.Background(),
|
||||
strings.NewReader(message),
|
||||
dapr.EncryptOptions{
|
||||
ComponentName: CryptoComponentName,
|
||||
// Name of the key to use
|
||||
// Since this is a RSA key, we specify that as key wrapping algorithm
|
||||
KeyName: RSAKeyName,
|
||||
KeyWrapAlgorithm: "RSA",
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to encrypt the message: %v", err)
|
||||
}
|
||||
|
||||
// The method returns a readable stream, which we read in full in memory
|
||||
encBytes, err := io.ReadAll(encStream)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read the stream for the encrypted message: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Encrypted the message, got %d bytes\n", len(encBytes))
|
||||
|
||||
// Now, decrypt the encrypted data
|
||||
decStream, err := client.Decrypt(context.Background(),
|
||||
bytes.NewReader(encBytes),
|
||||
dapr.DecryptOptions{
|
||||
// We just need to pass the name of the component
|
||||
ComponentName: CryptoComponentName,
|
||||
// Passing the name of the key is optional
|
||||
KeyName: RSAKeyName,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to decrypt the message: %v", err)
|
||||
}
|
||||
|
||||
// The method returns a readable stream, which we read in full in memory
|
||||
decBytes, err := io.ReadAll(decStream)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read the stream for the decrypted message: %v", err)
|
||||
}
|
||||
|
||||
// Print the message on the console
|
||||
fmt.Printf("Decrypted the message, got %d bytes\n", len(decBytes))
|
||||
fmt.Println(string(decBytes))
|
||||
}
|
||||
|
||||
func encryptDecryptFile(client dapr.Client) {
|
||||
const fileName = "desert.jpg"
|
||||
|
||||
// Get a readable stream to the input file
|
||||
plaintextF, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open plaintext file: %v", err)
|
||||
}
|
||||
defer plaintextF.Close()
|
||||
|
||||
// Encrypt the file
|
||||
encStream, err := client.Encrypt(context.Background(),
|
||||
plaintextF,
|
||||
dapr.EncryptOptions{
|
||||
ComponentName: CryptoComponentName,
|
||||
// Name of the key to use
|
||||
// Since this is a symmetric key, we specify AES as key wrapping algorithm
|
||||
KeyName: SymmetricKeyName,
|
||||
KeyWrapAlgorithm: "AES",
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to encrypt the file: %v", err)
|
||||
}
|
||||
|
||||
// Write the encrypted data to a file "encrypted.out"
|
||||
encryptedF, err := os.Create("encrypted.out")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open destination file: %v", err)
|
||||
}
|
||||
_, err = io.Copy(encryptedF, encStream)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to write encrypted stream to file: %v", err)
|
||||
}
|
||||
encryptedF.Close()
|
||||
|
||||
fmt.Println("Wrote encrypted data to encrypted.out")
|
||||
|
||||
// Now, decrypt the encrypted data
|
||||
// First, open the file "encrypted.out" again, this time for reading
|
||||
encryptedF, err = os.Open("encrypted.out")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open encrypted file: %v", err)
|
||||
}
|
||||
defer encryptedF.Close()
|
||||
|
||||
// Now, decrypt the encrypted data
|
||||
decStream, err := client.Decrypt(context.Background(),
|
||||
encryptedF,
|
||||
dapr.DecryptOptions{
|
||||
// We just need to pass the name of the component
|
||||
ComponentName: CryptoComponentName,
|
||||
// Passing the name of the key is optional
|
||||
KeyName: SymmetricKeyName,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to decrypt the file: %v", err)
|
||||
}
|
||||
|
||||
// Write the decrypted data to a file "decrypted.out.jpg"
|
||||
decryptedF, err := os.Create("decrypted.out.jpg")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open destination file: %v", err)
|
||||
}
|
||||
_, err = io.Copy(decryptedF, decStream)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to write decrypted stream to file: %v", err)
|
||||
}
|
||||
decryptedF.Close()
|
||||
|
||||
fmt.Println("Wrote decrypted data to decrypted.out.jpg")
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
# Dapr Distributed Scheduler Example with go-sdk
|
||||
|
||||
## Steps
|
||||
|
||||
### Prepare
|
||||
|
||||
- Dapr installed (v1.14 or higher)
|
||||
|
||||
### Run Distributed Scheduling Example
|
||||
|
||||
<!-- STEP
|
||||
name: Run Distributed Scheduling Example
|
||||
output_match_mode: substring
|
||||
expected_stdout_lines:
|
||||
- 'Scheduler stream connected'
|
||||
- 'schedulejob - success'
|
||||
- 'job 0 received'
|
||||
- 'payload: {db-backup {my-prod-db /backup-dir}}'
|
||||
- 'job 1 received'
|
||||
- 'payload: {db-backup {my-prod-db /backup-dir}}'
|
||||
- 'job 2 received'
|
||||
- 'payload: {db-backup {my-prod-db /backup-dir}}'
|
||||
- 'getjob - resp: &{prod-db-backup @every 1s 10 value:"{\"task\":\"db-backup\",\"metadata\":{\"db_name\":\"my-prod-db\",\"backup_location\":\"/backup-dir\"}}"}'
|
||||
- 'deletejob - success'
|
||||
|
||||
background: true
|
||||
sleep: 30
|
||||
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr run --app-id=distributed-scheduler \
|
||||
--metrics-port=9091 \
|
||||
--scheduler-host-address=localhost:50006 \
|
||||
--dapr-grpc-port 50001 \
|
||||
--app-port 50070 \
|
||||
--app-protocol grpc \
|
||||
--log-level debug \
|
||||
go run ./main.go
|
||||
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
|
@ -1,11 +0,0 @@
|
|||
package api
|
||||
|
||||
type Metadata struct {
|
||||
DBName string `json:"db_name"`
|
||||
BackupLocation string `json:"backup_location"`
|
||||
}
|
||||
|
||||
type DBBackup struct {
|
||||
Task string `json:"task"`
|
||||
Metadata Metadata `json:"metadata"`
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
daprc "github.com/dapr/go-sdk/client"
|
||||
"github.com/dapr/go-sdk/examples/dist-scheduler/api"
|
||||
"github.com/dapr/go-sdk/service/common"
|
||||
daprs "github.com/dapr/go-sdk/service/grpc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
server, err := daprs.NewService(":50070")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to start the server: %v", err)
|
||||
}
|
||||
|
||||
if err = server.AddJobEventHandler("prod-db-backup", prodDBBackupHandler); err != nil {
|
||||
log.Fatalf("failed to register job event handler: %v", err)
|
||||
}
|
||||
|
||||
log.Println("starting server")
|
||||
go func() {
|
||||
if err = server.Start(); err != nil {
|
||||
log.Fatalf("failed to start server: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Brief intermission to allow for the server to initialize.
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
jobData, err := json.Marshal(&api.DBBackup{
|
||||
Task: "db-backup",
|
||||
Metadata: api.Metadata{
|
||||
DBName: "my-prod-db",
|
||||
BackupLocation: "/backup-dir",
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
job := daprc.Job{
|
||||
Name: "prod-db-backup",
|
||||
Schedule: "@every 1s",
|
||||
Repeats: 10,
|
||||
Data: &anypb.Any{
|
||||
Value: jobData,
|
||||
},
|
||||
}
|
||||
|
||||
// create the client
|
||||
client, err := daprc.NewClient()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
err = client.ScheduleJobAlpha1(ctx, &job)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("schedulejob - success")
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
resp, err := client.GetJobAlpha1(ctx, "prod-db-backup")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("getjob - resp: %v\n", resp) // parse
|
||||
|
||||
err = client.DeleteJobAlpha1(ctx, "prod-db-backup")
|
||||
if err != nil {
|
||||
fmt.Printf("job deletion error: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("deletejob - success")
|
||||
}
|
||||
|
||||
if err = server.Stop(); err != nil {
|
||||
log.Fatalf("failed to stop server: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
var jobCount = 0
|
||||
|
||||
func prodDBBackupHandler(ctx context.Context, job *common.JobEvent) error {
|
||||
var jobPayload api.DBBackup
|
||||
if err := json.Unmarshal(job.Data, &jobPayload); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal payload: %v", err)
|
||||
}
|
||||
fmt.Printf("job %d received:\n type: %v \n payload: %v\n", jobCount, job.JobType, jobPayload)
|
||||
jobCount++
|
||||
return nil
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
module github.com/dapr/go-sdk/examples
|
||||
|
||||
go 1.24.2
|
||||
|
||||
replace github.com/dapr/go-sdk => ../
|
||||
|
||||
require (
|
||||
github.com/alecthomas/kingpin/v2 v2.4.0
|
||||
github.com/dapr/go-sdk v0.0.0-00010101000000-000000000000
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/google/uuid v1.6.0
|
||||
google.golang.org/grpc v1.72.0
|
||||
google.golang.org/grpc/examples v0.0.0-20240516203910-e22436abb809
|
||||
google.golang.org/protobuf v1.36.6
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/dapr/dapr v1.15.5 // indirect
|
||||
github.com/dapr/durabletask-go v0.6.5 // indirect
|
||||
github.com/dapr/kit v0.15.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/go-chi/chi/v5 v5.1.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e // indirect
|
||||
)
|
105
examples/go.sum
105
examples/go.sum
|
@ -1,105 +0,0 @@
|
|||
github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY=
|
||||
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/dapr/dapr v1.15.5 h1:bkCmcQQfaQ5C49P3l0elCzDr4/Oja5kitM3jStY+2RY=
|
||||
github.com/dapr/dapr v1.15.5/go.mod h1:wwopO8AD9CZOgVj4bsdXNmeQujMo0v3MLAqeaX+gb00=
|
||||
github.com/dapr/durabletask-go v0.6.5 h1:aWcxMfYudojpgRjJRdUr7yyZ7rGcvLtWXUuA4cGHBR0=
|
||||
github.com/dapr/durabletask-go v0.6.5/go.mod h1:nTZ5fCbJLnZbVdi6Z2YxdDF1OgQZL3LroogGuetrwuA=
|
||||
github.com/dapr/kit v0.15.2 h1:5H9IhKScU/SpE2Hxvr5vUlmYN1e2MJN15RoT8/KSziU=
|
||||
github.com/dapr/kit v0.15.2/go.mod h1:HwFsBKEbcyLanWlDZE7u/jnaDCD/tU+n3pkFNUctQNw=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
|
||||
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2 h1:IqsN8hx+lWLqlN+Sc3DoMy/watjofWiU8sRFgQ8fhKM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
|
||||
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/grpc/examples v0.0.0-20240516203910-e22436abb809 h1:f96Rv5C5Y2CWlbKK6KhKDdyFgGOjPHPEMsdyaxE9k0c=
|
||||
google.golang.org/grpc/examples v0.0.0-20240516203910-e22436abb809/go.mod h1:uaPEAc5V00jjG3DPhGFLXGT290RUV3+aNQigs1W50/8=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro=
|
||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
|
@ -17,8 +17,7 @@ output_match_mode: substring
|
|||
expected_stdout_lines:
|
||||
- 'Received: Dapr'
|
||||
background: true
|
||||
sleep: 30
|
||||
timeout_seconds: 60
|
||||
sleep: 15
|
||||
-->
|
||||
|
||||
```bash
|
||||
|
@ -40,7 +39,6 @@ expected_stdout_lines:
|
|||
output_match_mode: substring
|
||||
background: true
|
||||
sleep: 15
|
||||
timeout_seconds: 60
|
||||
-->
|
||||
|
||||
```bash
|
||||
|
@ -52,6 +50,15 @@ dapr run --app-id grpc-client \
|
|||
|
||||
### Cleanup
|
||||
|
||||
<!-- STEP
|
||||
expected_stdout_lines:
|
||||
- '✅ app stopped successfully: grpc-server'
|
||||
expected_stderr_lines:
|
||||
name: Shutdown dapr
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr stop --app-id grpc-server
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
module github.com/dapr/go-sdk/examples/grpc-service
|
||||
|
||||
go 1.19
|
||||
|
||||
replace github.com/dapr/go-sdk => ../../
|
||||
|
||||
require (
|
||||
github.com/dapr/go-sdk v0.0.0-00010101000000-000000000000
|
||||
google.golang.org/grpc v1.55.0
|
||||
google.golang.org/grpc/examples v0.0.0-20230602173802-c9d3ea567325
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
)
|
|
@ -0,0 +1,26 @@
|
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||
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/examples v0.0.0-20230602173802-c9d3ea567325 h1:2RthLftQfQtpQMEmkGxDGs+PAG/sVWONfKd7km4DRzM=
|
||||
google.golang.org/grpc/examples v0.0.0-20230602173802-c9d3ea567325/go.mod h1:JFf2mvgu0u96q6WJc59JQq9E9SQ6E93ML1ozmUNjW8k=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
@ -23,8 +23,7 @@ This quickstart requires you to have the following installed on your machine:
|
|||
|
||||
## Step 1 - Setup Dapr
|
||||
|
||||
- [Install the Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
|
||||
- [Initialise Dapr Locally](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
|
||||
Follow [instructions](https://docs.dapr.io/getting-started/install-dapr/) to download and install the Dapr CLI and initialize Dapr.
|
||||
|
||||
## Step 2 - Understand the code
|
||||
|
||||
|
@ -82,7 +81,6 @@ There are two ways to launch Dapr applications. You can pass the app executable
|
|||
name: Run and send order
|
||||
background: true
|
||||
sleep: 5
|
||||
timeout_seconds: 60
|
||||
expected_stdout_lines:
|
||||
- '== APP == dapr client initializing for: 127.0.0.1:3500'
|
||||
- '== APP == Sending order ID 20'
|
||||
|
@ -99,7 +97,6 @@ dapr run --app-id order-app --dapr-grpc-port 3500 --log-level error -- ./order p
|
|||
name: Run and get order
|
||||
background: true
|
||||
sleep: 5
|
||||
timeout_seconds: 60
|
||||
expected_stdout_lines:
|
||||
- '== APP == dapr client initializing for: 127.0.0.1:3500'
|
||||
- '== APP == Getting order'
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
module github.com/dapr/go-sdk/examples/hello-world
|
||||
|
||||
go 1.19
|
||||
|
||||
// Needed to validate SDK changes in CI/CD
|
||||
replace github.com/dapr/go-sdk => ../../
|
||||
|
||||
require (
|
||||
github.com/dapr/go-sdk v0.0.0-00010101000000-000000000000
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf // indirect
|
||||
google.golang.org/grpc v1.51.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
|
@ -0,0 +1,48 @@
|
|||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf h1:/JqRexUvugu6JURQ0O7RfV1EnvgrOxUV4tSjuAv0Sr0=
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
|
||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -20,7 +20,7 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
|
||||
dapr "github.com/dapr/go-sdk/client"
|
||||
)
|
||||
|
|
|
@ -21,7 +21,6 @@ expected_stdout_lines:
|
|||
- 'event - PubsubName: messages, Topic: neworder'
|
||||
background: true
|
||||
sleep: 15
|
||||
timeout_seconds: 60
|
||||
-->
|
||||
|
||||
```bash
|
||||
|
@ -44,7 +43,6 @@ expected_stdout_lines:
|
|||
- '== APP == data published'
|
||||
background: true
|
||||
sleep: 15
|
||||
timeout_seconds: 60
|
||||
-->
|
||||
|
||||
```bash
|
||||
|
@ -60,11 +58,20 @@ dapr run --app-id pub \
|
|||
|
||||
### Cleanup
|
||||
|
||||
<!-- STEP
|
||||
expected_stdout_lines:
|
||||
- '✅ app stopped successfully: sub'
|
||||
expected_stderr_lines:
|
||||
name: Shutdown dapr
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr stop --app-id sub
|
||||
(lsof -i:8080 | grep sub) | awk '{print $2}' | xargs kill
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Result
|
||||
|
||||
```shell
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
module github.com/dapr/go-sdk/examples/pubsub
|
||||
|
||||
go 1.19
|
||||
|
||||
// Needed to validate SDK changes in CI/CD
|
||||
replace github.com/dapr/go-sdk => ../../
|
||||
|
||||
require github.com/dapr/go-sdk v0.0.0-00010101000000-000000000000
|
||||
|
||||
require (
|
||||
github.com/go-chi/chi/v5 v5.0.8 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf // indirect
|
||||
google.golang.org/grpc v1.51.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
|
@ -0,0 +1,40 @@
|
|||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
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/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf h1:/JqRexUvugu6JURQ0O7RfV1EnvgrOxUV4tSjuAv0Sr0=
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
|
||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -16,8 +16,7 @@ output_match_mode: substring
|
|||
expected_stdout_lines:
|
||||
- "ContentType:text/plain, Verb:POST, QueryString:, hellow"
|
||||
background: true
|
||||
sleep: 30
|
||||
timeout_seconds: 60
|
||||
sleep: 15
|
||||
-->
|
||||
|
||||
```bash
|
||||
|
@ -61,7 +60,6 @@ expected_stdout_lines:
|
|||
- '== APP == output binding invoked'
|
||||
background: true
|
||||
sleep: 15
|
||||
timeout_seconds: 60
|
||||
-->
|
||||
|
||||
```bash
|
||||
|
@ -91,7 +89,7 @@ expected_stdout_lines:
|
|||
```bash
|
||||
dapr run --app-id custom-grpc-client \
|
||||
-d ./config \
|
||||
--max-body-size 41Mi \
|
||||
--dapr-http-max-request-size 41 \
|
||||
--log-level debug \
|
||||
go run ./custom-grpc-client/main.go
|
||||
```
|
||||
|
@ -157,7 +155,17 @@ Uses the [config/cron.yaml](config/cron.yaml) component
|
|||
|
||||
### Cleanup
|
||||
|
||||
<!-- STEP
|
||||
expected_stdout_lines:
|
||||
- '✅ app stopped successfully: serving'
|
||||
expected_stderr_lines:
|
||||
name: Shutdown dapr
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr stop --app-id serving
|
||||
(lsof -i:8080 | grep main) | awk '{print $2}' | xargs kill
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ func main() {
|
|||
|
||||
in := &dapr.InvokeBindingRequest{
|
||||
Name: "example-http-binding",
|
||||
Operation: "get",
|
||||
Operation: "create",
|
||||
}
|
||||
if err := client.InvokeOutputBinding(ctx, in); err != nil {
|
||||
panic(err)
|
||||
|
|
|
@ -7,6 +7,6 @@ spec:
|
|||
version: v1
|
||||
metadata:
|
||||
- name: url
|
||||
value: https://sandbox.api.service.nhs.uk/hello-world/hello/world
|
||||
value: https://http2.pro/api/v1
|
||||
- name: method
|
||||
value: GET
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
module github.com/dapr/go-sdk/examples/service
|
||||
|
||||
go 1.19
|
||||
|
||||
// Needed to validate SDK changes in CI/CD
|
||||
replace github.com/dapr/go-sdk => ../../
|
||||
|
||||
require (
|
||||
github.com/dapr/go-sdk v0.0.0-00010101000000-000000000000
|
||||
google.golang.org/grpc v1.52.3
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-chi/chi/v5 v5.0.8 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
golang.org/x/net v0.5.0 // indirect
|
||||
golang.org/x/sys v0.4.0 // indirect
|
||||
golang.org/x/text v0.6.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
|
@ -0,0 +1,40 @@
|
|||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
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/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf h1:/JqRexUvugu6JURQ0O7RfV1EnvgrOxUV4tSjuAv0Sr0=
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v1.52.3 h1:pf7sOysg4LdgBqduXveGKrcEwbStiK2rtfghdzlUYDQ=
|
||||
google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -12,8 +12,7 @@ This quickstart requires you to have the following installed on your machine:
|
|||
|
||||
## Step 1 - Setup Dapr
|
||||
|
||||
- [Install the Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
|
||||
- [Initialise Dapr Locally](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
|
||||
Follow [instructions](https://docs.dapr.io/getting-started/install-dapr/) to download and install the Dapr CLI and initialize Dapr.
|
||||
|
||||
## Step 2 - Understand the code
|
||||
|
||||
|
@ -70,7 +69,6 @@ There are two ways to launch Dapr applications. You can pass the app executable
|
|||
name: Run and send order
|
||||
background: true
|
||||
sleep: 5
|
||||
timeout_seconds: 60
|
||||
expected_stdout_lines:
|
||||
- '== APP == dapr client initializing for: /tmp/dapr-order-app-grpc.socket'
|
||||
- '== APP == Sending order ID 20'
|
||||
|
@ -87,7 +85,6 @@ dapr run --app-id order-app --log-level error --unix-domain-socket /tmp -- ./ord
|
|||
name: Run and get order
|
||||
background: true
|
||||
sleep: 5
|
||||
timeout_seconds: 60
|
||||
expected_stdout_lines:
|
||||
- '== APP == dapr client initializing for: /tmp/dapr-order-app-grpc.socket'
|
||||
- '== APP == Getting order'
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
module github.com/dapr/go-sdk/examples/socket
|
||||
|
||||
go 1.19
|
||||
|
||||
// Needed to validate SDK changes in CI/CD
|
||||
replace github.com/dapr/go-sdk => ../../
|
||||
|
||||
require (
|
||||
github.com/dapr/go-sdk v0.0.0-00010101000000-000000000000
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
golang.org/x/net v0.5.0 // indirect
|
||||
golang.org/x/sys v0.4.0 // indirect
|
||||
golang.org/x/text v0.6.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf // indirect
|
||||
google.golang.org/grpc v1.51.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
|
@ -0,0 +1,48 @@
|
|||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf h1:/JqRexUvugu6JURQ0O7RfV1EnvgrOxUV4tSjuAv0Sr0=
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
|
||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -6,8 +6,8 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
dapr "github.com/dapr/go-sdk/client"
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue