Compare commits

...

30 Commits

Author SHA1 Message Date
Javier Aliaga 16374cdb7b
chore: Use latest dapr/dapr version (#731)
* chore: Use latest dapr/dapr version

Signed-off-by: Javier Aliaga <javier@diagrid.io>

* chore: Bump golangci version

Signed-off-by: Javier Aliaga <javier@diagrid.io>

* chore: Bump golangci-lint version

Signed-off-by: Javier Aliaga <javier@diagrid.io>

* chore: Fix lint errors on tooling

Signed-off-by: Javier Aliaga <javier@diagrid.io>

* chore: Increase sleep to service example

Signed-off-by: Javier Aliaga <javier@diagrid.io>

---------

Signed-off-by: Javier Aliaga <javier@diagrid.io>
2025-06-03 05:59:33 -07:00
dependabot[bot] a3df75f17b
chore(deps): bump golang.org/x/net from 0.34.0 to 0.36.0 (#710)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.34.0 to 0.36.0.
- [Commits](https://github.com/golang/net/compare/v0.34.0...v0.36.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-13 10:09:46 +08:00
Mike Nguyen 81312e9da9
merge release-1.12 into main (#703)
* Adding the name of the failing activity. For more detail (#678)

Signed-off-by: arturo <jarturotrenard@gmail.com>

* add deadlettertopic support to non-streaming subscriptions (#685)

* add deadlettertopic support to non-streaming subscriptions

Signed-off-by: yaron2 <schneider.yaron@live.com>

* fix tests

Signed-off-by: yaron2 <schneider.yaron@live.com>

---------

Signed-off-by: yaron2 <schneider.yaron@live.com>

* Pick #674 and bump to rc13 (#686)

* Bump gover, tag, x/deps and dapr (#674)

* release: bump to rc2

Signed-off-by: mikeee <hey@mike.ee>

* chore: upgrade x/net and x/crypto

Signed-off-by: mikeee <hey@mike.ee>

* release: bump go to 1.23.5 and dapr to rc5

Signed-off-by: mikeee <hey@mike.ee>

* ci: bump validation workflow versions

Signed-off-by: mikeee <hey@mike.ee>

* bump cli and runtime to latest rc

Signed-off-by: Mike Nguyen <hey@mike.ee>

* chore: bump dapr to rc7 and dt-go to head

Signed-off-by: Mike Nguyen <hey@mike.ee>

* chore: bump to rc8

Signed-off-by: mikeee <hey@mike.ee>

* chore(release): bump to latest

Signed-off-by: mikeee <hey@mike.ee>

---------

Signed-off-by: mikeee <hey@mike.ee>
Signed-off-by: Mike Nguyen <hey@mike.ee>

* chore: bump cli and runtime vers

Signed-off-by: Mike Nguyen <hey@mike.ee>

* chore: bump to rc13

Signed-off-by: Mike Nguyen <hey@mike.ee>

---------

Signed-off-by: mikeee <hey@mike.ee>
Signed-off-by: Mike Nguyen <hey@mike.ee>

* feat: reconnect stream when grpc code is unknown / unavailable (#692)

* feat: reconnect stream when grpc code is unknown / unavailable

Signed-off-by: Eileen Yu <eileenylj@gmail.com>

* log error for closing stream

Signed-off-by: Eileen Yu <eileenylj@gmail.com>

---------

Signed-off-by: Eileen Yu <eileenylj@gmail.com>

* ci: include pre-release label for RCs (#675)

* ci: include pre-release label for RCs

Signed-off-by: Mike Nguyen <hey@mike.ee>

* ci: enumerate if statements

Signed-off-by: Mike Nguyen <hey@mike.ee>

---------

Signed-off-by: Mike Nguyen <hey@mike.ee>

* docs(sdk): add basic workflow example (#691)

* docs(sdk): add basic workflow example

Signed-off-by: Mike Nguyen <hey@mike.ee>

* docs: fix indentations

Signed-off-by: Mike Nguyen <hey@mike.ee>

---------

Signed-off-by: Mike Nguyen <hey@mike.ee>

* update conversation api field name (#695)

Signed-off-by: yaron2 <schneider.yaron@live.com>

* fix(examples): update deprecated flag (#689)

* fix(examples): update deprecated flag

Signed-off-by: Mike Nguyen <hey@mike.ee>

* ci: test cli PR

Signed-off-by: Mike Nguyen <hey@mike.ee>

* test(service): bump body size to 41Mi

Signed-off-by: Mike Nguyen <hey@mike.ee>

* chore: bump cli to rc6 and runtime to rc16

Signed-off-by: Mike Nguyen <hey@mike.ee>

---------

Signed-off-by: Mike Nguyen <hey@mike.ee>
Co-authored-by: Yaron Schneider <schneider.yaron@live.com>

* release: v1.12.0 version (#700)

Signed-off-by: Mike Nguyen <hey@mike.ee>

* ci: revert rc tests (#701)

Signed-off-by: Mike Nguyen <hey@mike.ee>

---------

Signed-off-by: arturo <jarturotrenard@gmail.com>
Signed-off-by: yaron2 <schneider.yaron@live.com>
Signed-off-by: mikeee <hey@mike.ee>
Signed-off-by: Mike Nguyen <hey@mike.ee>
Signed-off-by: Eileen Yu <eileenylj@gmail.com>
Co-authored-by: Arturo Trenard <jarturotrenard@gmail.com>
Co-authored-by: Yaron Schneider <schneider.yaron@live.com>
Co-authored-by: Eileen Yu <48944635+Eileen-Yu@users.noreply.github.com>
2025-02-28 12:30:46 -08:00
Mike Nguyen c81a381811
Bump gover, tag, x/deps and dapr (#674)
* release: bump to rc2

Signed-off-by: mikeee <hey@mike.ee>

* chore: upgrade x/net and x/crypto

Signed-off-by: mikeee <hey@mike.ee>

* release: bump go to 1.23.5 and dapr to rc5

Signed-off-by: mikeee <hey@mike.ee>

* ci: bump validation workflow versions

Signed-off-by: mikeee <hey@mike.ee>

* bump cli and runtime to latest rc

Signed-off-by: Mike Nguyen <hey@mike.ee>

* chore: bump dapr to rc7 and dt-go to head

Signed-off-by: Mike Nguyen <hey@mike.ee>

* chore: bump to rc8

Signed-off-by: mikeee <hey@mike.ee>

* chore(release): bump to latest

Signed-off-by: mikeee <hey@mike.ee>

---------

Signed-off-by: mikeee <hey@mike.ee>
Signed-off-by: Mike Nguyen <hey@mike.ee>
2025-02-03 15:02:44 -08:00
Mike Nguyen aded0b64d7
release: version bump and tidy (#673)
Signed-off-by: mikeee <hey@mike.ee>
2025-01-15 10:50:38 -08:00
Yaron Schneider 34fd54eb81
Add trace headers to http subscriptions (#671)
* add trace headers to http subscriptions

Signed-off-by: yaron2 <schneider.yaron@live.com>

* fix json

Signed-off-by: yaron2 <schneider.yaron@live.com>

---------

Signed-off-by: yaron2 <schneider.yaron@live.com>
2025-01-13 13:12:39 -08:00
Mike Nguyen c97fd6f30d
chore: bump validation dapr runtime to 1.15.0-rc.2 (#667)
* chore: bump validation dapr runtime to 1.15.0-rc.2

Signed-off-by: Mike Nguyen <hey@mike.ee>

* fix: replace dead api

Signed-off-by: Mike Nguyen <hey@mike.ee>

* fix: remove duetime and period

The scheduler reminders subsystem now being the default

Signed-off-by: Mike Nguyen <hey@mike.ee>

---------

Signed-off-by: Mike Nguyen <hey@mike.ee>
2025-01-13 08:37:02 -08:00
Fabian Martinez 2ab3420adc
update durabletask ref (#666)
* update durabletask ref

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

* lint

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

---------

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>
2025-01-03 01:30:30 -08:00
Diego Cardoso 495a389ff0
Update Dependencies - fix sec vulnerability (#658)
* update dependencies

* update dependencies
2024-12-13 08:44:16 -08:00
Hannah Hunter 7c63bb9ae1
add workflow to client doc (#657)
Signed-off-by: Hannah Hunter <hannahhunter@microsoft.com>
2024-12-09 17:06:46 -08:00
Michael Collins 31346f0d54
Introduce interface for topic event subscriber (#661)
* Introduce interface for topic event subscriber

I introduced the TopicEventSubscriber interface to allow for
subscribers to be implemented as structs. This will allow subscribers
to be initialized with any settings or dependencies needed to process
incoming events.

I converted TopicEventHandler to implement TopicEventSubscriber. I
revised TopicRegistrar to store TopicEventSubscribers instead of
TopicEventHandlers.

Resolves #660

Signed-off-by: Michael Collins <mfcollins3@me.com>

* Update go-service documentation

I added examples of how to use the TopicEventSubscriber interface to
create a new subscriber for both HTTP and gRPC services.

Signed-off-by: Michael Collins <mfcollins3@me.com>

---------

Signed-off-by: Michael Collins <mfcollins3@me.com>
2024-12-09 08:54:56 -08:00
Fabian Martinez 921a6a79c5
update durabletask to use fork and child workflow retries (#656)
* update durabletask to use fork and child workflow retries

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

* lint

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

---------

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>
2024-12-04 07:12:56 -08:00
dependabot[bot] 282a58bc9c
chore(deps): bump github.com/dapr/dapr (#655)
Bumps [github.com/dapr/dapr](https://github.com/dapr/dapr) from 1.14.5-0.20241120233620-c86a77f6db5f to 1.15.0-rc.1.
- [Release notes](https://github.com/dapr/dapr/releases)
- [Changelog](https://github.com/dapr/dapr/blob/master/RELEASE.md)
- [Commits](https://github.com/dapr/dapr/commits/v1.15.0-rc.1)

---
updated-dependencies:
- dependency-name: github.com/dapr/dapr
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-28 12:58:38 -08:00
Mike Nguyen dce63f1917
Add support for the conversation API (#646)
* feat: conversation api implementation

Signed-off-by: mikeee <hey@mike.ee>

* chore: deps for conversation api

Signed-off-by: mikeee <hey@mike.ee>

* fix: cleanup convo example

Signed-off-by: mikeee <hey@mike.ee>

* refactor: add a conversationrequest builder and docs

Signed-off-by: mikeee <hey@mike.ee>

* fix: lint and refactor, adding preallocations for ins/outs

Signed-off-by: Mike Nguyen <hey@mike.ee>

* fix: lint and imports

Signed-off-by: mikeee <hey@mike.ee>

* fix: bump to dapr master refs

Signed-off-by: mikeee <hey@mike.ee>

* fix: enable scheduler with cli fix + tidy

Signed-off-by: mikeee <hey@mike.ee>

---------

Signed-off-by: mikeee <hey@mike.ee>
Signed-off-by: Mike Nguyen <hey@mike.ee>
2024-11-27 07:57:22 -08:00
Mike Nguyen e52d60c714
ci: remove runtime version override (#642)
* ci: remove runtime version override

Signed-off-by: mikeee <hey@mike.ee>

* fix: remove base64 decode for jobs api example

Signed-off-by: mikeee <hey@mike.ee>

* fix(tests): update jobs api test

Signed-off-by: mikeee <hey@mike.ee>

---------

Signed-off-by: mikeee <hey@mike.ee>
Co-authored-by: Yaron Schneider <schneider.yaron@live.com>
2024-11-20 08:09:32 -08:00
Fabian Martinez 87659bf63e
add deprecation comments (#650)
Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>
2024-11-20 08:04:26 -08:00
Fabian Martinez c12c9594c4
worflows: activity retry policy (#644)
* worflows: activity retry policy

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

* adjust name

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

* fix build

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

* add tests

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

* register activity

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

---------

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>
Co-authored-by: Mike Nguyen <hey@mike.ee>
2024-11-13 22:02:27 -07:00
Mike Nguyen 59acca46a6
chore!: upgrade lint & go (#649)
* chore: bump go to 1.23.3 and golangci-lint to 1.61.0

Signed-off-by: mikeee <hey@mike.ee>

* fix: ineffective nolint directive removed

Signed-off-by: Mike Nguyen <hey@mike.ee>

* fix: perfsprint recommendations

Signed-off-by: Mike Nguyen <hey@mike.ee>

* fix: intrange recommendations - refactor loops

Signed-off-by: Mike Nguyen <hey@mike.ee>

* fix: remove existing dereferencing copies

Signed-off-by: Mike Nguyen <hey@mike.ee>

* fix!: address gosec overflows

BREAKING CHANGE: State consistency, concurrency and operation types are now int32 sized.
Panic on an overflow conversion for a proto duration

Signed-off-by: Mike Nguyen <hey@mike.ee>

* fix: tooling lint issues/ci update

Signed-off-by: Mike Nguyen <hey@mike.ee>

* fix: perfsprint suggestions on dapr-bot

Signed-off-by: Mike Nguyen <hey@mike.ee>

* chore(ci): remove gover remnants

Signed-off-by: Mike Nguyen <hey@mike.ee>

* chore(ci): upgrade golangci-lint action version

Signed-off-by: Mike Nguyen <hey@mike.ee>

---------

Signed-off-by: mikeee <hey@mike.ee>
Signed-off-by: Mike Nguyen <hey@mike.ee>
2024-11-13 22:01:59 -07:00
Mike Nguyen 3fff121af7
ci: only test using go.mod version (#647)
Signed-off-by: mikeee <hey@mike.ee>
2024-11-05 11:19:31 -08:00
Fabian Martinez dd9a2d5a3c
workflow examples: remove use of deprecated functions (#640)
* workflow examples: remove use of deprecated functions

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

* fix example tests

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

* Update README.md

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

* Update Makefile

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

---------

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>
2024-10-30 09:06:32 -07:00
Fabian Martinez 516684c202
workflows: support set custom status (#639)
Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>
2024-10-30 09:06:20 -07:00
Fabian Martinez e317f06e65
add purge options (#638)
* add options to PurgeWorkflow

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

* Update client.go

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

---------

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>
2024-10-30 09:06:07 -07:00
Fabian Martinez 4953b123ad
workflows support reuse id policy (#637)
* workflows support reuse id policy

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

* add new type

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

* lint

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

* wrap all types

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

* use existing type

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>

---------

Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>
2024-10-30 09:05:50 -07:00
Gowtham H N a074ea7428
Fixed superfluous bug (#619)
Signed-off-by: Gowtham H N <44719307+GowthamHN@users.noreply.github.com>
2024-10-28 10:51:07 -07:00
Yaron Schneider f9baae2539
Merge pull request #641 from mikeee/release-1.11
merge Release 1.11  to main
2024-10-22 20:29:11 -07:00
Yaron Schneider 6c59092f53
Change refs to 1.14.1 (#615)
* test 1.14.1-rc.1

Signed-off-by: yaron2 <schneider.yaron@live.com>

* update to 1.14.1

Signed-off-by: yaron2 <schneider.yaron@live.com>

---------

Signed-off-by: yaron2 <schneider.yaron@live.com>
2024-08-15 08:51:44 -07:00
Mike Nguyen f87f366fe7
Merge pull request #612 from mikeee/dapr1.14
chore: upgrade dapr to v1.14.0
2024-08-14 16:47:13 +01:00
mikeee 2749284a00
ci: remove version override
Signed-off-by: mikeee <hey@mike.ee>
2024-08-14 06:46:09 +01:00
mikeee 2cfb6e308e
chore: upgrade dapr to v1.14.0
Signed-off-by: mikeee <hey@mike.ee>
2024-08-14 06:36:43 +01:00
Mike Nguyen d1f04ee738
chore: bump dapr & cli to rc.8 to test (#606)
Signed-off-by: mikeee <hey@mike.ee>
2024-08-08 13:02:14 -07:00
87 changed files with 1255 additions and 634 deletions

View File

@ -49,9 +49,9 @@ func (b *Bot) HandleEvent(ctx context.Context, event Event) (res string, err err
switch command {
case "/assign":
assignee, err := b.AssignIssueToCommenter(event)
res = fmt.Sprintf("👍 Issue assigned to %s", assignee)
res = "👍 Issue assigned to " + assignee
if err == nil {
err = b.CreateIssueComment(fmt.Sprintf("🚀 Issue assigned to you @%s", assignee), event)
err = b.CreateIssueComment("🚀 Issue assigned to you @"+assignee, event)
} else {
err = b.CreateIssueComment("⚠️ Unable to assign issue", event)
}

View File

@ -1,8 +1,6 @@
module github.com/dapr/go-sdk/.github/workflows/dapr-bot
go 1.22
toolchain go1.22.0
go 1.23.3
require (
github.com/google/go-github/v55 v55.0.0

View File

@ -38,8 +38,9 @@ jobs:
run: |
echo "RELEASE_VERSION=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV
- name: Release
- name: Release Main
uses: actions/create-release@v1
if: ${{ !contains(github.ref , 'rc') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
@ -49,6 +50,18 @@ jobs:
draft: false
prerelease: false
- 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: Notify
uses: rjstone/discord-webhook-notify@v1
with:

View File

@ -17,8 +17,7 @@ jobs:
name: Test
runs-on: ubuntu-latest
env:
GOVER: ${{ matrix.gover }}
GOLANGCILINT_VER: v1.55.2
GOLANGCILINT_VER: v1.64.6
steps:
- name: Checkout
@ -38,7 +37,7 @@ jobs:
run: make test
- name: Lint
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v6
with:
version: ${{ env.GOLANGCILINT_VER }}
working-directory: ./.github/workflows/dapr-bot

View File

@ -8,17 +8,10 @@ on:
jobs:
build:
name: Test on ${{ matrix.gover }}
name: Test
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
gover:
- "1.21"
- "1.22"
env:
GOVER: ${{ matrix.gover }}
GOLANGCILINT_VER: v1.55.2
GOLANGCILINT_VER: v1.64.6
steps:
- name: Checkout
@ -29,7 +22,7 @@ jobs:
- name: Setup
uses: actions/setup-go@v5
with:
go-version: ${{ env.GOVER }}
go-version-file: 'go.mod'
- name: Tidy
run: make tidy

View File

@ -19,17 +19,13 @@ jobs:
strategy:
fail-fast: false
matrix:
gover:
- "1.21"
- "1.22"
os:
- "ubuntu-latest"
- "windows-latest"
- "macos-latest"
runs-on: ${{ matrix.os }}
env:
GOVER: ${{ matrix.gover }}
GOLANGCILINT_VER: v1.55.2 # 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
@ -38,7 +34,7 @@ jobs:
- name: Setup
uses: actions/setup-go@v5
with:
go-version: ${{ env.GOVER }}
go-version-file: ./tools/check-lint-version/go.mod
- name: Tidy
working-directory: ./tools/check-lint-version

View File

@ -38,9 +38,9 @@ jobs:
CHECKOUT_REF: ${{ github.ref }}
outputs:
DAPR_INSTALL_URL: ${{ env.DAPR_INSTALL_URL }}
DAPR_CLI_VER: 1.14.0-rc.6
DAPR_CLI_VER: ${{ steps.outputs.outputs.DAPR_CLI_VER }}
DAPR_CLI_REF: ${{ steps.outputs.outputs.DAPR_CLI_REF }}
DAPR_RUNTIME_VER: 1.14.0-rc.4
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 }}
@ -164,6 +164,7 @@ jobs:
[
"actor",
"configuration",
"conversation",
"crypto",
"dist-scheduler",
"grpc-service",

View File

@ -4,7 +4,7 @@ run:
concurrency: 4
# timeout for analysis, e.g. 30s, 5m, default is 1m
deadline: 10m
timeout: 15m
# exit code when at least one issue was found, default is 1
issues-exit-code: 1
@ -13,31 +13,35 @@ run:
tests: true
# list of build tags, all linters use it. Default is empty list.
#build-tags:
# - mytag
# 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$
skip-dirs:
- ^pkg.*client.*clientset.*versioned.*
- ^pkg.*client.*informers.*externalversions.*
- ^pkg.*proto.*
build-tags:
- unit
- allcomponents
- subtlecrypto
# 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:
# 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:
- ^pkg.*client.*clientset.*versioned.*
- ^pkg.*client.*informers.*externalversions.*
- ^pkg.*proto.*
# output configuration options
output:
# colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number"
format: tab
formats:
- format: tab
# print lines of code with issue, default is true
print-issued-lines: true
@ -57,23 +61,19 @@ linters-settings:
# default is false: such cases aren't reported by default.
check-blank: false
# [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.*
exclude-functions:
- 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,28 +86,12 @@ linters-settings:
# enable or disable analyzers by name
enable:
- atomicalign
enable-all: false
disable:
- shadow
enable-all: false
disable-all: false
revive:
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
# minimal confidence for issues, default is 0.8
confidence: 0.8
gofmt:
# simplify code: gofmt with `-s` option, true by default
simplify: true
@ -121,9 +105,6 @@ 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
@ -152,10 +133,9 @@ 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. (Do not specify a locale value)
# Default is to use a neutral variety of English.
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
# locale:
# locale: default
ignore-words:
- someword
lll:
@ -164,12 +144,6 @@ linters-settings:
line-length: 120
# tab width in spaces. Default to 1.
tab-width: 1
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
@ -187,7 +161,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:
@ -235,17 +209,20 @@ 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.
force-case-trailing-whitespace: 0
# 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
- nosnakecase
- musttag
- dupl
- errcheck
- funlen
@ -254,26 +231,16 @@ 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
@ -287,7 +254,6 @@ linters:
- varnamelen
- errorlint
- forcetypeassert
- ifshort
- maintidx
- nilnil
- predeclared
@ -300,14 +266,10 @@ linters:
- asasalint
- rowserrcheck
- sqlclosecheck
- structcheck
- varcheck
- deadcode
- golint
- inamedparam
issues:
exclude-rules:
- path: .*_test.go
linters:
- godot
- tagalign
- mnd
- canonicalheader
- err113
- fatcontext
- forbidigo # TODO: Re-enable and remove fmt.println

View File

@ -1,6 +1,6 @@
RELEASE_VERSION =v1.0.0-rc-3
GDOC_PORT =8888
GO_COMPAT_VERSION=1.21
GO_COMPAT_VERSION=1.22
.PHONY: all
all: help
@ -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

@ -37,11 +37,11 @@ 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 := 0; i < serverImplBaseCtxType.NumMethod(); i++ {
for i := range serverImplBaseCtxType.NumMethod() {
ignoredActorMethods = append(ignoredActorMethods, serverImplBaseCtxType.Method(i).Name)
}
ReminderCallType := reflect.TypeOf((*actor.ReminderCallee)(nil)).Elem()
for i := 0; i < ReminderCallType.NumMethod(); i++ {
for i := range ReminderCallType.NumMethod() {
ignoredActorMethods = append(ignoredActorMethods, ReminderCallType.Method(i).Name)
}
}
@ -265,7 +265,7 @@ 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 := 0; m < typ.NumMethod(); m++ {
for m := range typ.NumMethod() {
method := typ.Method(m)
// skip methods from ServerImplBaseCtx struct and ServerContext and ReminderCallee interfaces.
if slices.Contains(ignoredActorMethods, method.Name) {

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

@ -46,7 +46,6 @@ 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))
})

View File

@ -289,7 +289,7 @@ func (c *GRPCClient) implActor(actor actor.Client, serializer codec.Codec) {
}
numField := valueOfActor.NumField()
for i := 0; i < numField; i++ {
for i := range numField {
t := typeOfActor.Field(i)
methodName := t.Name
if methodName == "Type" {
@ -312,7 +312,7 @@ func (c *GRPCClient) implActor(actor actor.Client, serializer codec.Codec) {
}
funcOuts := make([]reflect.Type, outNum)
for i := 0; i < outNum; i++ {
for i := range outNum {
funcOuts[i] = t.Type.Out(i)
}

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

@ -230,24 +230,38 @@ type Client interface {
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.
@ -259,6 +273,9 @@ type Client interface {
// 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

View File

@ -98,7 +98,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 +120,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)
@ -466,7 +466,7 @@ func (s *testDaprServer) SubscribeConfiguration(in *pb.SubscribeConfigurationReq
return err
}
for i := 0; i < 5; i++ {
for range 5 {
select {
case <-stopCh:
return 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) {

146
client/conversation.go Normal file
View File

@ -0,0 +1,146 @@
/*
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
}

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

@ -27,7 +27,6 @@ type Parsed struct {
TLS bool
}
//nolint:revive
func ParseGRPCEndpoint(endpoint string) (Parsed, error) {
target := endpoint
if len(target) == 0 {

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) {

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

@ -14,7 +14,6 @@ limitations under the License.
package client
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
@ -23,7 +22,7 @@ import (
)
func TestSchedulingAlpha1(t *testing.T) {
ctx := context.Background()
ctx := t.Context()
t.Run("schedule job - valid", func(t *testing.T) {
err := testClient.ScheduleJobAlpha1(ctx, &Job{

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,6 +17,7 @@ import (
"context"
"errors"
"fmt"
"math"
"time"
"google.golang.org/protobuf/types/known/durationpb"
@ -65,11 +66,11 @@ const (
type (
// StateConsistency is the consistency enum type.
StateConsistency int
StateConsistency int32
// StateConcurrency is the concurrency enum type.
StateConcurrency int
StateConcurrency int32
// OperationType is the operation enum type.
OperationType int
OperationType int32
)
// GetPBConsistency get consistency pb value.
@ -252,9 +253,15 @@ 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),
Nanos: int32(nanos), //nolint:gosec
}
}
@ -484,7 +491,7 @@ func (c *GRPCClient) DeleteBulkState(ctx context.Context, storeName string, keys
}
items := make([]*DeleteStateItem, 0, len(keys))
for i := 0; i < len(keys); i++ {
for i := range keys {
item := &DeleteStateItem{
Key: keys[i],
Metadata: meta,
@ -502,7 +509,7 @@ func (c *GRPCClient) DeleteBulkStateItems(ctx context.Context, storeName string,
}
states := make([]*v1.StateItem, 0, len(items))
for i := 0; i < len(items); i++ {
for i := range items {
item := items[i]
if err := hasRequiredStateArgs(storeName, item.Key); err != nil {
return fmt.Errorf("missing required arguments: %w", err)

View File

@ -14,7 +14,6 @@ limitations under the License.
package client
import (
"context"
"testing"
"time"
@ -77,7 +76,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 +117,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 +188,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 +336,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 +409,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

@ -23,6 +23,9 @@ import (
"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"
)
@ -37,10 +40,15 @@ type SubscriptionOptions struct {
}
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 {
@ -55,7 +63,10 @@ func (c *GRPCClient) Subscribe(ctx context.Context, opts SubscriptionOptions) (*
}
s := &Subscription{
stream: stream,
ctx: ctx,
stream: stream,
createStream: c.subscribeInitialRequest,
opts: opts,
}
return s, nil
@ -101,52 +112,96 @@ func (s *Subscription) Close() error {
}
func (s *Subscription) Receive() (*SubscriptionMessage, error) {
resp, err := s.stream.Recv()
if err != nil {
return nil, err
}
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())
for {
resp, err := s.stream.Recv()
if err != nil {
select {
case <-s.ctx.Done():
return nil, errors.New("subscription context closed")
default:
if strings.HasPrefix(mediaType, "application/") &&
strings.HasSuffix(mediaType, "+json") {
// 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(),
}
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
return &SubscriptionMessage{
sub: s,
TopicEvent: topicEvent,
}, nil
}
}
func (s *SubscriptionMessage) Success() error {
@ -232,3 +287,13 @@ func (c *GRPCClient) subscribeInitialRequest(ctx context.Context, opts Subscript
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
}

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

@ -88,6 +88,91 @@ 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:
@ -245,6 +330,49 @@ 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

View File

@ -83,6 +83,35 @@ 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:

View File

@ -78,6 +78,35 @@ 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:

View File

@ -17,8 +17,8 @@ expected_stdout_lines:
- '== APP == get post request = laurence'
- '== APP == get req = hello'
- '== APP == get req = hello'
- '== APP == receive reminder = testReminderName state = "hello" duetime = 5s period = 5s'
- '== APP == receive reminder = testReminderName state = "hello" duetime = 5s period = 5s'
- '== APP == receive reminder = testReminderName state = "hello"'
- '== APP == receive reminder = testReminderName state = "hello"'
background: true
sleep: 30
timeout_seconds: 60
@ -99,6 +99,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" duetime = 5s period = 5s
== APP == receive reminder = testReminderName state = "hello" duetime = 5s period = 5s
== APP == receive reminder = testReminderName state = "hello"
== APP == receive reminder = testReminderName state = "hello"
```

View File

@ -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), "duetime = ", dueTime, "period = ", period)
fmt.Println("receive reminder = ", reminderName, " state = ", string(state))
}
func main() {

View File

@ -0,0 +1,36 @@
# 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'
```

View File

@ -0,0 +1,7 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: echo
spec:
type: conversation.echo
version: v1

View File

@ -0,0 +1,49 @@
/*
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)
}

View File

@ -15,11 +15,11 @@ expected_stdout_lines:
- 'Scheduler stream connected'
- 'schedulejob - success'
- 'job 0 received'
- 'extracted payload: {db-backup {my-prod-db /backup-dir}}'
- 'payload: {db-backup {my-prod-db /backup-dir}}'
- 'job 1 received'
- 'extracted payload: {db-backup {my-prod-db /backup-dir}}'
- 'payload: {db-backup {my-prod-db /backup-dir}}'
- 'job 2 received'
- 'extracted payload: {db-backup {my-prod-db /backup-dir}}'
- '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'

View File

@ -2,7 +2,6 @@ package main
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"log"
@ -96,19 +95,11 @@ func main() {
var jobCount = 0
func prodDBBackupHandler(ctx context.Context, job *common.JobEvent) error {
var jobData common.Job
if err := json.Unmarshal(job.Data, &jobData); err != nil {
return fmt.Errorf("failed to unmarshal job: %v", err)
}
decodedPayload, err := base64.StdEncoding.DecodeString(jobData.Value)
if err != nil {
return fmt.Errorf("failed to decode job payload: %v", err)
}
var jobPayload api.DBBackup
if err := json.Unmarshal(decodedPayload, &jobPayload); err != nil {
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 typeurl: %v\n value: %v\n extracted payload: %v\n", jobCount, job.JobType, jobData.TypeURL, jobData.Value, jobPayload)
fmt.Printf("job %d received:\n type: %v \n payload: %v\n", jobCount, job.JobType, jobPayload)
jobCount++
return nil
}

View File

@ -1,6 +1,6 @@
module github.com/dapr/go-sdk/examples
go 1.22.5
go 1.24.2
replace github.com/dapr/go-sdk => ../
@ -9,31 +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.65.0
google.golang.org/grpc v1.72.0
google.golang.org/grpc/examples v0.0.0-20240516203910-e22436abb809
google.golang.org/protobuf v1.34.2
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.14.0-rc.5 // 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/golang/protobuf v1.5.4 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/marusama/semaphore/v2 v2.5.0 // indirect
github.com/microsoft/durabletask-go v0.5.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
go.opentelemetry.io/otel v1.27.0 // indirect
go.opentelemetry.io/otel/metric v1.27.0 // indirect
go.opentelemetry.io/otel/trace v1.27.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d // 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
)

View File

@ -6,16 +6,20 @@ 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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/dapr/dapr v1.14.0-rc.5 h1:oTZPcT5fwda6bCMxrfenem6tOyeqW1nastxTwWInBCY=
github.com/dapr/dapr v1.14.0-rc.5/go.mod h1:IQWNthXF/I+qqlW4I0T+F4hCu74eKon4vjhpNvoBl8A=
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.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
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=
@ -29,58 +33,63 @@ 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=
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/marusama/semaphore/v2 v2.5.0 h1:o/1QJD9DBYOWRnDhPwDVAXQn6mQYD0gZaS1Tpx6DJGM=
github.com/marusama/semaphore/v2 v2.5.0/go.mod h1:z9nMiNUekt/LTpTUQdpp+4sJeYqUGpwMHfW0Z8V8fnQ=
github.com/microsoft/durabletask-go v0.5.0 h1:4DWBgg05wnkV/VwakaiPqZ4cARvATP74ZQJFcXVMC18=
github.com/microsoft/durabletask-go v0.5.0/go.mod h1:goe2gmMgLptCijMDQ7JsekaR86KjPUG64V9JDXvKBhE=
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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
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.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg=
go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ=
go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik=
go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak=
go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw=
go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d h1:k3zyW3BYYR30e8v3x0bTDdE9vpYFjZHK+HcyqkrppWk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
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.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
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=
@ -89,5 +98,8 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
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=

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
-->
@ -91,7 +91,7 @@ expected_stdout_lines:
```bash
dapr run --app-id custom-grpc-client \
-d ./config \
--dapr-http-max-request-size 41 \
--max-body-size 41Mi \
--log-level debug \
go run ./custom-grpc-client/main.go
```

View File

@ -95,7 +95,7 @@ func main() {
in := &dapr.InvokeBindingRequest{
Name: "example-http-binding",
Operation: "create",
Operation: "get",
}
if err := client.InvokeOutputBinding(ctx, in); err != nil {
panic(err)

View File

@ -7,6 +7,6 @@ spec:
version: v1
metadata:
- name: url
value: https://http2.pro/api/v1
value: https://sandbox.api.service.nhs.uk/hello-world/hello/world
- name: method
value: GET

View File

@ -15,6 +15,7 @@ expected_stdout_lines:
- '== APP == Worker initialized'
- '== APP == TestWorkflow registered'
- '== APP == TestActivity registered'
- '== APP == FailActivity registered'
- '== APP == runner started'
- '== APP == workflow started with id: a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9'
- '== APP == workflow paused'
@ -22,20 +23,14 @@ expected_stdout_lines:
- '== APP == stage: 1'
- '== APP == workflow event raised'
- '== APP == stage: 2'
- '== APP == fail activity executions: 3'
- '== APP == workflow status: COMPLETED'
- '== APP == workflow purged'
- '== APP == stage: 2'
- '== APP == workflow started with id: a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9'
- '== APP == workflow status: RUNNING'
- '== APP == workflow terminated'
- '== APP == workflow purged'
- '== APP == workflow client test'
- '== APP == [wfclient] started workflow with id: a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9'
- '== APP == [wfclient] workflow status: RUNNING'
- '== APP == [wfclient] stage: 1'
- '== APP == [wfclient] event raised'
- '== APP == [wfclient] stage: 2'
- '== APP == [wfclient] workflow terminated'
- '== APP == [wfclient] workflow purged'
- '== APP == workflow worker successfully shutdown'
background: true

View File

@ -16,19 +16,16 @@ package main
import (
"context"
"errors"
"fmt"
"log"
"time"
"github.com/dapr/go-sdk/client"
"github.com/dapr/go-sdk/workflow"
)
var stage = 0
const (
workflowComponent = "dapr"
)
var failActivityTries = 0
func main() {
w, err := workflow.NewWorker()
@ -48,76 +45,60 @@ func main() {
}
fmt.Println("TestActivity registered")
if err := w.RegisterActivity(FailActivity); err != nil {
log.Fatal(err)
}
fmt.Println("FailActivity registered")
// Start workflow runner
if err := w.Start(); err != nil {
log.Fatal(err)
}
fmt.Println("runner started")
daprClient, err := client.NewClient()
wfClient, err := workflow.NewClient()
if err != nil {
log.Fatalf("failed to intialise client: %v", err)
}
defer daprClient.Close()
defer wfClient.Close()
ctx := context.Background()
// Start workflow test
respStart, err := daprClient.StartWorkflowBeta1(ctx, &client.StartWorkflowRequest{
InstanceID: "a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9",
WorkflowComponent: workflowComponent,
WorkflowName: "TestWorkflow",
Options: nil,
Input: 1,
SendRawInput: false,
})
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)
}
fmt.Printf("workflow started with id: %v\n", respStart.InstanceID)
fmt.Printf("workflow started with id: %v\n", instanceID)
// Pause workflow test
err = daprClient.PauseWorkflowBeta1(ctx, &client.PauseWorkflowRequest{
InstanceID: "a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9",
WorkflowComponent: workflowComponent,
})
err = wfClient.SuspendWorkflow(ctx, instanceID, "")
if err != nil {
log.Fatalf("failed to pause workflow: %v", err)
}
respGet, err := daprClient.GetWorkflowBeta1(ctx, &client.GetWorkflowRequest{
InstanceID: "a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9",
WorkflowComponent: workflowComponent,
})
respFetch, err := wfClient.FetchWorkflowMetadata(ctx, instanceID, workflow.WithFetchPayloads(true))
if err != nil {
log.Fatalf("failed to get workflow: %v", err)
log.Fatalf("failed to fetch workflow: %v", err)
}
if respGet.RuntimeStatus != workflow.StatusSuspended.String() {
log.Fatalf("workflow not paused: %v", respGet.RuntimeStatus)
if respFetch.RuntimeStatus != workflow.StatusSuspended {
log.Fatalf("workflow not paused: %v", respFetch.RuntimeStatus)
}
fmt.Printf("workflow paused\n")
// Resume workflow test
err = daprClient.ResumeWorkflowBeta1(ctx, &client.ResumeWorkflowRequest{
InstanceID: "a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9",
WorkflowComponent: workflowComponent,
})
err = wfClient.ResumeWorkflow(ctx, instanceID, "")
if err != nil {
log.Fatalf("failed to resume workflow: %v", err)
}
respGet, err = daprClient.GetWorkflowBeta1(ctx, &client.GetWorkflowRequest{
InstanceID: "a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9",
WorkflowComponent: workflowComponent,
})
respFetch, err = wfClient.FetchWorkflowMetadata(ctx, instanceID, workflow.WithFetchPayloads(true))
if err != nil {
log.Fatalf("failed to get workflow: %v", err)
}
if respGet.RuntimeStatus != workflow.StatusRunning.String() {
if respFetch.RuntimeStatus != workflow.StatusRunning {
log.Fatalf("workflow not running")
}
@ -127,14 +108,7 @@ func main() {
// Raise Event Test
err = daprClient.RaiseEventWorkflowBeta1(ctx, &client.RaiseEventWorkflowRequest{
InstanceID: "a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9",
WorkflowComponent: workflowComponent,
EventName: "testEvent",
EventData: "testData",
SendRawData: false,
})
err = wfClient.RaiseEvent(ctx, instanceID, "testEvent", workflow.WithEventPayload("testData"))
if err != nil {
fmt.Printf("failed to raise event: %v", err)
}
@ -145,31 +119,31 @@ func main() {
fmt.Printf("stage: %d\n", stage)
respGet, err = daprClient.GetWorkflowBeta1(ctx, &client.GetWorkflowRequest{
InstanceID: "a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9",
WorkflowComponent: workflowComponent,
})
waitCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
_, err = wfClient.WaitForWorkflowCompletion(waitCtx, instanceID)
cancel()
if err != nil {
log.Fatalf("failed to wait for workflow: %v", err)
}
fmt.Printf("fail activity executions: %d\n", failActivityTries)
respFetch, err = wfClient.FetchWorkflowMetadata(ctx, instanceID, workflow.WithFetchPayloads(true))
if err != nil {
log.Fatalf("failed to get workflow: %v", err)
}
fmt.Printf("workflow status: %v\n", respGet.RuntimeStatus)
fmt.Printf("workflow status: %v\n", respFetch.RuntimeStatus)
// Purge workflow test
err = daprClient.PurgeWorkflowBeta1(ctx, &client.PurgeWorkflowRequest{
InstanceID: "a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9",
WorkflowComponent: workflowComponent,
})
err = wfClient.PurgeWorkflow(ctx, instanceID)
if err != nil {
log.Fatalf("failed to purge workflow: %v", err)
}
respGet, err = daprClient.GetWorkflowBeta1(ctx, &client.GetWorkflowRequest{
InstanceID: "a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9",
WorkflowComponent: workflowComponent,
})
if err != nil && respGet != nil {
log.Fatal("failed to purge workflow")
respFetch, err = wfClient.FetchWorkflowMetadata(ctx, instanceID, workflow.WithFetchPayloads(true))
if err == nil || respFetch != nil {
log.Fatalf("failed to purge workflow: %v", err)
}
fmt.Println("workflow purged")
@ -177,120 +151,30 @@ func main() {
fmt.Printf("stage: %d\n", stage)
// Terminate workflow test
respStart, err = daprClient.StartWorkflowBeta1(ctx, &client.StartWorkflowRequest{
InstanceID: "a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9",
WorkflowComponent: workflowComponent,
WorkflowName: "TestWorkflow",
Options: nil,
Input: 1,
SendRawInput: false,
})
id, 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)
}
fmt.Printf("workflow started with id: %v\n", instanceID)
fmt.Printf("workflow started with id: %s\n", respStart.InstanceID)
err = daprClient.TerminateWorkflowBeta1(ctx, &client.TerminateWorkflowRequest{
InstanceID: "a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9",
WorkflowComponent: workflowComponent,
})
if err != nil {
log.Fatalf("failed to terminate workflow: %v", err)
}
respGet, err = daprClient.GetWorkflowBeta1(ctx, &client.GetWorkflowRequest{
InstanceID: "a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9",
WorkflowComponent: workflowComponent,
})
metadata, err := wfClient.WaitForWorkflowStart(ctx, id)
if err != nil {
log.Fatalf("failed to get workflow: %v", err)
}
if respGet.RuntimeStatus != workflow.StatusTerminated.String() {
log.Fatal("failed to terminate workflow")
}
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 = daprClient.PurgeWorkflowBeta1(ctx, &client.PurgeWorkflowRequest{
InstanceID: "a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9",
WorkflowComponent: workflowComponent,
})
respGet, err = daprClient.GetWorkflowBeta1(ctx, &client.GetWorkflowRequest{
InstanceID: "a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9",
WorkflowComponent: workflowComponent,
})
if err == nil || respGet != nil {
err = wfClient.PurgeWorkflow(ctx, id)
if err != nil {
log.Fatalf("failed to purge workflow: %v", err)
}
fmt.Println("workflow purged")
// WFClient
// TODO: Expand client validation
stage = 0
fmt.Println("workflow client test")
wfClient, err := workflow.NewClient()
if err != nil {
log.Fatalf("[wfclient] faield to initialize: %v", err)
}
id, err := wfClient.ScheduleNewWorkflow(ctx, "TestWorkflow", workflow.WithInstanceID("a7a4168d-3a1c-41da-8a4f-e7f6d9c718d9"), workflow.WithInput(1))
if err != nil {
log.Fatalf("[wfclient] failed to start workflow: %v", err)
}
fmt.Printf("[wfclient] started workflow with id: %s\n", id)
metadata, err := wfClient.FetchWorkflowMetadata(ctx, id)
if err != nil {
log.Fatalf("[wfclient] failed to get worfklow: %v", err)
}
fmt.Printf("[wfclient] workflow status: %v\n", metadata.RuntimeStatus.String())
if stage != 1 {
log.Fatalf("Workflow assertion failed while validating the wfclient. Stage 1 expected, current: %d", stage)
}
fmt.Printf("[wfclient] stage: %d\n", stage)
// TODO: WaitForWorkflowStart
// TODO: WaitForWorkflowCompletion
// raise event
if err := wfClient.RaiseEvent(ctx, id, "testEvent", workflow.WithEventPayload("testData")); err != nil {
log.Fatalf("[wfclient] failed to raise event: %v", err)
}
fmt.Println("[wfclient] event raised")
// Sleep to allow the workflow to advance
time.Sleep(time.Second)
if stage != 2 {
log.Fatalf("Workflow assertion failed while validating the wfclient. Stage 2 expected, current: %d", stage)
}
fmt.Printf("[wfclient] stage: %d\n", stage)
// stop workflow
if err := wfClient.TerminateWorkflow(ctx, id); err != nil {
log.Fatalf("[wfclient] failed to terminate workflow: %v", err)
}
fmt.Println("[wfclient] workflow terminated")
if err := wfClient.PurgeWorkflow(ctx, id); err != nil {
log.Fatalf("[wfclient] failed to purge workflow: %v", err)
}
fmt.Println("[wfclient] workflow purged")
// stop workflow runtime
if err := w.Shutdown(); err != nil {
log.Fatalf("failed to shutdown runtime: %v", err)
@ -318,6 +202,15 @@ func TestWorkflow(ctx *workflow.WorkflowContext) (any, error) {
return nil, err
}
if err := ctx.CallActivity(FailActivity, workflow.ActivityRetryPolicy(workflow.RetryPolicy{
MaxAttempts: 3,
InitialRetryInterval: 100 * time.Millisecond,
BackoffCoefficient: 2,
MaxRetryInterval: 1 * time.Second,
})).Await(nil); err == nil {
return nil, fmt.Errorf("unexpected no error executing fail activity")
}
return output, nil
}
@ -331,3 +224,8 @@ func TestActivity(ctx workflow.ActivityContext) (any, error) {
return fmt.Sprintf("Stage: %d", stage), nil
}
func FailActivity(ctx workflow.ActivityContext) (any, error) {
failActivityTries += 1
return nil, errors.New("dummy activity error")
}

34
go.mod
View File

@ -1,34 +1,34 @@
module github.com/dapr/go-sdk
go 1.22.5
go 1.24.2
require (
github.com/dapr/dapr v1.14.0-rc.5
github.com/dapr/dapr v1.15.5
github.com/dapr/durabletask-go v0.6.5
github.com/go-chi/chi/v5 v5.1.0
github.com/golang/mock v1.6.0
github.com/google/uuid v1.6.0
github.com/microsoft/durabletask-go v0.5.0
github.com/stretchr/testify v1.9.0
google.golang.org/grpc v1.65.0
google.golang.org/protobuf v1.34.2
github.com/stretchr/testify v1.10.0
google.golang.org/grpc v1.72.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.2 // 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/golang/protobuf v1.5.4 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/marusama/semaphore/v2 v2.5.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
go.opentelemetry.io/otel v1.27.0 // indirect
go.opentelemetry.io/otel/metric v1.27.0 // indirect
go.opentelemetry.io/otel/trace v1.27.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
github.com/sirupsen/logrus v1.9.3 // 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
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e // indirect
)

86
go.sum
View File

@ -1,8 +1,13 @@
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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/dapr/dapr v1.14.0-rc.5 h1:oTZPcT5fwda6bCMxrfenem6tOyeqW1nastxTwWInBCY=
github.com/dapr/dapr v1.14.0-rc.5/go.mod h1:IQWNthXF/I+qqlW4I0T+F4hCu74eKon4vjhpNvoBl8A=
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/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
@ -16,41 +21,48 @@ 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.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
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/marusama/semaphore/v2 v2.5.0 h1:o/1QJD9DBYOWRnDhPwDVAXQn6mQYD0gZaS1Tpx6DJGM=
github.com/marusama/semaphore/v2 v2.5.0/go.mod h1:z9nMiNUekt/LTpTUQdpp+4sJeYqUGpwMHfW0Z8V8fnQ=
github.com/microsoft/durabletask-go v0.5.0 h1:4DWBgg05wnkV/VwakaiPqZ4cARvATP74ZQJFcXVMC18=
github.com/microsoft/durabletask-go v0.5.0/go.mod h1:goe2gmMgLptCijMDQ7JsekaR86KjPUG64V9JDXvKBhE=
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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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.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/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg=
go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ=
go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik=
go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak=
go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw=
go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4=
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/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-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
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.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
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/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=
@ -58,27 +70,31 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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/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.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
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-20240624140628-dc46fd24d27d h1:k3zyW3BYYR30e8v3x0bTDdE9vpYFjZHK+HcyqkrppWk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
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/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-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro=
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=

View File

@ -27,18 +27,23 @@ const (
)
// Service represents Dapr callback service.
//
//nolint:interfacebloat
type Service interface {
// AddHealthCheckHandler sets a health check handler, name: http (router) and grpc (invalid).
AddHealthCheckHandler(name string, fn HealthCheckHandler) error
// AddServiceInvocationHandler appends provided service invocation handler with its name to the service.
AddServiceInvocationHandler(name string, fn ServiceInvocationHandler) error
// AddTopicEventHandler appends provided event handler with its topic and optional metadata to the service.
// Note, retries are only considered when there is an error. Lack of error is considered as a success
// Note, retries are only considered when there is an error. Lack of error is considered as a success.
AddTopicEventHandler(sub *Subscription, fn TopicEventHandler) error
// AddTopicEventSubscriber appends the provided subscriber with its topic and optional metadata to the service.
// Note, retries are only considered when there is an error. Lack of error is considered as a success.
AddTopicEventSubscriber(sub *Subscription, subscriber TopicEventSubscriber) error
// AddBindingInvocationHandler appends provided binding invocation handler with its name to the service.
AddBindingInvocationHandler(name string, fn BindingInvocationHandler) error
// RegisterActorImplFactory Register a new actor to actor runtime of go sdk
// Deprecated: use RegisterActorImplFactoryContext instead
// Deprecated: use RegisterActorImplFactoryContext instead.
RegisterActorImplFactory(f actor.Factory, opts ...config.Option)
// RegisterActorImplFactoryContext Register a new actor to actor runtime of go sdk
RegisterActorImplFactoryContext(f actor.FactoryContext, opts ...config.Option)
@ -49,7 +54,7 @@ type Service interface {
Start() error
// Stop stops the previously started service.
Stop() error
// Gracefully stops the previous started service
// Gracefully stops the previous started service.
GracefulStop() error
}
@ -60,3 +65,13 @@ type (
JobEventHandler func(ctx context.Context, in *JobEvent) error
HealthCheckHandler func(context.Context) error
)
type TopicEventSubscriber interface {
Handle(ctx context.Context, e *TopicEvent) (retry bool, err error)
}
// Handle converts TopicEventHandler into an adapter that implements
// TopicEventSubscriber.
func (h TopicEventHandler) Handle(ctx context.Context, e *TopicEvent) (retry bool, err error) {
return h(ctx, e)
}

View File

@ -47,6 +47,10 @@ type TopicEvent struct {
PubsubName string `json:"pubsubname"`
// Metadata is the custom metadata attached to the event.
Metadata map[string]string `json:"metadata,omitempty"`
// TraceID is the tracing header identifier for the incoming event
TraceID string `json:"traceid"`
// TraceParent is name of the parent trace identifier for the incoming event
TraceParent string `json:"traceparent"`
}
func (e *TopicEvent) Struct(target interface{}) error {
@ -103,6 +107,8 @@ type Subscription struct {
Priority int `json:"priority"`
// DisableTopicValidation allows to receive events from publisher topics that differ from the subscribed topic.
DisableTopicValidation bool `json:"disableTopicValidation"`
// DeadLetterTopic is the name of the deadletter topic.
DeadLetterTopic string `json:"deadLetterTopic"`
}
type SubscriptionResponseStatus string

View File

@ -27,10 +27,10 @@ import (
// AddBindingInvocationHandler appends provided binding invocation handler with its name to the service.
func (s *Server) AddBindingInvocationHandler(name string, fn common.BindingInvocationHandler) error {
if name == "" {
return fmt.Errorf("binding name required")
return errors.New("binding name required")
}
if fn == nil {
return fmt.Errorf("binding handler required")
return errors.New("binding handler required")
}
s.bindingHandlers[name] = fn
return nil

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

@ -15,7 +15,7 @@ package grpc
import (
"context"
"fmt"
"errors"
pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
"github.com/dapr/go-sdk/service/common"
@ -26,7 +26,7 @@ import (
// AddHealthCheckHandler appends provided app health check handler.
func (s *Server) AddHealthCheckHandler(_ string, fn common.HealthCheckHandler) error {
if fn == nil {
return fmt.Errorf("health check handler required")
return errors.New("health check handler required")
}
s.healthCheckHandler = fn
@ -44,5 +44,5 @@ func (s *Server) HealthCheck(ctx context.Context, _ *emptypb.Empty) (*pb.HealthC
return &pb.HealthCheckResponse{}, nil
}
return nil, fmt.Errorf("health check handler not implemented")
return nil, errors.New("health check handler not implemented")
}

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

@ -28,7 +28,7 @@ import (
// AddServiceInvocationHandler appends provided service invocation handler with its method to the service.
func (s *Server) AddServiceInvocationHandler(method string, fn cc.ServiceInvocationHandler) error {
if method == "" || method == "/" {
return fmt.Errorf("servie name required")
return errors.New("service name required")
}
if method[0] == '/' {
@ -36,7 +36,7 @@ func (s *Server) AddServiceInvocationHandler(method string, fn cc.ServiceInvocat
}
if fn == nil {
return fmt.Errorf("invocation handler required")
return errors.New("invocation handler required")
}
s.invokeHandlers[method] = fn
return nil

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

@ -31,11 +31,20 @@ import (
// AddTopicEventHandler appends provided event handler with topic name to the service.
func (s *Server) AddTopicEventHandler(sub *common.Subscription, fn common.TopicEventHandler) error {
if fn == nil {
return errors.New("topic handler required")
}
return s.AddTopicEventSubscriber(sub, fn)
}
// AddTopicEventSubscriber appends the provided subscriber to the service.
func (s *Server) AddTopicEventSubscriber(sub *common.Subscription, subscriber common.TopicEventSubscriber) error {
if sub == nil {
return errors.New("subscription required")
}
return s.topicRegistrar.AddSubscription(sub, fn)
return s.topicRegistrar.AddSubscription(sub, subscriber)
}
// ListTopicSubscriptions is called by Dapr to get the list of topics in a pubsub component the app wants to subscribe to.
@ -44,10 +53,11 @@ func (s *Server) ListTopicSubscriptions(ctx context.Context, in *emptypb.Empty)
for _, v := range s.topicRegistrar {
s := v.Subscription
sub := &runtimev1pb.TopicSubscription{
PubsubName: s.PubsubName,
Topic: s.Topic,
Metadata: s.Metadata,
Routes: convertRoutes(s.Routes),
PubsubName: s.PubsubName,
Topic: s.Topic,
Metadata: s.Metadata,
Routes: convertRoutes(s.Routes),
DeadLetterTopic: s.DeadLetterTopic,
}
subs = append(subs, sub)
}
@ -142,7 +152,7 @@ func (s *Server) OnTopicEvent(ctx context.Context, in *runtimev1pb.TopicEventReq
in.GetPath(), in.GetPubsubName(), in.GetTopic(),
)
}
retry, err := h(ctx, e)
retry, err := h.Handle(ctx, e)
if err == nil {
return &runtimev1pb.TopicEventResponse{Status: runtimev1pb.TopicEventResponse_SUCCESS}, nil
}

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

@ -14,7 +14,7 @@ limitations under the License.
package http
import (
"fmt"
"errors"
"io"
"net/http"
"strings"
@ -25,14 +25,14 @@ import (
// AddBindingInvocationHandler appends provided binding invocation handler with its route to the service.
func (s *Server) AddBindingInvocationHandler(route string, fn common.BindingInvocationHandler) error {
if route == "" {
return fmt.Errorf("binding route required")
return errors.New("binding route required")
}
if fn == nil {
return fmt.Errorf("binding handler required")
return errors.New("binding handler required")
}
if !strings.HasPrefix(route, "/") {
route = fmt.Sprintf("/%s", route)
route = "/" + route
}
s.mux.Handle(route, optionsHandler(http.HandlerFunc(

View File

@ -14,7 +14,7 @@ limitations under the License.
package http
import (
"fmt"
"errors"
"net/http"
"strings"
@ -24,11 +24,11 @@ import (
// AddHealthCheckHandler appends provided app health check handler.
func (s *Server) AddHealthCheckHandler(route string, fn common.HealthCheckHandler) error {
if fn == nil {
return fmt.Errorf("health check handler required")
return errors.New("health check handler required")
}
if !strings.HasPrefix(route, "/") {
route = fmt.Sprintf("/%s", route)
route = "/" + route
}
s.mux.Handle(route, optionsHandler(http.HandlerFunc(

View File

@ -14,7 +14,7 @@ limitations under the License.
package http
import (
"fmt"
"errors"
"io"
"net/http"
"strings"
@ -27,11 +27,11 @@ import (
// AddServiceInvocationHandler appends provided service invocation handler with its route to the service.
func (s *Server) AddServiceInvocationHandler(route string, fn common.ServiceInvocationHandler) error {
if route == "" || route == "/" {
return fmt.Errorf("service route required")
return errors.New("service route required")
}
if fn == nil {
return fmt.Errorf("invocation handler required")
return errors.New("invocation handler required")
}
if !strings.HasPrefix(route, "/") {

View File

@ -2,17 +2,16 @@ package http
import (
"errors"
"fmt"
"github.com/dapr/go-sdk/service/common"
)
func (s *Server) AddJobEventHandler(name string, fn common.JobEventHandler) error {
if name == "" {
return fmt.Errorf("job event name required")
return errors.New("job event name required")
}
if fn == nil {
return fmt.Errorf("job event handler required")
return errors.New("job event handler required")
}
return errors.New("handling http scheduling requests has not been implemented in this sdk")

View File

@ -69,6 +69,10 @@ type topicEventJSON struct {
Topic string `json:"topic"`
// PubsubName is name of the pub/sub this message came from
PubsubName string `json:"pubsubname"`
// TraceID is the tracing header identifier for the incoming event
TraceID string `json:"traceid"`
// TraceParent is name of the parent trace identifier for the incoming event
TraceParent string `json:"traceparent"`
}
func (in topicEventJSON) getData() (data any, rawData []byte) {
@ -190,9 +194,11 @@ func (s *Server) registerBaseHandler() {
err := runtime.GetActorRuntimeInstanceContext().Deactivate(r.Context(), actorType, actorID)
if err == actorErr.ErrActorTypeNotFound || err == actorErr.ErrActorIDNotFound {
w.WriteHeader(http.StatusNotFound)
return
}
if err != actorErr.Success {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
@ -207,9 +213,11 @@ func (s *Server) registerBaseHandler() {
err := runtime.GetActorRuntimeInstanceContext().InvokeReminder(r.Context(), actorType, actorID, reminderName, reqData)
if err == actorErr.ErrActorTypeNotFound {
w.WriteHeader(http.StatusNotFound)
return
}
if err != actorErr.Success {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
@ -224,9 +232,11 @@ func (s *Server) registerBaseHandler() {
err := runtime.GetActorRuntimeInstanceContext().InvokeTimer(r.Context(), actorType, actorID, timerName, reqData)
if err == actorErr.ErrActorTypeNotFound {
w.WriteHeader(http.StatusNotFound)
return
}
if err != actorErr.Success {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
@ -235,6 +245,15 @@ func (s *Server) registerBaseHandler() {
// AddTopicEventHandler appends provided event handler with it's name to the service.
func (s *Server) AddTopicEventHandler(sub *common.Subscription, fn common.TopicEventHandler) error {
if fn == nil {
return errors.New("topic handler required")
}
return s.AddTopicEventSubscriber(sub, fn)
}
// AddTopicEventSubscriber appends the provided subscriber to the service.
func (s *Server) AddTopicEventSubscriber(sub *common.Subscription, subscriber common.TopicEventSubscriber) error {
if sub == nil {
return errors.New("subscription required")
}
@ -243,7 +262,7 @@ func (s *Server) AddTopicEventHandler(sub *common.Subscription, fn common.TopicE
if sub.Route == "" {
return errors.New("handler route name")
}
if err := s.topicRegistrar.AddSubscription(sub, fn); err != nil {
if err := s.topicRegistrar.AddSubscription(sub, subscriber); err != nil {
return err
}
@ -294,13 +313,15 @@ func (s *Server) AddTopicEventHandler(sub *common.Subscription, fn common.TopicE
PubsubName: in.PubsubName,
Topic: in.Topic,
Metadata: getCustomMetdataFromHeaders(r),
TraceID: in.TraceID,
TraceParent: in.TraceParent,
}
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
// execute user handler
retry, err := fn(r.Context(), &te)
retry, err := subscriber.Handle(r.Context(), &te)
if err == nil {
writeStatus(w, common.SubscriptionResponseStatusSuccess)
return

View File

@ -71,7 +71,9 @@ func TestEventHandler(t *testing.T) {
"comexampleextension1" : "value",
"comexampleothervalue" : 5,
"datacontenttype" : "application/json",
"data" : "eyJtZXNzYWdlIjoiaGVsbG8ifQ=="
"data" : "eyJtZXNzYWdlIjoiaGVsbG8ifQ==",
"traceid": "aaa",
"traceparent": "bbb"
}`
s := newServer("", nil)

View File

@ -2,7 +2,6 @@ package internal
import (
"errors"
"fmt"
"github.com/dapr/go-sdk/service/common"
)
@ -15,11 +14,11 @@ type TopicRegistrar map[string]*TopicRegistration
// TopicRegistration encapsulates the subscription and handlers.
type TopicRegistration struct {
Subscription *TopicSubscription
DefaultHandler common.TopicEventHandler
RouteHandlers map[string]common.TopicEventHandler
DefaultHandler common.TopicEventSubscriber
RouteHandlers map[string]common.TopicEventSubscriber
}
func (m TopicRegistrar) AddSubscription(sub *common.Subscription, fn common.TopicEventHandler) error {
func (m TopicRegistrar) AddSubscription(sub *common.Subscription, fn common.TopicEventSubscriber) error {
if sub.Topic == "" {
return errors.New("topic name required")
}
@ -27,7 +26,7 @@ func (m TopicRegistrar) AddSubscription(sub *common.Subscription, fn common.Topi
return errors.New("pub/sub name required")
}
if fn == nil {
return fmt.Errorf("topic handler required")
return errors.New("topic handler required")
}
var key string
@ -40,8 +39,8 @@ func (m TopicRegistrar) AddSubscription(sub *common.Subscription, fn common.Topi
ts, ok := m[key]
if !ok {
ts = &TopicRegistration{
Subscription: NewTopicSubscription(sub.PubsubName, sub.Topic),
RouteHandlers: make(map[string]common.TopicEventHandler),
Subscription: NewTopicSubscription(sub.PubsubName, sub.Topic, sub.DeadLetterTopic),
RouteHandlers: make(map[string]common.TopicEventSubscriber),
DefaultHandler: nil,
}
ts.Subscription.SetMetadata(sub.Metadata)

View File

@ -13,12 +13,12 @@ import (
)
func TestTopicRegistrarValidation(t *testing.T) {
fn := func(ctx context.Context, e *common.TopicEvent) (retry bool, err error) {
fn := common.TopicEventHandler(func(ctx context.Context, e *common.TopicEvent) (retry bool, err error) {
return false, nil
}
})
tests := map[string]struct {
sub common.Subscription
fn common.TopicEventHandler
fn common.TopicEventSubscriber
err string
}{
"pubsub required": {
@ -63,7 +63,6 @@ func TestTopicRegistrarValidation(t *testing.T) {
},
}
for name, tt := range tests {
tt := tt // dereference loop var
t.Run(name, func(t *testing.T) {
m := internal.TopicRegistrar{}
if tt.err != "" {
@ -76,9 +75,9 @@ func TestTopicRegistrarValidation(t *testing.T) {
}
func TestTopicAddSubscriptionMetadata(t *testing.T) {
handler := func(ctx context.Context, e *common.TopicEvent) (retry bool, err error) {
handler := common.TopicEventHandler(func(ctx context.Context, e *common.TopicEvent) (retry bool, err error) {
return false, nil
}
})
topicRegistrar := internal.TopicRegistrar{}
sub := &common.Subscription{
PubsubName: "pubsubname",

View File

@ -18,6 +18,8 @@ type TopicSubscription struct {
Routes *TopicRoutes `json:"routes,omitempty"`
// Metadata is the subscription metadata.
Metadata map[string]string `json:"metadata,omitempty"`
// DeadLetterTopic is the name of the deadletter topic.
DeadLetterTopic string `json:"deadLetterTopic"`
}
// TopicRoutes encapsulates the default route and multiple routing rules.
@ -42,10 +44,11 @@ type TopicRule struct {
}
// NewTopicSubscription creates a new `TopicSubscription`.
func NewTopicSubscription(pubsubName, topic string) *TopicSubscription {
func NewTopicSubscription(pubsubName, topic, deadLetterTopic string) *TopicSubscription {
return &TopicSubscription{ //nolint:exhaustivestruct
PubsubName: pubsubName,
Topic: topic,
PubsubName: pubsubName,
Topic: topic,
DeadLetterTopic: deadLetterTopic,
}
}

View File

@ -12,7 +12,7 @@ import (
func TestTopicSubscripiton(t *testing.T) {
t.Run("duplicate metadata", func(t *testing.T) {
sub := internal.NewTopicSubscription("test", "mytopic")
sub := internal.NewTopicSubscription("test", "mytopic", "")
require.NoError(t, sub.SetMetadata(map[string]string{
"test": "test",
}))
@ -22,7 +22,7 @@ func TestTopicSubscripiton(t *testing.T) {
})
t.Run("duplicate route", func(t *testing.T) {
sub := internal.NewTopicSubscription("test", "mytopic")
sub := internal.NewTopicSubscription("test", "mytopic", "")
require.NoError(t, sub.SetDefaultRoute("/test"))
assert.Equal(t, "/test", sub.Route)
require.EqualError(t, sub.SetDefaultRoute("/test"),
@ -30,7 +30,7 @@ func TestTopicSubscripiton(t *testing.T) {
})
t.Run("duplicate route after routing rule", func(t *testing.T) {
sub := internal.NewTopicSubscription("test", "mytopic")
sub := internal.NewTopicSubscription("test", "mytopic", "")
require.NoError(t, sub.AddRoutingRule("/other", `event.type == "test"`, 0))
require.NoError(t, sub.SetDefaultRoute("/test"))
require.EqualError(t, sub.SetDefaultRoute("/test"),
@ -38,7 +38,7 @@ func TestTopicSubscripiton(t *testing.T) {
})
t.Run("default route after routing rule", func(t *testing.T) {
sub := internal.NewTopicSubscription("test", "mytopic")
sub := internal.NewTopicSubscription("test", "mytopic", "")
require.NoError(t, sub.SetDefaultRoute("/test"))
assert.Equal(t, "/test", sub.Route)
require.NoError(t, sub.AddRoutingRule("/other", `event.type == "test"`, 0))
@ -49,14 +49,14 @@ func TestTopicSubscripiton(t *testing.T) {
})
t.Run("duplicate routing rule priority", func(t *testing.T) {
sub := internal.NewTopicSubscription("test", "mytopic")
sub := internal.NewTopicSubscription("test", "mytopic", "")
require.NoError(t, sub.AddRoutingRule("/other", `event.type == "other"`, 1))
require.EqualError(t, sub.AddRoutingRule("/test", `event.type == "test"`, 1),
"subscription for topic mytopic on pubsub test already has a routing rule with priority 1")
})
t.Run("priority ordering", func(t *testing.T) {
sub := internal.NewTopicSubscription("test", "mytopic")
sub := internal.NewTopicSubscription("test", "mytopic", "")
require.NoError(t, sub.AddRoutingRule("/100", `event.type == "100"`, 100))
require.NoError(t, sub.AddRoutingRule("/1", `event.type == "1"`, 1))
require.NoError(t, sub.AddRoutingRule("/50", `event.type == "50"`, 50))

View File

@ -1,8 +1,6 @@
module github.com/dapr/go-sdk/tools/check-lint-version
go 1.21
toolchain go1.21.6
go 1.23.3
require (
github.com/stretchr/testify v1.8.4

View File

@ -51,7 +51,7 @@ func getCurrentVersion() (string, error) {
if matches == nil {
return "", fmt.Errorf("no version found: %v", string(out))
}
return fmt.Sprintf("v%s", matches[1]), err
return "v" + matches[1], err
}
func isVersionValid(workflowVersion, currentVersion string) bool {
@ -72,7 +72,7 @@ func compareVersions(path string) string {
if !validVersion {
return fmt.Sprintf("Invalid version, expected: %s, current: %s - See: https://golangci-lint.run/usage/install/ for instructions to update", workflowVersion, currentVersion)
}
return fmt.Sprintf("Linter version is valid (MajorMinor): %s", currentVersion)
return "Linter version is valid (MajorMinor): " + currentVersion
}
func main() {

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.55.2", 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.55.2", 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.11.0
v1.12.0

View File

@ -17,10 +17,11 @@ package workflow
import (
"context"
"encoding/json"
"time"
"google.golang.org/protobuf/types/known/wrapperspb"
"github.com/microsoft/durabletask-go/task"
"github.com/dapr/durabletask-go/task"
)
type ActivityContext struct {
@ -38,7 +39,16 @@ func (wfac *ActivityContext) Context() context.Context {
type callActivityOption func(*callActivityOptions) error
type callActivityOptions struct {
rawInput *wrapperspb.StringValue
rawInput *wrapperspb.StringValue
retryPolicy *RetryPolicy
}
type RetryPolicy struct {
MaxAttempts int
InitialRetryInterval time.Duration
BackoffCoefficient float64
MaxRetryInterval time.Duration
RetryTimeout time.Duration
}
// ActivityInput is an option to pass a JSON-serializable input
@ -61,6 +71,26 @@ func ActivityRawInput(input string) callActivityOption {
}
}
func ActivityRetryPolicy(policy RetryPolicy) callActivityOption {
return func(opts *callActivityOptions) error {
opts.retryPolicy = &policy
return nil
}
}
func (opts *callActivityOptions) getRetryPolicy() *task.RetryPolicy {
if opts.retryPolicy == nil {
return nil
}
return &task.RetryPolicy{
MaxAttempts: opts.retryPolicy.MaxAttempts,
InitialRetryInterval: opts.retryPolicy.InitialRetryInterval,
BackoffCoefficient: opts.retryPolicy.BackoffCoefficient,
MaxRetryInterval: opts.retryPolicy.MaxRetryInterval,
RetryTimeout: opts.retryPolicy.RetryTimeout,
}
}
func marshalData(input any) ([]byte, error) {
if input == nil {
return nil, nil

View File

@ -19,13 +19,17 @@ import (
"encoding/json"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/dapr/durabletask-go/task"
)
type testingTaskActivityContext struct {
inputBytes []byte
ctx context.Context
}
func (t *testingTaskActivityContext) GetInput(v any) error {
@ -33,7 +37,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) {
@ -41,7 +45,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)
@ -50,7 +54,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())
})
}
@ -69,6 +73,26 @@ func TestCallActivityOptions(t *testing.T) {
opts := returnCallActivityOptions(ActivityRawInput("test"))
assert.Equal(t, "test", opts.rawInput.GetValue())
})
t.Run("activity retry policy - set", func(t *testing.T) {
opts := returnCallActivityOptions(ActivityRetryPolicy(RetryPolicy{
MaxAttempts: 3,
InitialRetryInterval: 100 * time.Millisecond,
BackoffCoefficient: 2,
MaxRetryInterval: 2 * time.Second,
}))
assert.Equal(t, &task.RetryPolicy{
MaxAttempts: 3,
InitialRetryInterval: 100 * time.Millisecond,
BackoffCoefficient: 2,
MaxRetryInterval: 2 * time.Second,
}, opts.getRetryPolicy())
})
t.Run("activity retry policy - empty", func(t *testing.T) {
opts := returnCallActivityOptions()
assert.Empty(t, opts.getRetryPolicy())
})
}
func returnCallActivityOptions(opts ...callActivityOption) callActivityOptions {

View File

@ -20,17 +20,34 @@ import (
"fmt"
"time"
"github.com/microsoft/durabletask-go/api"
"github.com/microsoft/durabletask-go/backend"
durabletaskclient "github.com/microsoft/durabletask-go/client"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/wrapperspb"
"github.com/dapr/durabletask-go/api"
"github.com/dapr/durabletask-go/backend"
durabletaskclient "github.com/dapr/durabletask-go/client"
dapr "github.com/dapr/go-sdk/client"
)
type Client struct {
conn *grpc.ClientConn
taskHubClient *durabletaskclient.TaskHubGrpcClient
}
type WorkflowIDReusePolicy struct {
OperationStatus []Status
Action CreateWorkflowAction
}
type CreateWorkflowAction = api.CreateOrchestrationAction
const (
ReuseIDActionError CreateWorkflowAction = api.REUSE_ID_ACTION_ERROR
ReuseIDActionIgnore CreateWorkflowAction = api.REUSE_ID_ACTION_IGNORE
ReuseIDActionTerminate CreateWorkflowAction = api.REUSE_ID_ACTION_TERMINATE
)
// WithInstanceID is an option to set an InstanceID when scheduling a new workflow.
func WithInstanceID(id string) api.NewOrchestrationOptions {
return api.WithInstanceID(api.InstanceID(id))
@ -45,7 +62,7 @@ func WithInput(input any) api.NewOrchestrationOptions {
// WithRawInput is an option to pass a byte slice as an input when scheduling a new workflow.
func WithRawInput(input string) api.NewOrchestrationOptions {
return api.WithRawInput(input)
return api.WithRawInput(wrapperspb.String(input))
}
// WithStartTime is an option to set the start time when scheduling a new workflow.
@ -53,6 +70,13 @@ func WithStartTime(time time.Time) api.NewOrchestrationOptions {
return api.WithStartTime(time)
}
func WithReuseIDPolicy(policy WorkflowIDReusePolicy) api.NewOrchestrationOptions {
return api.WithOrchestrationIdReusePolicy(&api.OrchestrationIdReusePolicy{
OperationStatus: convertStatusSlice(policy.OperationStatus),
Action: policy.Action,
})
}
// WithFetchPayloads is an option to return the payload from a workflow.
func WithFetchPayloads(fetchPayloads bool) api.FetchOrchestrationMetadataOptions {
return api.WithFetchPayloads(fetchPayloads)
@ -65,7 +89,7 @@ func WithEventPayload(data any) api.RaiseEventOptions {
// WithRawEventData is an option to send a byte slice with an event to a workflow.
func WithRawEventData(data string) api.RaiseEventOptions {
return api.WithRawEventData(data)
return api.WithRawEventData(wrapperspb.String(data))
}
// WithOutput is an option to define an output when terminating a workflow.
@ -75,7 +99,17 @@ func WithOutput(data any) api.TerminateOptions {
// WithRawOutput is an option to define a byte slice to output when terminating a workflow.
func WithRawOutput(data string) api.TerminateOptions {
return api.WithRawOutput(data)
return api.WithRawOutput(wrapperspb.String(data))
}
// WithRecursiveTerminate configures whether to terminate all sub-workflows created by the target workflow.
func WithRecursiveTerminate(recursive bool) api.TerminateOptions {
return api.WithRecursiveTerminate(recursive)
}
// WithRecursivePurge configures whether to purge all sub-workflows created by the target workflow.
func WithRecursivePurge(recursive bool) api.PurgeOptions {
return api.WithRecursivePurge(recursive)
}
type clientOption func(*clientOptions) error
@ -113,9 +147,11 @@ func NewClient(opts ...clientOption) (*Client, error) {
return &Client{}, fmt.Errorf("failed to initialise dapr.Client: %v", err)
}
taskHubClient := durabletaskclient.NewTaskHubGrpcClient(daprClient.GrpcClientConn(), backend.DefaultLogger())
conn := daprClient.GrpcClientConn()
taskHubClient := durabletaskclient.NewTaskHubGrpcClient(conn, backend.DefaultLogger())
return &Client{
conn: conn,
taskHubClient: taskHubClient,
}, nil
}
@ -205,9 +241,13 @@ func (c *Client) ResumeWorkflow(ctx context.Context, id, reason string) error {
// PurgeWorkflow will purge a given workflow and return an error output.
// NOTE: The workflow must be in a terminated or completed state.
func (c *Client) PurgeWorkflow(ctx context.Context, id string) error {
func (c *Client) PurgeWorkflow(ctx context.Context, id string, opts ...api.PurgeOptions) error {
if id == "" {
return errors.New("no workflow id specified")
}
return c.taskHubClient.PurgeOrchestrationState(ctx, api.InstanceID(id))
return c.taskHubClient.PurgeOrchestrationState(ctx, api.InstanceID(id), opts...)
}
func (c *Client) Close() {
_ = c.conn.Close()
}

View File

@ -15,7 +15,6 @@ limitations under the License.
package workflow
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
@ -52,9 +51,12 @@ 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, "", nil)
id, err := testClient.ScheduleNewWorkflow(ctx, "", WithReuseIDPolicy(WorkflowIDReusePolicy{
OperationStatus: []Status{StatusCompleted},
Action: ReuseIDActionIgnore,
}))
require.Error(t, err)
assert.Empty(t, id)
})

View File

@ -18,7 +18,7 @@ import (
"fmt"
"time"
"github.com/microsoft/durabletask-go/task"
"github.com/dapr/durabletask-go/task"
)
type WorkflowContext struct {
@ -50,6 +50,11 @@ func (wfc *WorkflowContext) IsReplaying() bool {
return wfc.orchestrationContext.IsReplaying
}
// SetCustomStatus sets custom status to the workflow context
func (wfc *WorkflowContext) SetCustomStatus(cs string) {
wfc.orchestrationContext.SetCustomStatus(cs)
}
// CallActivity returns a completable task for a given activity.
// You must call Await(output any) on the returned Task to block the workflow and wait for the task to complete.
// The value passed to the Await method must be a pointer or can be nil to ignore the returned value.
@ -63,7 +68,7 @@ func (wfc *WorkflowContext) CallActivity(activity interface{}, opts ...callActiv
}
}
return wfc.orchestrationContext.CallActivity(activity, task.WithRawActivityInput(options.rawInput.GetValue()))
return wfc.orchestrationContext.CallActivity(activity, task.WithRawActivityInput(options.rawInput), task.WithActivityRetryPolicy(options.getRetryPolicy()))
}
// CallChildWorkflow returns a completable task for a given workflow.
@ -79,9 +84,9 @@ func (wfc *WorkflowContext) CallChildWorkflow(workflow interface{}, opts ...call
}
}
if options.instanceID != "" {
return wfc.orchestrationContext.CallSubOrchestrator(workflow, task.WithRawSubOrchestratorInput(options.rawInput.GetValue()), task.WithSubOrchestrationInstanceID(options.instanceID))
return wfc.orchestrationContext.CallSubOrchestrator(workflow, task.WithRawSubOrchestratorInput(options.rawInput), task.WithSubOrchestrationInstanceID(options.instanceID), task.WithSubOrchestrationRetryPolicy(options.getRetryPolicy()))
}
return wfc.orchestrationContext.CallSubOrchestrator(workflow, task.WithRawSubOrchestratorInput(options.rawInput.GetValue()))
return wfc.orchestrationContext.CallSubOrchestrator(workflow, task.WithRawSubOrchestratorInput(options.rawInput), task.WithSubOrchestrationRetryPolicy(options.getRetryPolicy()))
}
// CreateTimer returns a completable task that blocks for a given duration.

View File

@ -18,9 +18,10 @@ import (
"testing"
"time"
"github.com/microsoft/durabletask-go/task"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/dapr/durabletask-go/task"
)
func TestContext(t *testing.T) {

View File

@ -14,7 +14,10 @@ limitations under the License.
*/
package workflow
import "github.com/microsoft/durabletask-go/api"
import (
"github.com/dapr/durabletask-go/api"
"github.com/dapr/durabletask-go/api/protos"
)
type Status int
@ -48,12 +51,42 @@ func (s Status) String() string {
return status[s]
}
func (s Status) RuntimeStatus() api.OrchestrationStatus {
switch s {
case StatusRunning:
return api.RUNTIME_STATUS_RUNNING
case StatusCompleted:
return api.RUNTIME_STATUS_COMPLETED
case StatusContinuedAsNew:
return api.RUNTIME_STATUS_CONTINUED_AS_NEW
case StatusFailed:
return api.RUNTIME_STATUS_FAILED
case StatusCanceled:
return api.RUNTIME_STATUS_CANCELED
case StatusTerminated:
return api.RUNTIME_STATUS_TERMINATED
case StatusPending:
return api.RUNTIME_STATUS_PENDING
case StatusSuspended:
return api.RUNTIME_STATUS_SUSPENDED
}
return -1
}
type WorkflowState struct {
Metadata api.OrchestrationMetadata
Metadata protos.OrchestrationMetadata
}
// RuntimeStatus returns the status from a workflow state.
func (wfs *WorkflowState) RuntimeStatus() Status {
s := Status(wfs.Metadata.RuntimeStatus.Number())
s := Status(wfs.Metadata.GetRuntimeStatus().Number())
return s
}
func convertStatusSlice(ss []Status) []api.OrchestrationStatus {
out := []api.OrchestrationStatus{}
for _, s := range ss {
out = append(out, s.RuntimeStatus())
}
return out
}

View File

@ -17,12 +17,13 @@ package workflow
import (
"testing"
"github.com/microsoft/durabletask-go/api"
"github.com/stretchr/testify/assert"
"github.com/dapr/durabletask-go/api/protos"
)
func TestString(t *testing.T) {
wfState := WorkflowState{Metadata: api.OrchestrationMetadata{RuntimeStatus: 0}}
wfState := WorkflowState{Metadata: protos.OrchestrationMetadata{RuntimeStatus: 0}}
t.Run("test running", func(t *testing.T) {
s := wfState.RuntimeStatus()

View File

@ -25,9 +25,9 @@ import (
dapr "github.com/dapr/go-sdk/client"
"github.com/microsoft/durabletask-go/backend"
durabletaskclient "github.com/microsoft/durabletask-go/client"
"github.com/microsoft/durabletask-go/task"
"github.com/dapr/durabletask-go/backend"
durabletaskclient "github.com/dapr/durabletask-go/client"
"github.com/dapr/durabletask-go/task"
)
type WorkflowWorker struct {
@ -125,7 +125,13 @@ func wrapActivity(a Activity) task.Activity {
return func(ctx task.ActivityContext) (any, error) {
aCtx := ActivityContext{ctx: ctx}
return a(aCtx)
result, err := a(aCtx)
if err != nil {
activityName, _ := getFunctionName(a) // Get the activity name for context
return nil, fmt.Errorf("activity %s failed: %w", activityName, err)
}
return result, nil
}
}

View File

@ -19,7 +19,7 @@ import (
daprClient "github.com/dapr/go-sdk/client"
"github.com/microsoft/durabletask-go/task"
"github.com/dapr/durabletask-go/task"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@ -18,9 +18,10 @@ import (
"fmt"
"time"
"github.com/microsoft/durabletask-go/api"
"github.com/microsoft/durabletask-go/task"
"google.golang.org/protobuf/types/known/wrapperspb"
"github.com/dapr/durabletask-go/api/protos"
"github.com/dapr/durabletask-go/task"
)
type Metadata struct {
@ -43,29 +44,29 @@ type FailureDetails struct {
IsNonRetriable bool `json:"IsNonRetriable"`
}
func convertMetadata(orchestrationMetadata *api.OrchestrationMetadata) *Metadata {
func convertMetadata(orchestrationMetadata *protos.OrchestrationMetadata) *Metadata {
metadata := Metadata{
InstanceID: string(orchestrationMetadata.InstanceID),
Name: orchestrationMetadata.Name,
RuntimeStatus: Status(orchestrationMetadata.RuntimeStatus.Number()),
CreatedAt: orchestrationMetadata.CreatedAt,
LastUpdatedAt: orchestrationMetadata.LastUpdatedAt,
SerializedInput: orchestrationMetadata.SerializedInput,
SerializedOutput: orchestrationMetadata.SerializedOutput,
SerializedCustomStatus: orchestrationMetadata.SerializedCustomStatus,
InstanceID: orchestrationMetadata.GetInstanceId(),
Name: orchestrationMetadata.GetName(),
RuntimeStatus: Status(orchestrationMetadata.GetRuntimeStatus().Number()),
CreatedAt: orchestrationMetadata.GetCreatedAt().AsTime(),
LastUpdatedAt: orchestrationMetadata.GetLastUpdatedAt().AsTime(),
SerializedInput: orchestrationMetadata.GetInput().GetValue(),
SerializedOutput: orchestrationMetadata.GetOutput().GetValue(),
SerializedCustomStatus: orchestrationMetadata.GetCustomStatus().GetValue(),
}
if orchestrationMetadata.FailureDetails != nil {
if orchestrationMetadata.GetFailureDetails() != nil {
metadata.FailureDetails = &FailureDetails{
Type: orchestrationMetadata.FailureDetails.GetErrorType(),
Message: orchestrationMetadata.FailureDetails.GetErrorMessage(),
StackTrace: orchestrationMetadata.FailureDetails.GetStackTrace().GetValue(),
IsNonRetriable: orchestrationMetadata.FailureDetails.GetIsNonRetriable(),
Type: orchestrationMetadata.GetFailureDetails().GetErrorType(),
Message: orchestrationMetadata.GetFailureDetails().GetErrorMessage(),
StackTrace: orchestrationMetadata.GetFailureDetails().GetStackTrace().GetValue(),
IsNonRetriable: orchestrationMetadata.GetFailureDetails().GetIsNonRetriable(),
}
if orchestrationMetadata.FailureDetails.GetInnerFailure() != nil {
if orchestrationMetadata.GetFailureDetails().GetInnerFailure() != nil {
var root *FailureDetails
current := root
failure := orchestrationMetadata.FailureDetails.GetInnerFailure()
failure := orchestrationMetadata.GetFailureDetails().GetInnerFailure()
for {
current.Type = failure.GetErrorType()
current.Message = failure.GetErrorMessage()
@ -87,8 +88,9 @@ func convertMetadata(orchestrationMetadata *api.OrchestrationMetadata) *Metadata
}
type callChildWorkflowOptions struct {
instanceID string
rawInput *wrapperspb.StringValue
instanceID string
rawInput *wrapperspb.StringValue
retryPolicy *RetryPolicy
}
type callChildWorkflowOption func(*callChildWorkflowOptions) error
@ -121,6 +123,26 @@ func ChildWorkflowInstanceID(instanceID string) callChildWorkflowOption {
}
}
func ChildWorkflowRetryPolicy(policy RetryPolicy) callChildWorkflowOption {
return func(opts *callChildWorkflowOptions) error {
opts.retryPolicy = &policy
return nil
}
}
func (opts *callChildWorkflowOptions) getRetryPolicy() *task.RetryPolicy {
if opts.retryPolicy == nil {
return nil
}
return &task.RetryPolicy{
MaxAttempts: opts.retryPolicy.MaxAttempts,
InitialRetryInterval: opts.retryPolicy.InitialRetryInterval,
BackoffCoefficient: opts.retryPolicy.BackoffCoefficient,
MaxRetryInterval: opts.retryPolicy.MaxRetryInterval,
RetryTimeout: opts.retryPolicy.RetryTimeout,
}
}
// NewTaskSlice returns a slice of tasks which can be executed in parallel
func NewTaskSlice(length int) []task.Task {
taskSlice := make([]task.Task, length)

View File

@ -2,15 +2,18 @@ package workflow
import (
"testing"
"time"
"github.com/microsoft/durabletask-go/api"
"github.com/stretchr/testify/assert"
"github.com/dapr/durabletask-go/api/protos"
"github.com/dapr/durabletask-go/task"
)
func TestConvertMetadata(t *testing.T) {
t.Run("convert metadata", func(t *testing.T) {
rawMetadata := &api.OrchestrationMetadata{
InstanceID: api.InstanceID("test"),
rawMetadata := &protos.OrchestrationMetadata{
InstanceId: "test",
}
metadata := convertMetadata(rawMetadata)
assert.NotEmpty(t, metadata)
@ -37,6 +40,26 @@ func TestCallChildWorkflowOptions(t *testing.T) {
opts := returnCallChildWorkflowOptions(ChildWorkflowInput(make(chan int)))
assert.Empty(t, opts.rawInput.GetValue())
})
t.Run("child workflow retry policy - set", func(t *testing.T) {
opts := returnCallChildWorkflowOptions(ChildWorkflowRetryPolicy(RetryPolicy{
MaxAttempts: 3,
InitialRetryInterval: 100 * time.Millisecond,
BackoffCoefficient: 2,
MaxRetryInterval: 2 * time.Second,
}))
assert.Equal(t, &task.RetryPolicy{
MaxAttempts: 3,
InitialRetryInterval: 100 * time.Millisecond,
BackoffCoefficient: 2,
MaxRetryInterval: 2 * time.Second,
}, opts.getRetryPolicy())
})
t.Run("child workflow retry policy - empty", func(t *testing.T) {
opts := returnCallChildWorkflowOptions()
assert.Empty(t, opts.getRetryPolicy())
})
}
func returnCallChildWorkflowOptions(opts ...callChildWorkflowOption) callChildWorkflowOptions {