Merge branch 'main' into dependabot/go_modules/dot-github/workflows/dapr-bot/golang.org/x/crypto-0.35.0

This commit is contained in:
Mike Nguyen 2025-08-29 12:53:18 +01:00 committed by GitHub
commit ba8e2627cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
58 changed files with 1010 additions and 439 deletions

View File

@ -17,7 +17,7 @@ jobs:
name: Test
runs-on: ubuntu-latest
env:
GOLANGCILINT_VER: v1.61.0
GOLANGCILINT_VER: v1.64.6
steps:
- name: Checkout

View File

@ -11,7 +11,7 @@ jobs:
name: Test
runs-on: ubuntu-latest
env:
GOLANGCILINT_VER: v1.61.0
GOLANGCILINT_VER: v1.64.6
steps:
- name: Checkout

View File

@ -25,7 +25,7 @@ jobs:
- "macos-latest"
runs-on: ${{ matrix.os }}
env:
GOLANGCILINT_VER: v1.61.0 # Make sure to bump /tools/check-lint-version/main_test.go
GOLANGCILINT_VER: v1.64.6 # Make sure to bump /tools/check-lint-version/main_test.go
steps:
- name: Checkout

View File

@ -38,9 +38,9 @@ jobs:
CHECKOUT_REF: ${{ github.ref }}
outputs:
DAPR_INSTALL_URL: ${{ env.DAPR_INSTALL_URL }}
DAPR_CLI_VER: ${{ steps.outputs.outputs.DAPR_CLI_VER }}
DAPR_CLI_VER: 1.16.0-rc.1
DAPR_CLI_REF: ${{ steps.outputs.outputs.DAPR_CLI_REF }}
DAPR_RUNTIME_VER: ${{ steps.outputs.outputs.DAPR_RUNTIME_VER }}
DAPR_RUNTIME_VER: 1.16.0-rc.3
CHECKOUT_REPO: ${{ steps.outputs.outputs.CHECKOUT_REPO }}
CHECKOUT_REF: ${{ steps.outputs.outputs.CHECKOUT_REF }}
DAPR_REF: ${{ steps.outputs.outputs.DAPR_REF }}
@ -166,7 +166,7 @@ jobs:
"configuration",
"conversation",
"crypto",
"dist-scheduler",
"jobs",
"grpc-service",
"hello-world",
"pubsub",
@ -175,6 +175,7 @@ jobs:
"socket",
"workflow",
"workflow-parallel",
"workflow-taskexecutionid"
]
steps:
- name: Check out code onto GOPATH

View File

@ -234,7 +234,6 @@ linters:
- lll
- unparam
- wsl
- gomnd
- testpackage
- nestif
- nlreturn
@ -271,8 +270,6 @@ linters:
- tagalign
- mnd
- canonicalheader
- exportloopref
- execinquery
- err113
- fatcontext
- forbidigo # TODO: Re-enable and remove fmt.println

View File

@ -33,6 +33,10 @@ cover: ## Displays test coverage in the client and service packages
lint: check-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 && \

View File

@ -48,6 +48,7 @@ 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"))
@ -89,6 +90,7 @@ 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"))
@ -109,6 +111,7 @@ 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"))

View File

@ -14,7 +14,6 @@ limitations under the License.
package client
import (
"context"
"testing"
"github.com/stretchr/testify/require"
@ -25,7 +24,7 @@ import (
const testActorType = "test"
func TestInvokeActor(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
in := &InvokeActorRequest{
ActorID: "fn",
Method: "mockMethod",
@ -74,7 +73,7 @@ func TestInvokeActor(t *testing.T) {
}
func TestRegisterActorReminder(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
in := &RegisterActorReminderRequest{
ActorID: "fn",
Data: []byte(`{hello}`),
@ -137,7 +136,7 @@ func TestRegisterActorReminder(t *testing.T) {
}
func TestRegisterActorTimer(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
in := &RegisterActorTimerRequest{
ActorID: "fn",
Data: []byte(`{hello}`),
@ -215,7 +214,7 @@ func TestRegisterActorTimer(t *testing.T) {
}
func TestUnregisterActorReminder(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
in := &UnregisterActorReminderRequest{
ActorID: "fn",
ActorType: testActorType,
@ -260,7 +259,7 @@ func TestUnregisterActorReminder(t *testing.T) {
}
func TestUnregisterActorTimer(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
in := &UnregisterActorTimerRequest{
ActorID: "fn",
ActorType: testActorType,

View File

@ -14,7 +14,6 @@ limitations under the License.
package client
import (
"context"
"testing"
"github.com/stretchr/testify/require"
@ -25,7 +24,7 @@ import (
// go test -timeout 30s ./client -count 1 -run ^TestInvokeBinding$
func TestInvokeBinding(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
in := &InvokeBindingRequest{
Name: "test",
Operation: "fn",

View File

@ -355,7 +355,7 @@ func NewClientWithAddressContext(ctx context.Context, address string) (client Cl
return nil, fmt.Errorf("error parsing address '%s': %w", address, err)
}
at := &authToken{}
at := newAuthToken()
opts := []grpc.DialOption{
grpc.WithUserAgent(userAgent()),
@ -404,7 +404,7 @@ func NewClientWithSocket(socket string) (client Client, err error) {
if socket == "" {
return nil, errors.New("nil socket")
}
at := &authToken{}
at := newAuthToken()
logger.Printf("dapr client initializing for: %s", socket)
addr := "unix://" + socket
conn, err := grpc.Dial( //nolint:staticcheck
@ -421,11 +421,6 @@ func NewClientWithSocket(socket string) (client Client, err error) {
}
func newClientWithConnection(conn *grpc.ClientConn, authToken *authToken) Client {
apiToken := os.Getenv(apiTokenEnvVarName)
if apiToken != "" {
logger.Println("client uses API token")
authToken.set(apiToken)
}
return &GRPCClient{
connection: conn,
protoClient: pb.NewDaprClient(conn),
@ -435,14 +430,26 @@ func newClientWithConnection(conn *grpc.ClientConn, authToken *authToken) Client
// NewClientWithConnection instantiates Dapr client using specific connection.
func NewClientWithConnection(conn *grpc.ClientConn) Client {
return newClientWithConnection(conn, &authToken{})
return newClientWithConnection(conn, newAuthToken())
}
// NOTE: authToken must be created using newAuthToken()
// it is crucial to correctly initialize the dapr client with the API token from the environment variable
type authToken struct {
mu sync.RWMutex
authToken string
}
func newAuthToken() *authToken {
apiToken := os.Getenv(apiTokenEnvVarName)
if apiToken != "" {
logger.Println("API Token loaded from the environment variable")
}
return &authToken{
authToken: apiToken,
}
}
func (a *authToken) get() string {
a.mu.RLock()
defer a.mu.RUnlock()

View File

@ -32,6 +32,7 @@ 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/durationpb"
"google.golang.org/protobuf/types/known/emptypb"
commonv1pb "github.com/dapr/dapr/pkg/proto/common/v1"
@ -98,7 +99,7 @@ func TestNewClient(t *testing.T) {
})
t.Run("new client with trace ID", func(t *testing.T) {
_ = testClient.WithTraceID(context.Background(), "test")
_ = testClient.WithTraceID(t.Context(), "test")
})
t.Run("new socket client closed with token", func(t *testing.T) {
@ -120,13 +121,13 @@ func TestNewClient(t *testing.T) {
c, err := NewClientWithSocket(testSocket)
require.NoError(t, err)
defer c.Close()
ctx := c.WithTraceID(context.Background(), "")
ctx := c.WithTraceID(t.Context(), "")
_ = c.WithTraceID(ctx, "test")
})
}
func TestShutdown(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
t.Run("shutdown", func(t *testing.T) {
err := testClient.Shutdown(ctx)
@ -563,10 +564,11 @@ func (s *testDaprServer) ScheduleJobAlpha1(ctx context.Context, in *pb.ScheduleJ
func (s *testDaprServer) GetJobAlpha1(ctx context.Context, in *pb.GetJobRequest) (*pb.GetJobResponse, error) {
var (
schedule = "@every 10s"
dueTime = "10s"
repeats uint32 = 4
ttl = "10s"
schedule = "@every 10s"
dueTime = "10s"
repeats uint32 = 4
ttl = "10s"
maxRetries uint32 = 4
)
return &pb.GetJobResponse{
Job: &pb.Job{
@ -576,6 +578,14 @@ func (s *testDaprServer) GetJobAlpha1(ctx context.Context, in *pb.GetJobRequest)
DueTime: &dueTime,
Ttl: &ttl,
Data: nil,
FailurePolicy: &commonv1pb.JobFailurePolicy{
Policy: &commonv1pb.JobFailurePolicy_Constant{
Constant: &commonv1pb.JobFailurePolicyConstant{
MaxRetries: &maxRetries,
Interval: &durationpb.Duration{Seconds: 10},
},
},
},
},
}, nil
}

View File

@ -1,7 +1,6 @@
package client
import (
"context"
"sync/atomic"
"testing"
"time"
@ -16,7 +15,7 @@ const (
)
func TestGetConfigurationItem(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
t.Run("get configuration item", func(t *testing.T) {
resp, err := testClient.GetConfigurationItem(ctx, "example-config", "mykey")
@ -31,7 +30,7 @@ func TestGetConfigurationItem(t *testing.T) {
}
func TestGetConfigurationItems(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
keys := []string{"mykey1", "mykey2", "mykey3"}
t.Run("Test get configuration items", func(t *testing.T) {
@ -44,7 +43,7 @@ func TestGetConfigurationItems(t *testing.T) {
}
func TestSubscribeConfigurationItems(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
var counter, totalCounter uint32
counter = 0
@ -67,7 +66,7 @@ func TestSubscribeConfigurationItems(t *testing.T) {
}
func TestUnSubscribeConfigurationItems(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
var counter, totalCounter uint32
t.Run("Test unsubscribe configuration items", func(t *testing.T) {

View File

@ -101,8 +101,10 @@ func WithTemperature(temp float64) conversationRequestOption {
// 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) {
//nolint:staticcheck
cinputs := make([]*runtimev1pb.ConversationInput, len(req.inputs))
for i, in := range req.inputs {
//nolint:staticcheck
cinputs[i] = &runtimev1pb.ConversationInput{
Content: in.Content,
Role: in.Role,
@ -115,7 +117,7 @@ func (c *GRPCClient) ConverseAlpha1(ctx context.Context, req conversationRequest
opt(&req)
}
}
//nolint:staticcheck
request := runtimev1pb.ConversationRequest{
Name: req.name,
ContextID: req.ContextID,

View File

@ -30,7 +30,7 @@ import (
)
func TestEncrypt(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
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 := context.Background()
ctx := t.Context()
t.Run("missing ComponentName", func(t *testing.T) {
out, err := testClient.Decrypt(ctx,

View File

@ -14,7 +14,6 @@ limitations under the License.
package client
import (
"context"
"testing"
"github.com/stretchr/testify/require"
@ -39,7 +38,7 @@ type _testStructwithSlices struct {
}
func TestInvokeMethodWithContent(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
data := "ping"
t.Run("with content", func(t *testing.T) {

232
client/jobs.go Normal file
View File

@ -0,0 +1,232 @@
/*
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"
"errors"
"time"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/durationpb"
commonpb "github.com/dapr/dapr/pkg/proto/common/v1"
runtimepb "github.com/dapr/dapr/pkg/proto/runtime/v1"
"github.com/dapr/kit/ptr"
)
type FailurePolicy interface {
GetPBFailurePolicy() *commonpb.JobFailurePolicy
}
type JobFailurePolicyConstant struct {
MaxRetries *uint32
Interval *time.Duration
}
func (f *JobFailurePolicyConstant) GetPBFailurePolicy() *commonpb.JobFailurePolicy {
constantfp := &commonpb.JobFailurePolicyConstant{}
if f.MaxRetries != nil {
constantfp.MaxRetries = f.MaxRetries
}
if f.Interval != nil {
constantfp.Interval = durationpb.New(*f.Interval)
}
return &commonpb.JobFailurePolicy{
Policy: &commonpb.JobFailurePolicy_Constant{
Constant: constantfp,
},
}
}
type JobFailurePolicyDrop struct{}
func (f *JobFailurePolicyDrop) GetPBFailurePolicy() *commonpb.JobFailurePolicy {
return &commonpb.JobFailurePolicy{
Policy: &commonpb.JobFailurePolicy_Drop{
Drop: &commonpb.JobFailurePolicyDrop{},
},
}
}
type Job struct {
Name string
Schedule *string
Repeats *uint32
DueTime *string
TTL *string
Data *anypb.Any
FailurePolicy FailurePolicy
Overwrite bool
}
type JobOption func(*Job)
func NewJob(name string, opts ...JobOption) *Job {
job := &Job{
Name: name,
}
for _, opt := range opts {
opt(job)
}
return job
}
func WithJobSchedule(schedule string) JobOption {
return func(job *Job) {
job.Schedule = &schedule
}
}
func WithJobRepeats(repeats uint32) JobOption {
return func(job *Job) {
job.Repeats = &repeats
}
}
func WithJobDueTime(dueTime string) JobOption {
return func(job *Job) {
job.DueTime = &dueTime
}
}
func WithJobTTL(ttl string) JobOption {
return func(job *Job) {
job.TTL = &ttl
}
}
func WithJobData(data *anypb.Any) JobOption {
return func(job *Job) {
job.Data = data
}
}
func WithJobConstantFailurePolicy() JobOption {
return func(job *Job) {
job.FailurePolicy = &JobFailurePolicyConstant{}
}
}
func WithJobConstantFailurePolicyMaxRetries(maxRetries uint32) JobOption {
return func(job *Job) {
if job.FailurePolicy == nil {
job.FailurePolicy = &JobFailurePolicyConstant{}
}
if constantPolicy, ok := job.FailurePolicy.(*JobFailurePolicyConstant); ok {
constantPolicy.MaxRetries = &maxRetries
} else {
job.FailurePolicy = &JobFailurePolicyConstant{
MaxRetries: &maxRetries,
}
}
}
}
func WithJobConstantFailurePolicyInterval(interval time.Duration) JobOption {
return func(job *Job) {
if job.FailurePolicy == nil {
job.FailurePolicy = &JobFailurePolicyConstant{}
}
if constantPolicy, ok := job.FailurePolicy.(*JobFailurePolicyConstant); ok {
constantPolicy.Interval = &interval
} else {
job.FailurePolicy = &JobFailurePolicyConstant{
Interval: &interval,
}
}
}
}
func WithJobDropFailurePolicy() JobOption {
return func(job *Job) {
job.FailurePolicy = &JobFailurePolicyDrop{}
}
}
// ScheduleJobAlpha1 raises and schedules a job.
func (c *GRPCClient) ScheduleJobAlpha1(ctx context.Context, job *Job) error {
if job.Name == "" {
return errors.New("job name is required")
}
if job.Data == nil {
return errors.New("job data is required")
}
jobRequest := &runtimepb.Job{
Name: job.Name,
Data: job.Data,
Schedule: job.Schedule,
Repeats: job.Repeats,
DueTime: job.DueTime,
Ttl: job.TTL,
}
if job.FailurePolicy != nil {
jobRequest.FailurePolicy = job.FailurePolicy.GetPBFailurePolicy()
}
_, err := c.protoClient.ScheduleJobAlpha1(ctx, &runtimepb.ScheduleJobRequest{
Job: jobRequest,
Overwrite: job.Overwrite,
})
return err
}
// GetJobAlpha1 retrieves a scheduled job.
func (c *GRPCClient) GetJobAlpha1(ctx context.Context, name string) (*Job, error) {
if name == "" {
return nil, errors.New("job name is required")
}
resp, err := c.protoClient.GetJobAlpha1(ctx, &runtimepb.GetJobRequest{
Name: name,
})
if err != nil {
return nil, err
}
var failurePolicy FailurePolicy
switch policy := resp.GetJob().GetFailurePolicy().GetPolicy().(type) {
case *commonpb.JobFailurePolicy_Constant:
interval := time.Duration(policy.Constant.GetInterval().GetSeconds()) * time.Second
failurePolicy = &JobFailurePolicyConstant{
MaxRetries: ptr.Of(policy.Constant.GetMaxRetries()),
Interval: &interval,
}
case *commonpb.JobFailurePolicy_Drop:
failurePolicy = &JobFailurePolicyDrop{}
}
return &Job{
Name: resp.GetJob().GetName(),
Schedule: ptr.Of(resp.GetJob().GetSchedule()),
Repeats: ptr.Of(resp.GetJob().GetRepeats()),
DueTime: ptr.Of(resp.GetJob().GetDueTime()),
TTL: ptr.Of(resp.GetJob().GetTtl()),
Data: resp.GetJob().GetData(),
FailurePolicy: failurePolicy,
}, nil
}
// DeleteJobAlpha1 deletes a scheduled job.
func (c *GRPCClient) DeleteJobAlpha1(ctx context.Context, name string) error {
if name == "" {
return errors.New("job name is required")
}
_, err := c.protoClient.DeleteJobAlpha1(ctx, &runtimepb.DeleteJobRequest{
Name: name,
})
return err
}

119
client/jobs_test.go Normal file
View File

@ -0,0 +1,119 @@
/*
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"
"time"
"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) {
job := NewJob("test",
WithJobSchedule("test"),
WithJobData(&anypb.Any{}),
WithJobConstantFailurePolicy(),
)
err := testClient.ScheduleJobAlpha1(ctx, job)
require.NoError(t, err)
})
t.Run("get job - valid", func(t *testing.T) {
expected := NewJob("name",
WithJobSchedule("@every 10s"),
WithJobRepeats(4),
WithJobDueTime("10s"),
WithJobTTL("10s"),
WithJobConstantFailurePolicy(),
WithJobConstantFailurePolicyMaxRetries(4),
WithJobConstantFailurePolicyInterval(time.Second*10),
)
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)
})
}
func TestJobBuilder(t *testing.T) {
t.Run("basic job creation", func(t *testing.T) {
job := NewJob("test-job")
assert.Equal(t, "test-job", job.Name)
assert.Nil(t, job.Schedule)
assert.Nil(t, job.Repeats)
assert.Nil(t, job.DueTime)
assert.Nil(t, job.TTL)
assert.Nil(t, job.Data)
assert.Nil(t, job.FailurePolicy)
})
t.Run("job with all options and constant failure policy", func(t *testing.T) {
job := NewJob("full-job",
WithJobSchedule("@every 10m"),
WithJobRepeats(5),
WithJobDueTime("2024-12-31T23:59:59Z"),
WithJobTTL("2h"),
WithJobData(&anypb.Any{TypeUrl: "test", Value: []byte("test-data")}),
WithJobConstantFailurePolicy(),
WithJobConstantFailurePolicyMaxRetries(3),
WithJobConstantFailurePolicyInterval(time.Minute*2),
)
assert.Equal(t, "full-job", job.Name)
assert.Equal(t, "@every 10m", *job.Schedule)
assert.Equal(t, uint32(5), *job.Repeats)
assert.Equal(t, "2024-12-31T23:59:59Z", *job.DueTime)
assert.Equal(t, "2h", *job.TTL)
assert.Equal(t, &anypb.Any{TypeUrl: "test", Value: []byte("test-data")}, job.Data)
constantPolicy, ok := job.FailurePolicy.(*JobFailurePolicyConstant)
require.True(t, ok)
assert.Equal(t, uint32(3), *constantPolicy.MaxRetries)
assert.Equal(t, time.Minute*2, *constantPolicy.Interval)
})
t.Run("job with all options and drop failure policy", func(t *testing.T) {
job := NewJob("full-job",
WithJobSchedule("@every 10m"),
WithJobRepeats(5),
WithJobDueTime("2024-12-31T23:59:59Z"),
WithJobTTL("2h"),
WithJobData(&anypb.Any{TypeUrl: "test", Value: []byte("test-data")}),
WithJobDropFailurePolicy(),
)
assert.Equal(t, "full-job", job.Name)
assert.Equal(t, "@every 10m", *job.Schedule)
assert.Equal(t, uint32(5), *job.Repeats)
assert.Equal(t, "2024-12-31T23:59:59Z", *job.DueTime)
assert.Equal(t, "2h", *job.TTL)
assert.Equal(t, &anypb.Any{TypeUrl: "test", Value: []byte("test-data")}, job.Data)
_, ok := job.FailurePolicy.(*JobFailurePolicyDrop)
require.True(t, ok)
})
}

View File

@ -14,7 +14,6 @@ limitations under the License.
package client
import (
"context"
"testing"
"github.com/stretchr/testify/require"
@ -29,7 +28,7 @@ const (
)
func TestLock(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
t.Run("try lock invalid store name", func(t *testing.T) {
r, err := testClient.TryLockAlpha1(ctx, "", &LockRequest{})

View File

@ -1,7 +1,6 @@
package client
import (
"context"
"testing"
"github.com/stretchr/testify/require"
@ -11,7 +10,7 @@ import (
// Test GetMetadata returns
func TestGetMetadata(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
t.Run("get meta", func(t *testing.T) {
metadata, err := testClient.GetMetadata(ctx)
require.NoError(t, err)
@ -20,7 +19,7 @@ func TestGetMetadata(t *testing.T) {
}
func TestSetMetadata(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
t.Run("set meta", func(t *testing.T) {
err := testClient.SetMetadata(ctx, "test_key", "test_value")
require.NoError(t, err)

View File

@ -14,7 +14,6 @@ limitations under the License.
package client
import (
"context"
"testing"
"github.com/stretchr/testify/require"
@ -39,7 +38,7 @@ type _testCustomContentwithSlices struct {
// go test -timeout 30s ./client -count 1 -run ^TestPublishEvent$
func TestPublishEvent(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
t.Run("with data", func(t *testing.T) {
err := testClient.PublishEvent(ctx, "messages", "test", []byte("ping"))
@ -96,7 +95,7 @@ func TestPublishEvent(t *testing.T) {
// go test -timeout 30s ./client -count 1 -run ^TestPublishEvents$
func TestPublishEvents(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
t.Run("without pubsub name", func(t *testing.T) {
res := testClient.PublishEvents(ctx, "", "test", []interface{}{"ping", "pong"})

View File

@ -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
}

View File

@ -1,58 +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"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/anypb"
)
func TestSchedulingAlpha1(t *testing.T) {
ctx := context.Background()
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)
})
}

View File

@ -14,7 +14,6 @@ limitations under the License.
package client
import (
"context"
"testing"
"github.com/stretchr/testify/require"
@ -24,7 +23,7 @@ import (
// go test -timeout 30s ./client -count 1 -run ^TestGetSecret$
func TestGetSecret(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
t.Run("without store", func(t *testing.T) {
out, err := testClient.GetSecret(ctx, "", "key1", nil)
@ -53,7 +52,7 @@ func TestGetSecret(t *testing.T) {
}
func TestGetBulkSecret(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
t.Run("without store", func(t *testing.T) {
out, err := testClient.GetBulkSecret(ctx, "", nil)

View File

@ -17,10 +17,6 @@ import (
"context"
"errors"
"fmt"
"math"
"time"
"google.golang.org/protobuf/types/known/durationpb"
v1 "github.com/dapr/dapr/pkg/proto/common/v1"
pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
@ -249,22 +245,6 @@ func copyStateOptionDefault() *StateOptions {
}
}
func toProtoDuration(d time.Duration) *durationpb.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{
Seconds: secs,
Nanos: int32(nanos), //nolint:gosec
}
}
// ExecuteStateTransaction provides way to execute multiple operations on a specified store.
func (c *GRPCClient) ExecuteStateTransaction(ctx context.Context, storeName string, meta map[string]string, ops []*StateOperation) error {
if storeName == "" {

View File

@ -14,11 +14,11 @@ limitations under the License.
package client
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/durationpb"
"github.com/stretchr/testify/assert"
@ -59,7 +59,7 @@ func TestTypes(t *testing.T) {
func TestDurationConverter(t *testing.T) {
d := 10 * time.Second
pd := toProtoDuration(d)
pd := durationpb.New(d)
assert.NotNil(t, pd)
assert.Equal(t, int64(10), pd.GetSeconds())
}
@ -77,7 +77,7 @@ func TestStateOptionsConverter(t *testing.T) {
// go test -timeout 30s ./client -count 1 -run ^TestSaveState$
func TestSaveState(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
data := testData
store := testStore
key := "key1"
@ -118,7 +118,7 @@ func TestSaveState(t *testing.T) {
// go test -timeout 30s ./client -count 1 -run ^TestDeleteState$
func TestDeleteState(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
data := testData
store := testStore
key := "key1"
@ -189,7 +189,7 @@ func TestDeleteState(t *testing.T) {
}
func TestDeleteBulkState(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
data := testData
store := testStore
keys := []string{"key1", "key2", "key3"}
@ -337,7 +337,7 @@ func TestDeleteBulkState(t *testing.T) {
// go test -timeout 30s ./client -count 1 -run ^TestStateTransactions$
func TestStateTransactions(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
data := `{ "message": "test" }`
store := testStore
meta := map[string]string{}
@ -410,7 +410,7 @@ func TestStateTransactions(t *testing.T) {
}
func TestQueryState(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
data := testData
store := testStore
key1 := "key1"

View File

@ -13,7 +13,9 @@ limitations under the License.
package client
import "encoding/json"
import (
"encoding/json"
)
// isCloudEvent returns true if the event is a CloudEvent.
// An event is a CloudEvent if it `id`, `source`, `specversion` and `type` fields.

View File

@ -116,14 +116,14 @@ func createNonBlockingClient(ctx context.Context, serverAddr string) (client Cli
}
func TestGrpcWaitHappyCase(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
err := testClient.Wait(ctx, waitTimeout)
require.NoError(t, err)
}
func TestGrpcWaitUnresponsiveTcpServer(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
server, err := createUnresponsiveTCPServer()
require.NoError(t, err)
@ -141,7 +141,7 @@ func TestGrpcWaitUnresponsiveTcpServer(t *testing.T) {
}
func TestGrpcWaitUnresponsiveUnixServer(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
server, err := createUnresponsiveUnixServer()
require.NoError(t, err)

View File

@ -15,7 +15,6 @@ limitations under the License.
package client
import (
"context"
"math"
"testing"
@ -35,7 +34,7 @@ func TestMarshalInput(t *testing.T) {
}
func TestWorkflowBeta1(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
// 1: StartWorkflow
t.Run("start workflow - valid (without id)", func(t *testing.T) {

View File

@ -12,12 +12,12 @@ When contributing to the [Go SDK](https://github.com/dapr/go-sdk) the following
The `examples` directory contains code samples for users to run to try out specific functionality of the various Go SDK packages and extensions. When writing new and updated samples keep in mind:
- All examples should be runnable on Windows, Linux, and MacOS. While Go code is consistent among operating systems, any pre/post example commands should provide options through [codetabs]({{< ref "contributing-docs.md#tabbed-content" >}})
- All examples should be runnable on Windows, Linux, and MacOS. While Go code is consistent among operating systems, any pre/post example commands should provide options through [tabpane]({{% ref "contributing-docs.md#tabbed-content" %}})
- Contain steps to download/install any required pre-requisites. Someone coming in with a fresh OS install should be able to start on the example and complete it without an error. Links to external download pages are fine.
## Docs
The `daprdocs` directory contains the markdown files that are rendered into the [Dapr Docs](https://docs.dapr.io) website. When the documentation website is built this repo is cloned and configured so that its contents are rendered with the docs content. When writing docs keep in mind:
- All rules in the [docs guide]({{< ref contributing-docs.md >}}) should be followed in addition to these.
- All rules in the [docs guide]({{% ref contributing-docs.md %}}) should be followed in addition to these.
- All files and directories should be prefixed with `go-` to ensure all file/directory names are globally unique across all Dapr documentation.

View File

@ -18,11 +18,11 @@ A client library to help build Dapr applications in Go. This client supports all
{{< card title="**Client**">}}
Use the Go Client SDK for invoking public Dapr APIs
[**Learn more about the Go Client SDK**]({{< ref go-client >}})
[**Learn more about the Go Client SDK**]({{% ref go-client %}})
{{< /card >}}
{{< card title="**Service**">}}
Use the Dapr Service (Callback) SDK for Go to create services that will be invoked by Dapr.
[**Learn more about the Go Service (Callback) SDK**]({{< ref go-service >}})
[**Learn more about the Go Service (Callback) SDK**]({{% ref go-service %}})
{{< /card >}}
{{< /cardpane >}}

View File

@ -11,17 +11,17 @@ The Dapr client package allows you to interact with other Dapr applications from
## Prerequisites
- [Dapr CLI]({{< ref install-dapr-cli.md >}}) installed
- Initialized [Dapr environment]({{< ref install-dapr-selfhost.md >}})
- [Dapr CLI]({{% ref install-dapr-cli.md %}}) installed
- Initialized [Dapr environment]({{% ref install-dapr-selfhost.md %}})
- [Go installed](https://golang.org/doc/install)
## Import the client package
## Import the client package
```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).
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
@ -54,7 +54,7 @@ if err != nil {
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)
@ -65,7 +65,7 @@ if err != nil {
## Building blocks
The Go SDK allows you to interface with all of the [Dapr building blocks]({{< ref building-blocks >}}).
The Go SDK allows you to interface with all of the [Dapr building blocks]({{% ref building-blocks %}}).
### Service Invocation
@ -86,7 +86,7 @@ content := &dapr.DataContent{
resp, err = client.InvokeMethodWithContent(ctx, "app-id", "method-name", "post", content)
```
For a full guide on service invocation, visit [How-To: Invoke a service]({{< ref howto-invoke-discover-services.md >}}).
For a full guide on service invocation, visit [How-To: Invoke a service]({{% ref howto-invoke-discover-services.md %}}).
### Workflows
@ -102,14 +102,14 @@ import (
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
}
@ -118,7 +118,7 @@ func ExampleActivity(ctx workflow.ActivityContext) (any, error) {
if err := ctx.GetInput(&input); err != nil {
return "", err
}
return fmt.Sprintf("hello %s", input), nil
}
@ -128,47 +128,47 @@ func main() {
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 >}}).
- [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)
@ -180,7 +180,7 @@ For simple use-cases, Dapr client provides easy to use `Save`, `Get`, `Delete` m
```go
ctx := context.Background()
data := []byte("hello")
store := "my-store" // defined in the component YAML
store := "my-store" // defined in the component YAML
// save state with the key key1, default options: strong, last-write
if err := client.SaveState(ctx, store, "key1", data, nil); err != nil {
@ -269,7 +269,7 @@ meta := map[string]string{}
err := testClient.ExecuteStateTransaction(ctx, store, meta, ops)
```
Retrieve, filter, and sort key/value data stored in your statestore using `QueryState`.
Retrieve, filter, and sort key/value data stored in your statestore using `QueryState`.
```go
// Define the query string
@ -306,7 +306,7 @@ for _, account := range queryResponse {
> **Note:** Query state API is currently in alpha
For a full guide on state management, visit [How-To: Save & get state]({{< ref howto-get-save-state.md >}}).
For a full guide on state management, visit [How-To: Save & get state]({{% ref howto-get-save-state.md %}}).
### Publish Messages
To publish data onto a topic, the Dapr Go client provides a simple method:
@ -328,11 +328,11 @@ if res.Error != nil {
}
```
For a full guide on pub/sub, visit [How-To: Publish & subscribe]({{< ref howto-publish-subscribe.md >}}).
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:
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) {
@ -340,7 +340,7 @@ func TestActivity(ctx workflow.ActivityContext) (any, error) {
if err := ctx.GetInput(&input); err != nil {
return "", err
}
// Do something here
return "result", nil
}
@ -361,7 +361,7 @@ func TestWorkflow(ctx *workflow.WorkflowContext) (any, error) {
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
}
@ -369,10 +369,113 @@ func TestWorkflow(ctx *workflow.WorkflowContext) (any, error) {
}
```
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.
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)
### Jobs
The Dapr client Go SDK allows you to schedule, get, and delete jobs. Jobs enable you to schedule work to be executed at specific times or intervals.
#### Scheduling a Job
To schedule a new job, use the `ScheduleJobAlpha1` method:
```go
import (
"google.golang.org/protobuf/types/known/anypb"
)
// Create job data
data, err := anypb.New(&YourDataStruct{Message: "Hello, Job!"})
if err != nil {
panic(err)
}
// Create a simple job using the builder pattern
job := client.NewJob("my-scheduled-job",
client.WithJobData(data),
client.WithJobDueTime("10s"), // Execute in 10 seconds
)
// Schedule the job
err = client.ScheduleJobAlpha1(ctx, job)
if err != nil {
panic(err)
}
```
#### Job with Schedule and Repeats
You can create recurring jobs using the `Schedule` field with cron expressions:
```go
job := client.NewJob("recurring-job",
client.WithJobData(data),
client.WithJobSchedule("0 9 * * *"), // Run at 9 AM every day
client.WithJobRepeats(10), // Repeat 10 times
client.WithJobTTL("1h"), // Job expires after 1 hour
)
err = client.ScheduleJobAlpha1(ctx, job)
```
#### Job with Failure Policy
Configure how jobs should handle failures using failure policies:
```go
// Constant retry policy with max retries and interval
job := client.NewJob("resilient-job",
client.WithJobData(data),
client.WithJobDueTime("2024-01-01T10:00:00Z"),
client.WithJobConstantFailurePolicy(),
client.WithJobConstantFailurePolicyMaxRetries(3),
client.WithJobConstantFailurePolicyInterval(30*time.Second),
)
err = client.ScheduleJobAlpha1(ctx, job)
```
For jobs that should not be retried on failure, use the drop policy:
```go
job := client.NewJob("one-shot-job",
client.WithJobData(data),
client.WithJobDueTime("2024-01-01T10:00:00Z"),
client.WithJobDropFailurePolicy(),
)
err = client.ScheduleJobAlpha1(ctx, job)
```
#### Getting a Job
To get information about a scheduled job:
```go
job, err := client.GetJobAlpha1(ctx, "my-scheduled-job")
if err != nil {
panic(err)
}
fmt.Printf("Job: %s, Schedule: %s, Repeats: %d\n",
job.Name, job.Schedule, job.Repeats)
```
#### Deleting a Job
To cancel a scheduled job:
```go
err = client.DeleteJobAlpha1(ctx, "my-scheduled-job")
if err != nil {
panic(err)
}
```
For a full guide on jobs, visit [How-To: Schedule and manage jobs]({{< ref howto-schedule-and-handle-triggered-jobs.md >}}).
### Output Bindings
@ -398,7 +501,7 @@ in := &dapr.InvokeBindingRequest{
out, err := client.InvokeBinding(ctx, in)
```
For a full guide on output bindings, visit [How-To: Use bindings]({{< ref howto-bindings.md >}}).
For a full guide on output bindings, visit [How-To: Use bindings]({{% ref howto-bindings.md %}}).
### Actors
@ -460,7 +563,7 @@ func main() {
}
```
For a full guide on actors, visit [the Actors building block documentation]({{< ref actors >}}).
For a full guide on actors, visit [the Actors building block documentation]({{% ref actors %}}).
### Secret Management
@ -498,7 +601,7 @@ func main() {
```
For a full guide on secrets, visit [How-To: Retrieve secrets]({{< ref howto-secrets.md >}}).
For a full guide on secrets, visit [How-To: Retrieve secrets]({{% ref howto-secrets.md %}}).
### Distributed Lock
@ -522,7 +625,7 @@ func main() {
panic(err)
}
defer client.Close()
resp, err := client.TryLockAlpha1(ctx, "lockstore", &dapr.LockRequest{
LockOwner: "random_id_abc123",
ResourceID: "my_file_name",
@ -533,7 +636,7 @@ func main() {
}
```
For a full guide on distributed lock, visit [How-To: Use a lock]({{< ref howto-use-distributed-lock.md >}}).
For a full guide on distributed lock, visit [How-To: Use a lock]({{% ref howto-use-distributed-lock.md %}}).
### Configuration
@ -564,7 +667,7 @@ go func() {
}()
```
For a full guide on configuration, visit [How-To: Manage configuration from a store]({{< ref howto-manage-configuration.md >}}).
For a full guide on configuration, visit [How-To: Manage configuration from a store]({{% ref howto-manage-configuration.md %}}).
### Cryptography
@ -595,7 +698,7 @@ 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 >}}).
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)

View File

@ -7,5 +7,5 @@ description: How to get up and running with the Dapr Service (Callback) SDK for
no_list: true
---
In addition to this Dapr API client, Dapr Go SDK also provides service package to bootstrap your Dapr callback services. These services can be developed in either gRPC or HTTP:
- [HTTP Service]({{< ref http-service.md >}})
- [gRPC Service]({{< ref grpc-service.md >}})
- [HTTP Service]({{% ref http-service.md %}})
- [gRPC Service]({{% ref grpc-service.md %}})

View File

@ -1,6 +1,6 @@
module github.com/dapr/go-sdk/examples
go 1.23.6
go 1.24.4
replace github.com/dapr/go-sdk => ../
@ -9,32 +9,32 @@ require (
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.70.0
google.golang.org/grpc v1.73.0
google.golang.org/grpc/examples v0.0.0-20240516203910-e22436abb809
google.golang.org/protobuf v1.36.4
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.0-rc.17 // indirect
github.com/dapr/durabletask-go v0.6.3 // indirect
github.com/dapr/kit v0.15.0 // indirect
github.com/dapr/dapr v1.16.0-rc.3 // indirect
github.com/dapr/durabletask-go v0.7.3-0.20250711135247-7a35af6fe0e5 // indirect
github.com/dapr/kit v0.15.4 // 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-chi/chi/v5 v5.2.2 // indirect
github.com/go-logr/logr v1.4.3 // 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.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 // indirect
go.opentelemetry.io/otel v1.37.0 // indirect
go.opentelemetry.io/otel/metric v1.37.0 // indirect
go.opentelemetry.io/otel/trace v1.37.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 // indirect
)

View File

@ -6,25 +6,25 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3
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.0-rc.17 h1:bR0rd4FH81IteuOHTWVNyl58ZuQTDp3DYaTtXnpZ6JA=
github.com/dapr/dapr v1.15.0-rc.17/go.mod h1:SD0AXom2XpX7pr8eYlbJ+gHfNREsflsrzCR19AZJ7/Q=
github.com/dapr/durabletask-go v0.6.3 h1:WHhSAw1YL4xneK3Jo5nGfmMaJxfFodIIF5q1rpkDDfs=
github.com/dapr/durabletask-go v0.6.3/go.mod h1:nTZ5fCbJLnZbVdi6Z2YxdDF1OgQZL3LroogGuetrwuA=
github.com/dapr/kit v0.15.0 h1:446jrEOQV/0rt6FwmoKrifP3vav5+Uh/u38DqU8q+JM=
github.com/dapr/kit v0.15.0/go.mod h1:HwFsBKEbcyLanWlDZE7u/jnaDCD/tU+n3pkFNUctQNw=
github.com/dapr/dapr v1.16.0-rc.3 h1:D99V20GOhb+bZXH1PngME+wgzIZCcBFOvmaP7DOZxGo=
github.com/dapr/dapr v1.16.0-rc.3/go.mod h1:uyKnxMohSg87LSFzZ/oyuiGSo0+qkzeR0eXncPyIV9c=
github.com/dapr/durabletask-go v0.7.3-0.20250711135247-7a35af6fe0e5 h1:l8oBGwcfCwqvSYDZwla0A2fhENmXFc1Wk4lR0VEq+is=
github.com/dapr/durabletask-go v0.7.3-0.20250711135247-7a35af6fe0e5/go.mod h1:0Ts4rXp74JyG19gDWPcwNo5V6NBZzhARzHF5XynmA7Q=
github.com/dapr/kit v0.15.4 h1:29DezCR22OuZhXX4yPEc+lqcOf/PNaeAuIEx9nGv394=
github.com/dapr/kit v0.15.4/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.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
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/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.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
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/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/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=
@ -33,8 +33,8 @@ 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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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=
@ -63,33 +63,33 @@ github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8
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.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc=
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
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.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
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.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 h1:J1H9f+LEdWAfHcez/4cvaVBox7cOYT+IU6rgqj5x++8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
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.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
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.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
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=
@ -101,5 +101,5 @@ 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-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg=
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=

View File

@ -1,4 +1,4 @@
# Dapr Distributed Scheduler Example with go-sdk
# Dapr Jobs Example with go-sdk
## Steps
@ -6,10 +6,10 @@
- Dapr installed (v1.14 or higher)
### Run Distributed Scheduling Example
### Run Jobs Example
<!-- STEP
name: Run Distributed Scheduling Example
name: Run Jobs Example
output_match_mode: substring
expected_stdout_lines:
- 'Scheduler stream connected'
@ -20,7 +20,7 @@ expected_stdout_lines:
- '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\"}}"}'
- 'getjob - resp: Name: prod-db-backup, Schedule: @every 1s, Repeats: 10, DueTime: , TTL: , Data: value:"{\"task\":\"db-backup\",\"metadata\":{\"db_name\":\"my-prod-db\",\"backup_location\":\"/backup-dir\"}}"'
- 'deletejob - success'
background: true
@ -29,7 +29,7 @@ sleep: 30
-->
```bash
dapr run --app-id=distributed-scheduler \
dapr run --app-id=jobs \
--metrics-port=9091 \
--scheduler-host-address=localhost:50006 \
--dapr-grpc-port 50001 \

View File

@ -10,7 +10,7 @@ import (
"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/examples/jobs/api"
"github.com/dapr/go-sdk/service/common"
daprs "github.com/dapr/go-sdk/service/grpc"
)
@ -49,14 +49,16 @@ func main() {
panic(err)
}
job := daprc.Job{
Name: "prod-db-backup",
Schedule: "@every 1s",
Repeats: 10,
Data: &anypb.Any{
job := daprc.NewJob("prod-db-backup",
daprc.WithJobSchedule("@every 1s"),
daprc.WithJobRepeats(10),
daprc.WithJobData(&anypb.Any{
Value: jobData,
},
}
}),
daprc.WithJobConstantFailurePolicy(),
daprc.WithJobConstantFailurePolicyMaxRetries(4),
daprc.WithJobConstantFailurePolicyInterval(time.Second*30),
)
// create the client
client, err := daprc.NewClient()
@ -65,7 +67,7 @@ func main() {
}
defer client.Close()
err = client.ScheduleJobAlpha1(ctx, &job)
err = client.ScheduleJobAlpha1(ctx, job)
if err != nil {
panic(err)
}
@ -78,7 +80,7 @@ func main() {
if err != nil {
panic(err)
}
fmt.Printf("getjob - resp: %v\n", resp) // parse
fmt.Printf("getjob - resp: Name: %s, Schedule: %s, Repeats: %d, DueTime: %s, TTL: %s, Data: %v\n", resp.Name, *resp.Schedule, *resp.Repeats, *resp.DueTime, *resp.TTL, resp.Data) // parse
err = client.DeleteJobAlpha1(ctx, "prod-db-backup")
if err != nil {

View File

@ -16,7 +16,7 @@ output_match_mode: substring
expected_stdout_lines:
- "ContentType:text/plain, Verb:POST, QueryString:, hellow"
background: true
sleep: 15
sleep: 30
timeout_seconds: 60
-->

View File

@ -0,0 +1,61 @@
# Dapr Parallel Workflow Example with go-sdk
## Step
### Prepare
- Dapr installed
### Run Workflow
<!-- STEP
name: Run Workflow
output_match_mode: substring
expected_stdout_lines:
- '== APP == Workflow(s) and activities registered.'
- 'work item listener started'
- '== APP == RetryN 1'
- '== APP == RetryN 2'
- '== APP == RetryN 3'
- '== APP == RetryN 4'
- '== APP == RetryN 1'
- '== APP == RetryN 2'
- '== APP == RetryN 3'
- '== APP == RetryN 4'
- '== APP == workflow status: COMPLETED'
- '== APP == workflow terminated'
- '== APP == workflow purged'
background: true
sleep: 30
timeout_seconds: 60
-->
```bash
dapr run --app-id workflow-taskexecutionid \
--dapr-grpc-port 50001 \
--log-level debug \
--resources-path ./config \
-- go run ./main.go
```
<!-- END_STEP -->
## Result
```
- '== APP == Workflow(s) and activities registered.'
- 'work item listener started'
- '== APP == RetryN 1'
- '== APP == RetryN 2'
- '== APP == RetryN 3'
- '== APP == RetryN 4'
- '== APP == RetryN 1'
- '== APP == RetryN 2'
- '== APP == RetryN 3'
- '== APP == RetryN 4'
- '== APP == workflow status: COMPLETED'
- '== APP == workflow terminated'
- '== APP == workflow purged'
```

View File

@ -0,0 +1,14 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: wf-store
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
- name: actorStateStore
value: "true"

View File

@ -0,0 +1,108 @@
package main
import (
"context"
"fmt"
"log"
"sync"
"sync/atomic"
"time"
"github.com/dapr/go-sdk/workflow"
)
func main() {
w, err := workflow.NewWorker()
if err != nil {
log.Fatalf("failed to initialise worker: %v", err)
}
if err := w.RegisterWorkflow(TaskExecutionIdWorkflow); err != nil {
log.Fatalf("failed to register workflow: %v", err)
}
if err := w.RegisterActivity(RetryN); err != nil {
log.Fatalf("failed to register activity: %v", err)
}
fmt.Println("Workflow(s) and activities registered.")
if err := w.Start(); err != nil {
log.Fatalf("failed to start worker")
}
wfClient, err := workflow.NewClient()
if err != nil {
log.Fatalf("failed to initialise client: %v", err)
}
ctx := context.Background()
id, err := wfClient.ScheduleNewWorkflow(ctx, "TaskExecutionIdWorkflow", workflow.WithInput(5))
if err != nil {
log.Fatalf("failed to schedule a new workflow: %v", err)
}
metadata, err := wfClient.WaitForWorkflowCompletion(ctx, id)
if err != nil {
log.Fatalf("failed to get workflow: %v", err)
}
fmt.Printf("workflow status: %s\n", metadata.RuntimeStatus.String())
err = wfClient.TerminateWorkflow(ctx, id)
if err != nil {
log.Fatalf("failed to terminate workflow: %v", err)
}
fmt.Println("workflow terminated")
err = wfClient.PurgeWorkflow(ctx, id)
if err != nil {
log.Fatalf("failed to purge workflow: %v", err)
}
fmt.Println("workflow purged")
}
var eMap = sync.Map{}
func TaskExecutionIdWorkflow(ctx *workflow.WorkflowContext) (any, error) {
var retries int
if err := ctx.GetInput(&retries); err != nil {
return 0, err
}
var workBatch []int
if err := ctx.CallActivity(RetryN, workflow.ActivityRetryPolicy(workflow.RetryPolicy{
MaxAttempts: retries,
InitialRetryInterval: 100 * time.Millisecond,
BackoffCoefficient: 2,
MaxRetryInterval: 1 * time.Second,
}), workflow.ActivityInput(retries)).Await(&workBatch); err != nil {
return 0, err
}
if err := ctx.CallActivity(RetryN, workflow.ActivityRetryPolicy(workflow.RetryPolicy{
MaxAttempts: retries,
InitialRetryInterval: 100 * time.Millisecond,
BackoffCoefficient: 2,
MaxRetryInterval: 1 * time.Second,
}), workflow.ActivityInput(retries)).Await(&workBatch); err != nil {
return 0, err
}
return 0, nil
}
func RetryN(ctx workflow.ActivityContext) (any, error) {
taskExecutionID := ctx.GetTaskExecutionID()
counter, _ := eMap.LoadOrStore(taskExecutionID, &atomic.Int32{})
var retries int32
if err := ctx.GetInput(&retries); err != nil {
return 0, err
}
counter.(*atomic.Int32).Add(1)
fmt.Println("RetryN ", counter.(*atomic.Int32).Load())
if counter.(*atomic.Int32).Load() < retries-1 {
return nil, fmt.Errorf("failed")
}
return nil, nil
}

View File

@ -64,6 +64,10 @@ func main() {
ctx := context.Background()
// Start workflow test
// Set the start time to the current time to not wait for the workflow to
// "start". This is useful for increasing the throughput of creating
// workflows.
// workflow.WithStartTime(time.Now())
instanceID, err := wfClient.ScheduleNewWorkflow(ctx, "TestWorkflow", workflow.WithInstanceID("a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9"), workflow.WithInput(1))
if err != nil {
log.Fatalf("failed to start workflow: %v", err)

31
go.mod
View File

@ -1,35 +1,34 @@
module github.com/dapr/go-sdk
go 1.23.6
go 1.24.4
require (
github.com/dapr/dapr v1.15.0-rc.17
github.com/dapr/durabletask-go v0.6.3
github.com/go-chi/chi/v5 v5.1.0
github.com/dapr/dapr v1.16.0-rc.3
github.com/dapr/durabletask-go v0.7.3-0.20250711135247-7a35af6fe0e5
github.com/dapr/kit v0.15.4
github.com/go-chi/chi/v5 v5.2.2
github.com/golang/mock v1.6.0
github.com/google/uuid v1.6.0
github.com/stretchr/testify v1.10.0
google.golang.org/grpc v1.70.0
google.golang.org/protobuf v1.36.4
google.golang.org/grpc v1.73.0
google.golang.org/protobuf v1.36.6
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/dapr/kit v0.15.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c // indirect
golang.org/x/net v0.36.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 // indirect
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 // indirect
)

72
go.sum
View File

@ -1,17 +1,17 @@
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/dapr/dapr v1.15.0-rc.17 h1:bR0rd4FH81IteuOHTWVNyl58ZuQTDp3DYaTtXnpZ6JA=
github.com/dapr/dapr v1.15.0-rc.17/go.mod h1:SD0AXom2XpX7pr8eYlbJ+gHfNREsflsrzCR19AZJ7/Q=
github.com/dapr/durabletask-go v0.6.3 h1:WHhSAw1YL4xneK3Jo5nGfmMaJxfFodIIF5q1rpkDDfs=
github.com/dapr/durabletask-go v0.6.3/go.mod h1:nTZ5fCbJLnZbVdi6Z2YxdDF1OgQZL3LroogGuetrwuA=
github.com/dapr/kit v0.15.0 h1:446jrEOQV/0rt6FwmoKrifP3vav5+Uh/u38DqU8q+JM=
github.com/dapr/kit v0.15.0/go.mod h1:HwFsBKEbcyLanWlDZE7u/jnaDCD/tU+n3pkFNUctQNw=
github.com/dapr/dapr v1.16.0-rc.3 h1:D99V20GOhb+bZXH1PngME+wgzIZCcBFOvmaP7DOZxGo=
github.com/dapr/dapr v1.16.0-rc.3/go.mod h1:uyKnxMohSg87LSFzZ/oyuiGSo0+qkzeR0eXncPyIV9c=
github.com/dapr/durabletask-go v0.7.3-0.20250711135247-7a35af6fe0e5 h1:l8oBGwcfCwqvSYDZwla0A2fhENmXFc1Wk4lR0VEq+is=
github.com/dapr/durabletask-go v0.7.3-0.20250711135247-7a35af6fe0e5/go.mod h1:0Ts4rXp74JyG19gDWPcwNo5V6NBZzhARzHF5XynmA7Q=
github.com/dapr/kit v0.15.4 h1:29DezCR22OuZhXX4yPEc+lqcOf/PNaeAuIEx9nGv394=
github.com/dapr/kit v0.15.4/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/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-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
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=
@ -21,8 +21,8 @@ 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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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=
@ -43,26 +43,26 @@ github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
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.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
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.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc=
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
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/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -71,30 +71,30 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
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.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
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.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 h1:J1H9f+LEdWAfHcez/4cvaVBox7cOYT+IU6rgqj5x++8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
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/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-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg=
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=

View File

@ -40,7 +40,7 @@ func TestListInputBindings(t *testing.T) {
require.NoError(t, err)
err = server.AddBindingInvocationHandler("test2", testBindingHandler)
require.NoError(t, err)
resp, err := server.ListInputBindings(context.Background(), &emptypb.Empty{})
resp, err := server.ListInputBindings(t.Context(), &emptypb.Empty{})
require.NoError(t, err)
assert.NotNil(t, resp)
assert.Lenf(t, resp.GetBindings(), 2, "expected 2 handlers")
@ -57,7 +57,7 @@ func TestBindingForErrors(t *testing.T) {
// go test -timeout 30s ./service/grpc -count 1 -run ^TestBinding$
func TestBinding(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
methodName := "test"
server := getTestServer()

View File

@ -37,7 +37,7 @@ func TestHealthCheckHandlerForErrors(t *testing.T) {
// go test -timeout 30s ./service/grpc -count 1 -run ^TestHealthCheck$
func TestHealthCheck(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
server := getTestServer()
startTestServer(server)

View File

@ -67,21 +67,21 @@ func TestInvokeWithToken(t *testing.T) {
grpcMetadata := metadata.New(map[string]string{
cc.APITokenKey: os.Getenv(cc.AppAPITokenEnvVar),
})
ctx := metadata.NewIncomingContext(context.Background(), grpcMetadata)
ctx := metadata.NewIncomingContext(t.Context(), grpcMetadata)
in := &common.InvokeRequest{Method: methodName}
_, err := server.OnInvoke(ctx, in)
require.NoError(t, err)
})
t.Run("invoke with empty token, return failed", func(t *testing.T) {
in := &common.InvokeRequest{Method: methodName}
_, err := server.OnInvoke(context.Background(), in)
_, err := server.OnInvoke(t.Context(), in)
require.Error(t, err)
})
t.Run("invoke with mismatch token, return failed", func(t *testing.T) {
grpcMetadata := metadata.New(map[string]string{
cc.APITokenKey: "mismatch-token",
})
ctx := metadata.NewOutgoingContext(context.Background(), grpcMetadata)
ctx := metadata.NewOutgoingContext(t.Context(), grpcMetadata)
in := &common.InvokeRequest{Method: methodName}
_, err := server.OnInvoke(ctx, in)
require.Error(t, err)
@ -93,7 +93,7 @@ func TestInvokeWithToken(t *testing.T) {
func TestInvoke(t *testing.T) {
methodName := "test"
methodNameWithError := "error"
ctx := context.Background()
ctx := t.Context()
server := getTestServer()
err := server.AddServiceInvocationHandler("/"+methodName, testInvokeHandler)

View File

@ -57,7 +57,7 @@ func TestTopicSubscriptionList(t *testing.T) {
}
err := server.AddTopicEventHandler(sub1, eventHandler)
require.NoError(t, err)
resp, err := server.ListTopicSubscriptions(context.Background(), &emptypb.Empty{})
resp, err := server.ListTopicSubscriptions(t.Context(), &emptypb.Empty{})
require.NoError(t, err)
assert.NotNil(t, resp)
if assert.Lenf(t, resp.GetSubscriptions(), 1, "expected 1 handlers") {
@ -76,7 +76,7 @@ func TestTopicSubscriptionList(t *testing.T) {
}
err = server.AddTopicEventHandler(sub2, eventHandler)
require.NoError(t, err)
resp, err = server.ListTopicSubscriptions(context.Background(), &emptypb.Empty{})
resp, err = server.ListTopicSubscriptions(t.Context(), &emptypb.Empty{})
require.NoError(t, err)
assert.NotNil(t, resp)
if assert.Lenf(t, resp.GetSubscriptions(), 1, "expected 1 handlers") {
@ -96,7 +96,7 @@ func TestTopicSubscriptionList(t *testing.T) {
// go test -timeout 30s ./service/grpc -count 1 -run ^TestTopic$
func TestTopic(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
sub := &common.Subscription{
PubsubName: "messages",
@ -158,7 +158,7 @@ func TestTopic(t *testing.T) {
Topic: sub2.Topic,
PubsubName: sub2.PubsubName,
}
ctx := metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{"Metadata.key1": "value1"}))
ctx := metadata.NewIncomingContext(t.Context(), metadata.New(map[string]string{"Metadata.key1": "value1"}))
_, err = server.OnTopicEvent(ctx, in)
require.NoError(t, err)
})
@ -167,7 +167,7 @@ func TestTopic(t *testing.T) {
}
func TestTopicWithValidationDisabled(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
sub := &common.Subscription{
PubsubName: "messages",
@ -197,7 +197,7 @@ func TestTopicWithValidationDisabled(t *testing.T) {
}
func TestTopicWithErrors(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
sub1 := &common.Subscription{
PubsubName: "messages",
@ -269,7 +269,7 @@ func eventHandlerWithError(ctx context.Context, event *common.TopicEvent) (retry
}
func TestEventDataHandling(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
tests := map[string]struct {
contentType string

View File

@ -28,7 +28,7 @@ func TestParseWorkflow(t *testing.T) {
t.Run("parse testing workflow file", func(t *testing.T) {
parsedVersion, err := parseWorkflowVersionFromFile("../../.github/workflows/test-tooling.yml")
assert.Equal(t, "v1.61.0", parsedVersion)
assert.Equal(t, "v1.64.6", parsedVersion)
require.NoError(t, err)
})
}
@ -36,7 +36,7 @@ func TestParseWorkflow(t *testing.T) {
func TestGetCurrentVersion(t *testing.T) {
t.Run("get current version from system", func(t *testing.T) {
currentVersion, err := getCurrentVersion()
assert.Equal(t, "v1.61.0", currentVersion)
assert.Equal(t, "v1.64.6", currentVersion)
require.NoError(t, err)
})
@ -49,23 +49,23 @@ func TestGetCurrentVersion(t *testing.T) {
func TestIsVersionValid(t *testing.T) {
t.Run("compare versions - exactly equal to", func(t *testing.T) {
assert.True(t, true, isVersionValid("v1.54.2", "v1.54.2"))
assert.True(t, isVersionValid("v1.54.2", "v1.54.2"))
})
t.Run("compare versions - patch version greater (workflow)", func(t *testing.T) {
assert.True(t, true, isVersionValid("v1.54.3", "v1.54.2"))
assert.True(t, isVersionValid("v1.54.3", "v1.54.2"))
})
t.Run("compare versions - patch version greater (installed)", func(t *testing.T) {
assert.True(t, true, isVersionValid("v1.54.2", "v1.54.3"))
assert.True(t, isVersionValid("v1.54.2", "v1.54.3"))
})
t.Run("compare versions - invalid (installed)", func(t *testing.T) {
assert.False(t, false, isVersionValid("v1.54.2", "v1.52.2"))
assert.False(t, isVersionValid("v1.54.2", "v1.52.2"))
})
t.Run("compare versions - invalid (workflow)", func(t *testing.T) {
assert.False(t, false, isVersionValid("v1.52.2", "v1.54.2"))
assert.False(t, isVersionValid("v1.52.2", "v1.54.2"))
})
}

View File

@ -1 +1 @@
v1.12.0
v1.13.0

View File

@ -36,6 +36,10 @@ func (wfac *ActivityContext) Context() context.Context {
return wfac.ctx.Context()
}
func (wfac *ActivityContext) GetTaskExecutionID() string {
return wfac.ctx.GetTaskExecutionID()
}
type callActivityOption func(*callActivityOptions) error
type callActivityOptions struct {

View File

@ -28,7 +28,17 @@ import (
)
type testingTaskActivityContext struct {
inputBytes []byte
inputBytes []byte
ctx context.Context
taskExecutionID string
}
func (t *testingTaskActivityContext) GetTaskID() int32 {
return 0
}
func (t *testingTaskActivityContext) GetTaskExecutionID() string {
return t.taskExecutionID
}
func (t *testingTaskActivityContext) GetInput(v any) error {
@ -36,7 +46,7 @@ func (t *testingTaskActivityContext) GetInput(v any) error {
}
func (t *testingTaskActivityContext) Context() context.Context {
return context.TODO()
return t.ctx
}
func TestActivityContext(t *testing.T) {
@ -44,7 +54,7 @@ func TestActivityContext(t *testing.T) {
inputBytes, err := json.Marshal(inputString)
require.NoErrorf(t, err, "required no error, but got %v", err)
ac := ActivityContext{ctx: &testingTaskActivityContext{inputBytes: inputBytes}}
ac := ActivityContext{ctx: &testingTaskActivityContext{inputBytes: inputBytes, ctx: t.Context()}}
t.Run("test getinput", func(t *testing.T) {
var inputReturn string
err := ac.GetInput(&inputReturn)
@ -53,7 +63,7 @@ func TestActivityContext(t *testing.T) {
})
t.Run("test context", func(t *testing.T) {
assert.Equal(t, context.TODO(), ac.Context())
assert.Equal(t, t.Context(), ac.Context())
})
}
@ -118,3 +128,19 @@ func TestMarshalData(t *testing.T) {
assert.Equal(t, []byte{0x22, 0x74, 0x65, 0x73, 0x74, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22}, out)
})
}
func TestTaskExecutionID(t *testing.T) {
ac := ActivityContext{ctx: &testingTaskActivityContext{ctx: t.Context(), taskExecutionID: "testTaskExecutionID"}}
t.Run("test getTaskExecutionID", func(t *testing.T) {
assert.Equal(t, "testTaskExecutionID", ac.GetTaskExecutionID())
})
}
func TestTaskID(t *testing.T) {
ac := ActivityContext{ctx: &testingTaskActivityContext{ctx: t.Context(), taskExecutionID: "testTaskExecutionID"}}
t.Run("test getTaskID", func(t *testing.T) {
assert.EqualValues(t, 0, ac.ctx.GetTaskID())
})
}

View File

@ -65,7 +65,10 @@ func WithRawInput(input string) api.NewOrchestrationOptions {
return api.WithRawInput(wrapperspb.String(input))
}
// WithStartTime is an option to set the start time when scheduling a new workflow.
// WithStartTime is an option to set the start time when scheduling a new
// workflow. Setting this option will prevent Dapr from "waiting" for the
// Workflow to start, meaning that it can improve workflow creation throughput.
// Meaning setting this value to `time.Now()` can be useful.
func WithStartTime(time time.Time) api.NewOrchestrationOptions {
return api.WithStartTime(time)
}

View File

@ -15,7 +15,6 @@ limitations under the License.
package workflow
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
@ -52,7 +51,7 @@ func TestClientMethods(t *testing.T) {
testClient := Client{
taskHubClient: nil,
}
ctx := context.Background()
ctx := t.Context()
t.Run("ScheduleNewWorkflow - empty wf name", func(t *testing.T) {
id, err := testClient.ScheduleNewWorkflow(ctx, "", WithReuseIDPolicy(WorkflowIDReusePolicy{
OperationStatus: []Status{StatusCompleted},

View File

@ -94,7 +94,16 @@ func (wfc *WorkflowContext) CallChildWorkflow(workflow interface{}, opts ...call
// The value passed to the Await method must be a pointer or can be nil to ignore the returned value.
// Alternatively, tasks can be awaited using the task.WhenAll or task.WhenAny methods, allowing the workflow
// to block and wait for multiple tasks at the same time.
func (wfc *WorkflowContext) CreateTimer(duration time.Duration) task.Task {
func (wfc *WorkflowContext) CreateTimer(duration time.Duration, opts ...createTimerOption) task.Task {
options := new(createTimerOptions)
for _, configure := range opts {
if err := configure(options); err != nil {
return nil
}
}
if options.name != nil {
return wfc.orchestrationContext.CreateTimer(duration, task.WithTimerName(*options.name))
}
return wfc.orchestrationContext.CreateTimer(duration)
}

View File

@ -64,22 +64,29 @@ func NewWorker(opts ...workerOption) (*WorkflowWorker, error) {
return nil, errors.New("failed to load options")
}
}
var daprClient dapr.Client
var err error
if options.daprClient == nil {
daprClient, err = dapr.NewClient()
} else {
var (
daprClient dapr.Client
err error
closeFunc = func() {}
)
if options.daprClient != nil {
daprClient = options.daprClient
} else {
if daprClient, err = dapr.NewClient(); err != nil {
return nil, err
}
closeFunc = daprClient.Close
}
if err != nil {
return nil, err
}
grpcConn := daprClient.GrpcClientConn()
return &WorkflowWorker{
tasks: task.NewTaskRegistry(),
client: durabletaskclient.NewTaskHubGrpcClient(grpcConn, backend.DefaultLogger()),
close: daprClient.Close,
close: closeFunc,
}, nil
}

View File

@ -148,3 +148,16 @@ func NewTaskSlice(length int) []task.Task {
taskSlice := make([]task.Task, length)
return taskSlice
}
type createTimerOption func(*createTimerOptions) error
type createTimerOptions struct {
name *string
}
func WithTimerName(name string) createTimerOption {
return func(opt *createTimerOptions) error {
opt.name = &name
return nil
}
}

View File

@ -5,6 +5,7 @@ import (
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/dapr/durabletask-go/api/protos"
"github.com/dapr/durabletask-go/task"
@ -76,3 +77,21 @@ func TestNewTaskSlice(t *testing.T) {
tasks := NewTaskSlice(10)
assert.Len(t, tasks, 10)
}
func TestCreateTimerOptions(t *testing.T) {
t.Run("create timer options - valid", func(t *testing.T) {
opts := returnCreateTimerOptions(WithTimerName("test"))
require.NotNil(t, opts.name)
require.Equal(t, "test", *opts.name)
})
}
func returnCreateTimerOptions(opts ...createTimerOption) createTimerOptions {
options := new(createTimerOptions)
for _, configure := range opts {
if err := configure(options); err != nil {
return *options
}
}
return *options
}