├── .devcontainer ├── Dockerfile ├── devcontainer.json └── docker-compose.yml ├── .env ├── .github ├── CODEOWNERS └── workflows │ └── ci.yml ├── .gitignore ├── .gitpod.readme.md ├── .gitpod.yml ├── .vscode └── launch.json ├── LICENSE ├── Makefile ├── README.md ├── await-signals ├── README.md ├── await_signals_workflow.go ├── await_signals_workflow_test.go ├── starter │ └── main.go └── worker │ └── main.go ├── batch-sliding-window ├── README.md ├── batch_workflow.go ├── record_loader_activity.go ├── record_processor_workflow.go ├── sliding_window_workflow.go ├── starter │ └── main.go └── worker │ └── main.go ├── branch ├── README.md ├── activity.go ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── build-id-versioning ├── README.md ├── starter │ └── main.go ├── worker │ └── main.go └── workflows.go ├── cancellation ├── README.md ├── activity.go ├── cancel │ └── main.go ├── starter │ └── main.go ├── worker │ └── main.go └── workflow.go ├── child-workflow-continue-as-new ├── README.md ├── child_workflow.go ├── parent_workflow.go ├── starter │ └── main.go └── worker │ └── main.go ├── child-workflow ├── README.md ├── child_workflow.go ├── parent_workflow.go ├── parent_workflow_test.go ├── starter │ └── main.go └── worker │ └── main.go ├── choice-exclusive ├── README.md ├── order_activities.go ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── choice-multi ├── README.md ├── order_activities.go ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── codec-server ├── README.md ├── codec-server │ ├── main.go │ └── oidc.go ├── data_converter.go ├── go.mod ├── go.sum ├── starter │ └── main.go ├── worker │ └── main.go └── workflow.go ├── cron ├── README.md ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── ctxpropagation ├── README.md ├── activities.go ├── propagator.go ├── starter │ └── main.go ├── tracer.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── datadog ├── README.md ├── starter │ └── main.go ├── worker │ └── main.go └── workflow.go ├── docker-compose.yml ├── dsl ├── README.md ├── activities.go ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go ├── workflow1.yaml └── workflow2.yaml ├── dynamic ├── README.md ├── activities.go ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── dynamicconfig └── development-sql.yaml ├── dynamicmtls ├── README.md ├── dynamicmtls.go ├── starter │ └── main.go └── worker │ └── main.go ├── eager-workflow-start ├── README.md └── main.go ├── early-return ├── README.md ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── encryption ├── README.md ├── codec-server │ └── main.go ├── crypt.go ├── data_converter.go ├── data_converter_test.go ├── propagator.go ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── expense ├── README.md ├── activities.go ├── starter │ └── main.go ├── ui │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── fileprocessing ├── README.md ├── activities.go ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── go.mod ├── go.sum ├── goroutine ├── README.md ├── goroutine_workflow.go ├── goroutine_workflow_test.go ├── starter │ └── main.go └── worker │ └── main.go ├── greetings ├── README.md ├── activities.go ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── greetingslocal ├── README.md ├── activities.go ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── grpc-proxy ├── README.md ├── data_converter.go ├── go.mod ├── go.sum ├── proxy-server │ ├── main.go │ └── oidc.go ├── starter │ └── main.go ├── worker │ └── main.go └── workflow.go ├── helloworld-apiKey ├── README.md ├── helloworld.go ├── helloworld_test.go ├── starter │ └── main.go └── worker │ └── main.go ├── helloworld ├── README.md ├── helloworld.go ├── helloworld.json ├── helloworld_test.go ├── replay_test.go ├── starter │ └── main.go └── worker │ └── main.go ├── helloworldmtls ├── README.md ├── helloworld.go ├── helloworld_test.go ├── starter │ └── main.go └── worker │ └── main.go ├── logger-interceptor ├── README.md ├── interceptor.go ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── memo ├── README.md ├── memo_workflow.go ├── memo_workflow_test.go ├── starter │ └── main.go └── worker │ └── main.go ├── metrics ├── README.md ├── metrics.go ├── starter │ └── main.go ├── worker │ └── main.go └── workflow.go ├── multi-history-replay ├── README.md ├── replayer │ └── main.go ├── starter │ └── main.go └── worker │ └── main.go ├── mutex ├── README.md ├── mutex_workflow.go ├── mutex_workflow_test.go ├── starter │ └── main.go └── worker │ └── main.go ├── nexus-cancelation ├── README.md ├── caller │ ├── starter │ │ └── main.go │ ├── worker │ │ └── main.go │ └── workflows.go └── handler │ ├── app.go │ └── worker │ └── main.go ├── nexus-context-propagation ├── README.md ├── caller │ ├── starter │ │ └── main.go │ └── worker │ │ └── main.go ├── handler │ ├── app.go │ └── worker │ │ └── main.go └── interceptor.go ├── nexus ├── README.md ├── caller │ ├── starter │ │ └── main.go │ ├── worker │ │ └── main.go │ ├── workflows.go │ └── workflows_test.go ├── handler │ ├── app.go │ └── worker │ │ └── main.go ├── options │ └── cli.go └── service │ ├── api.go │ └── description.md ├── opentelemetry ├── README.md ├── honeycomb_traces.png ├── opentelemetry.go ├── opentelemetry_setup.go ├── starter │ └── main.go └── worker │ └── main.go ├── pickfirst ├── README.md ├── pickfirst_workflow.go ├── pickfirst_workflow_test.go ├── starter │ └── main.go └── worker │ └── main.go ├── polling ├── README.md ├── frequent │ ├── README.md │ ├── activities.go │ ├── starter │ │ └── main.go │ ├── worker │ │ └── main.go │ ├── workflow.go │ └── workflow_test.go ├── infrequent │ ├── README.md │ ├── activities.go │ ├── starter │ │ └── main.go │ ├── worker │ │ └── main.go │ ├── workflow.go │ └── workflow_test.go ├── periodic_sequence │ ├── README.md │ ├── activities.go │ ├── child_workflow.go │ ├── starter │ │ └── main.go │ ├── worker │ │ └── main.go │ ├── workflow.go │ └── workflow_test.go └── testservice.go ├── pso ├── README.md ├── activities.go ├── dataconverter.go ├── functions.go ├── particle.go ├── position.go ├── query │ └── main.go ├── settings.go ├── starter │ └── main.go ├── swarm.go ├── utils.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── query ├── README.md ├── query │ └── main.go ├── query_workflow.go ├── query_workflow_test.go ├── starter │ └── main.go └── worker │ └── main.go ├── recovery ├── README.md ├── cache │ ├── cache.go │ └── lru.go ├── query │ └── main.go ├── recovery_workflow.go ├── signal │ └── main.go ├── starter │ └── main.go ├── trip_workflow.go └── worker │ └── main.go ├── reqrespactivity ├── README.md ├── request │ └── main.go ├── requester.go ├── requester_test.go ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── reqrespquery ├── README.md ├── request │ └── main.go ├── requester.go ├── requester_test.go ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── reqrespupdate ├── README.md ├── request │ └── main.go ├── requester.go ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── retryactivity ├── README.md ├── retry_activity_workflow.go ├── retry_activity_workflow_test.go ├── starter │ └── main.go └── worker │ └── main.go ├── safe_message_handler ├── README.md ├── activities.go ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── saga ├── README.md ├── activity.go ├── shared.go ├── start │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── schedule ├── README.md ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── searchattributes ├── README.md ├── searchattributes_workflow.go ├── searchattributes_workflow_test.go ├── starter │ └── main.go └── worker │ └── main.go ├── serverjwtauth ├── README.md ├── key │ └── main.go ├── keyutil.go ├── starter │ └── main.go └── worker │ └── main.go ├── session-failure ├── README.md ├── activities.go ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── shoppingcart ├── README.md ├── ui │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── sleep-for-days ├── README.md ├── sleepfordays_workflow.go ├── sleepfordays_workflow_test.go ├── starter │ └── main.go └── worker │ └── main.go ├── slogadapter ├── README.md ├── starter │ └── main.go ├── worker │ └── main.go └── workflow.go ├── snappycompress ├── README.md ├── data_converter.go ├── data_converter_test.go ├── plugin │ └── main.go ├── snappycompress.go ├── starter │ └── main.go └── worker │ └── main.go ├── splitmerge-future ├── README.md ├── splitmerge_workflow.go ├── splitmerge_workflow_test.go ├── starter │ └── main.go └── worker │ └── main.go ├── splitmerge-selector ├── README.md ├── splitmerge_workflow.go ├── splitmerge_workflow_test.go ├── starter │ └── main.go └── worker │ └── main.go ├── start-delay ├── README.md ├── starter │ └── main.go └── worker │ └── main.go ├── synchronous-proxy ├── README.md ├── activities.go ├── flow.png ├── proxy │ └── main.go ├── ui │ └── main.go ├── worker │ └── main.go └── workflow.go ├── temporal-fixtures ├── README.md ├── large-event-history │ ├── README.md │ ├── starter │ │ └── main.go │ ├── worker │ │ └── main.go │ ├── workflow.go │ └── workflow_test.go ├── largepayload │ ├── README.md │ ├── activities.go │ ├── starter │ │ └── main.go │ ├── worker │ │ └── main.go │ ├── workflow.go │ └── workflow_test.go ├── namespaces │ ├── README.md │ └── main.go ├── openNclosed │ ├── README.md │ ├── starter │ │ └── main.go │ ├── worker │ │ └── main.go │ ├── workflow.go │ └── workflow_test.go ├── rainbow-statuses │ ├── README.md │ ├── activities.go │ ├── rainbowstatusesworkflow.json │ ├── starter │ │ └── main.go │ ├── worker │ │ └── main.go │ ├── workflow.go │ └── workflow_test.go └── stuck-workflows │ ├── README.md │ ├── starter │ └── main.go │ ├── worker │ └── main.go │ ├── workflow.go │ └── workflow_test.go ├── timer ├── README.md ├── starter │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── typed-searchattributes ├── README.md ├── searchattributes_workflow.go ├── searchattributes_workflow_test.go ├── starter │ └── main.go └── worker │ └── main.go ├── updatabletimer ├── README.md ├── starter │ └── main.go ├── updater │ └── main.go ├── worker │ └── main.go ├── workflow.go └── workflow_test.go ├── update ├── README.md ├── starter │ └── main.go ├── update.go └── worker │ └── main.go ├── worker-specific-task-queues ├── README.md ├── activities.go ├── starter │ └── main.go ├── worker │ └── main.go ├── worker_specific_task_queue.go ├── workflow.go └── workflow_test.go ├── workflow-security-interceptor ├── README.md ├── interceptor.go ├── security_interceptor_test.go ├── starter │ └── main.go ├── worker │ └── main.go └── workflow.go └── zapadapter ├── README.md ├── starter └── main.go ├── worker └── main.go ├── workflow.go └── zap_adapter.go /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.231.6/containers/go/.devcontainer/base.Dockerfile 2 | 3 | # [Choice] Go version (use -bullseye variants on local arm64/Apple Silicon): 1, 1.16, 1.17, 1-bullseye, 1.16-bullseye, 1.17-bullseye, 1-buster, 1.16-buster, 1.17-buster 4 | ARG VARIANT="1-bullseye" 5 | FROM mcr.microsoft.com/vscode/devcontainers/go:0-${VARIANT} 6 | 7 | # [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 8 | ARG NODE_VERSION="none" 9 | RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi 10 | 11 | # [Optional] Uncomment this section to install additional OS packages. 12 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 13 | # && apt-get -y install --no-install-recommends curl gnupg 14 | 15 | # [Optional] Uncomment the next lines to use go get to install anything else you need 16 | # USER vscode 17 | # RUN go install [packages] 18 | 19 | # [Optional] Uncomment this line to install global node packages. 20 | # RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | COMPOSE_PROJECT_NAME=temporal 2 | CASSANDRA_VERSION=3.11.9 3 | ELASTICSEARCH_VERSION=7.16.2 4 | MYSQL_VERSION=8 5 | POSTGRESQL_VERSION=13 6 | TEMPORAL_VERSION=1.22.0 7 | TEMPORAL_UI_VERSION=2.10.3 8 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in 2 | # the repo. Unless a later match takes precedence, 3 | # @temporalio/sdk will be requested for review when 4 | # someone opens a pull request. 5 | * @temporalio/sdk -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build-and-test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Print build information 13 | run: "echo head_ref: ${{ github.head_ref }}, ref: ${{ github.ref }}" 14 | - uses: actions/checkout@v4 15 | with: 16 | submodules: recursive 17 | - uses: actions/setup-go@v5 18 | with: 19 | go-version: "1.22" 20 | - name: CI Build 21 | run: make ci-build 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gobincache 2 | *.out 3 | *.test 4 | *.xml 5 | *.swp 6 | .idea/ 7 | *.iml 8 | *.cov 9 | *.html 10 | .tmp/ 11 | .DS_Store 12 | test 13 | test.log 14 | vendor/ 15 | # Executables produced by temporal-go-samples repo 16 | bin/ 17 | .fossa.yml 18 | *~ 19 | -------------------------------------------------------------------------------- /.gitpod.readme.md: -------------------------------------------------------------------------------- 1 | # Welcome to the Go SDK Gitpod setup! 2 | 3 | If you're reading this, you probably clicked on our Gitpod link: 4 | [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-908a85?logo=gitpod)](https://gitpod.io/#https://github.com/temporalio/samples-go/) 5 | 6 | We have preconfigured Gitpod with some recommended settings (in [.gitpod.yml](/.gitpod.yml)). 7 | When you first launch, we will open 3 terminals: 8 | 9 | - Pane 1: Temporal Server - running in background with Docker Compose 10 | - Pane 2: Hello World sample 11 | - Left: Temporal Worker - run `go run worker/main.go` 12 | - Right: Temporal Client - run `go run starter/main.go` 13 | 14 | It takes ~1 minute for the Docker Compose cluster to start up, so we put in manual sleeps as we have not found a more reliable solution. 15 | 16 | Once you have it up and running (Temporal Web should show the first Workflow Execution), you can use our [Hello World Walkthrough tutorial](https://learn.temporal.io/getting_started/go/hello_world_in_go) to orient you to the sample file structure. 17 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch test package", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "test", 12 | "program": "${file}"//"${workspaceFolder}" 13 | }, 14 | { 15 | "name": "Launch worker", 16 | "type": "go", 17 | "request": "launch", 18 | "mode": "debug", 19 | "program": "${file}", 20 | "cwd": "${workspaceFolder}", 21 | "args": ["-m", "worker"] 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2025 Temporal Technologies, Inc. 2 | 3 | Copyright (c) 2020 Uber Technologies, Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | -------------------------------------------------------------------------------- /await-signals/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | await_signals "github.com/temporalio/samples-go/await-signals" 5 | "log" 6 | 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | ) 10 | 11 | func main() { 12 | // The client and worker are heavyweight objects that should be created once per process. 13 | c, err := client.Dial(client.Options{ 14 | HostPort: client.DefaultHostPort, 15 | }) 16 | if err != nil { 17 | log.Fatalln("Unable to create client", err) 18 | } 19 | defer c.Close() 20 | 21 | w := worker.New(c, "await_signals", worker.Options{}) 22 | 23 | w.RegisterWorkflow(await_signals.AwaitSignalsWorkflow) 24 | 25 | err = w.Run(worker.InterruptCh()) 26 | if err != nil { 27 | log.Fatalln("Unable to start worker", err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /batch-sliding-window/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | batch_sliding_window "github.com/temporalio/samples-go/batch-sliding-window" 5 | "log" 6 | 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | ) 10 | 11 | func main() { 12 | // The client and worker are heavyweight objects that should be created once per process. 13 | c, err := client.Dial(client.Options{ 14 | HostPort: client.DefaultHostPort, 15 | }) 16 | if err != nil { 17 | log.Fatalln("Unable to create client", err) 18 | } 19 | defer c.Close() 20 | 21 | w := worker.New(c, "batch-sliding-window", worker.Options{}) 22 | 23 | w.RegisterWorkflow(batch_sliding_window.ProcessBatchWorkflow) 24 | w.RegisterWorkflow(batch_sliding_window.SlidingWindowWorkflow) 25 | w.RegisterWorkflow(batch_sliding_window.RecordProcessorWorkflow) 26 | 27 | w.RegisterActivity(&batch_sliding_window.RecordLoader{RecordCount: 90}) 28 | 29 | err = w.Run(worker.InterruptCh()) 30 | if err != nil { 31 | log.Fatalln("Unable to start worker", err) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /branch/README.md: -------------------------------------------------------------------------------- 1 | ## Parallel Activities 2 | 3 | This sample demonstrates how to kick off multiple activities in parallel and 4 | then synchronously await their results once all have completed. 5 | 6 | 7 | Make sure the [Temporal Server is running locally](https://learn.temporal.io/getting_started/go/dev_environment/#set-up-a-local-temporal-service-for-development-with-temporal-cli). 8 | 9 | From the root of the project, start a Worker: 10 | 11 | ```bash 12 | go run branch/worker/main.go 13 | ``` 14 | 15 | Start the Workflow Execution: 16 | 17 | ```bash 18 | go run branch/starter/main.go 19 | ``` 20 | 21 | -------------------------------------------------------------------------------- /branch/activity.go: -------------------------------------------------------------------------------- 1 | package branch 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // @@@SNIPSTART samples-go-branch-activity-definition 8 | // SampleActivity is a Temporal Activity Definition 9 | func SampleActivity(input string) (string, error) { 10 | name := "sampleActivity" 11 | fmt.Printf("Run %s with input %v \n", name, input) 12 | return "Result_" + input, nil 13 | } 14 | 15 | // @@@SNIPEND 16 | -------------------------------------------------------------------------------- /branch/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/branch" 10 | ) 11 | 12 | // @@@SNIPSTART samples-go-branch-worker-starter 13 | func main() { 14 | // The client and worker are heavyweight objects that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | w := worker.New(c, "branch", worker.Options{}) 24 | 25 | w.RegisterWorkflow(branch.SampleBranchWorkflow) 26 | w.RegisterActivity(branch.SampleActivity) 27 | 28 | err = w.Run(worker.InterruptCh()) 29 | if err != nil { 30 | log.Fatalln("Unable to start worker", err) 31 | } 32 | } 33 | 34 | // @@@SNIPEND 35 | -------------------------------------------------------------------------------- /branch/workflow_test.go: -------------------------------------------------------------------------------- 1 | // @@@SNIPSTART samples-go-branch-workflow-definition-test 2 | package branch 3 | 4 | import ( 5 | "sort" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/mock" 9 | 10 | "github.com/stretchr/testify/suite" 11 | "go.temporal.io/sdk/testsuite" 12 | ) 13 | 14 | type UnitTestSuite struct { 15 | suite.Suite 16 | testsuite.WorkflowTestSuite 17 | } 18 | 19 | func TestUnitTestSuite(t *testing.T) { 20 | suite.Run(t, new(UnitTestSuite)) 21 | } 22 | 23 | func (s *UnitTestSuite) Test_BranchWorkflow() { 24 | env := s.NewTestWorkflowEnvironment() 25 | env.RegisterActivity(SampleActivity) 26 | env.OnActivity(SampleActivity, mock.Anything).Return("one", nil).Once() 27 | env.OnActivity(SampleActivity, mock.Anything).Return("two", nil).Once() 28 | env.OnActivity(SampleActivity, mock.Anything).Return("three", nil).Once() 29 | env.ExecuteWorkflow(SampleBranchWorkflow, 3) 30 | s.True(env.IsWorkflowCompleted()) 31 | s.NoError(env.GetWorkflowError()) 32 | var result []string 33 | s.NoError(env.GetWorkflowResult(&result)) 34 | sort.Strings(result) 35 | expected := []string{"one", "two", "three"} 36 | sort.Strings(expected) 37 | s.Equal(expected, result) 38 | } 39 | 40 | // @@@SNIPEND 41 | -------------------------------------------------------------------------------- /build-id-versioning/README.md: -------------------------------------------------------------------------------- 1 | # Build ID Based Versioning 2 | This sample illustrates how to use Build ID based versioning to help you appropriately roll out 3 | incompatible and compatible changes to workflow and activity code for the same task queue. 4 | 5 | ## Description 6 | The sample shows you how to roll out both a compatible change and an incompatible change to a 7 | workflow. 8 | 9 | ## Running 10 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 11 | 2) Run 12 | ``` 13 | go run build-id-versioning/worker/main.go 14 | ``` 15 | to start the appropriate workers. It will print a task queue name to the console, which you 16 | will need to copy and paste when running the next step. This is to allow running the sample 17 | repeatedly without encountering issues due to Build IDs already existing on the queue. 18 | 19 | 3) Run 20 | ``` 21 | go run build-id-versioning/starter/main.go 22 | ``` 23 | to start the workflows. 24 | -------------------------------------------------------------------------------- /cancellation/README.md: -------------------------------------------------------------------------------- 1 | ## Cancellation 2 | 3 | Make sure the [Temporal Server is running locally](https://learn.temporal.io/getting_started/go/dev_environment/#set-up-a-local-temporal-service-for-development-with-temporal-cli). 4 | 5 | From the root of the project, start a Worker: 6 | 7 | ```bash 8 | go run cancellation/worker/main.go 9 | ``` 10 | 11 | Start the Workflow Execution: 12 | 13 | ```bash 14 | go run cancellation/starter/main.go 15 | ``` 16 | 17 | Cancel the Workflow Execution: 18 | 19 | ```bash 20 | go run cancellation/cancel/main.go 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /cancellation/cancel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "log" 7 | 8 | "go.temporal.io/sdk/client" 9 | ) 10 | 11 | // @@@SNIPSTART samples-go-cancellation-cancel-workflow-execution-trigger 12 | func main() { 13 | var workflowID string 14 | flag.StringVar(&workflowID, "wid", "workflowID-to-cancel", "workflowID of the Workflow Execution to be canceled.") 15 | flag.Parse() 16 | 17 | if workflowID == "" { 18 | flag.PrintDefaults() 19 | return 20 | } 21 | 22 | // The client is a heavyweight object that should be created once per process. 23 | c, err := client.Dial(client.Options{ 24 | HostPort: client.DefaultHostPort, 25 | }) 26 | if err != nil { 27 | log.Fatalln("Unable to create client", err) 28 | } 29 | defer c.Close() 30 | 31 | err = c.CancelWorkflow(context.Background(), workflowID, "") 32 | if err != nil { 33 | log.Fatalln("Unable to cancel Workflow Execution", err) 34 | } 35 | log.Println("Workflow Execution cancelled", "WorkflowID", workflowID) 36 | } 37 | 38 | // @@@SNIPEND 39 | -------------------------------------------------------------------------------- /cancellation/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "log" 7 | 8 | "go.temporal.io/sdk/client" 9 | 10 | "github.com/temporalio/samples-go/cancellation" 11 | ) 12 | 13 | // @@@SNIPSTART samples-go-cancellation-workflow-execution-starter 14 | func main() { 15 | var workflowID string 16 | flag.StringVar(&workflowID, "w", "workflowID-to-cancel", "w is the workflowID of the workflow to be canceled.") 17 | flag.Parse() 18 | 19 | // The client is a heavyweight object that should be created once per process. 20 | c, err := client.Dial(client.Options{ 21 | HostPort: client.DefaultHostPort, 22 | }) 23 | if err != nil { 24 | log.Fatalln("Unable to create client", err) 25 | } 26 | defer c.Close() 27 | 28 | workflowOptions := client.StartWorkflowOptions{ 29 | ID: workflowID, 30 | TaskQueue: "cancel-activity", 31 | } 32 | 33 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, cancellation.YourWorkflow) 34 | if err != nil { 35 | log.Fatalln("Unable to execute workflow", err) 36 | } 37 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 38 | } 39 | 40 | // @@@SNIPEND 41 | -------------------------------------------------------------------------------- /cancellation/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/cancellation" 10 | ) 11 | 12 | // @@@SNIPSTART samples-go-cancellation-worker-starter 13 | func main() { 14 | // The client and worker are heavyweight objects that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | w := worker.New(c, "cancel-activity", worker.Options{}) 24 | 25 | w.RegisterWorkflow(cancellation.YourWorkflow) 26 | w.RegisterActivity(&cancellation.Activities{}) 27 | 28 | err = w.Run(worker.InterruptCh()) 29 | if err != nil { 30 | log.Fatalln("Unable to start worker", err) 31 | } 32 | } 33 | 34 | // @@@SNIPEND 35 | -------------------------------------------------------------------------------- /child-workflow-continue-as-new/README.md: -------------------------------------------------------------------------------- 1 | ## Child Workflow Continue-As-New 2 | 3 | This sample demonstrates that when a Child Workflow calls Continue-As-New it is not visible by a parent. 4 | Parent Workflow Executions receive a notification that a Child Workflow Execution has completed only when the full execution has completed, failed, or timed out. 5 | 6 | This feature is very useful when there is a need to process a large set of data. 7 | The Child Execution can iterate over the data set, calling Continue-As-New periodically without polluting the parents' history. 8 | 9 | Make sure the [Temporal Server is running locally](https://learn.temporal.io/getting_started/go/dev_environment/#set-up-a-local-temporal-service-for-development-with-temporal-cli). 10 | 11 | Start the Worker: 12 | 13 | ```bash 14 | go run child-workflow-continue-as-new/worker/main.go 15 | ``` 16 | 17 | Start the Parent Workflow Execution: 18 | 19 | ```bash 20 | go run child-workflow-continue-as-new/starter/main.go 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /child-workflow-continue-as-new/child_workflow.go: -------------------------------------------------------------------------------- 1 | package child_workflow_continue_as_new 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "go.temporal.io/sdk/workflow" 8 | ) 9 | 10 | // @@@SNIPSTART samples-go-cw-cas-child-workflow-definition 11 | // SampleChildWorkflow is a Workflow Definition 12 | func SampleChildWorkflow(ctx workflow.Context, totalCount, runCount int) (string, error) { 13 | logger := workflow.GetLogger(ctx) 14 | logger.Info("Child workflow execution started.") 15 | if runCount <= 0 { 16 | logger.Error("Invalid valid for run count.", "RunCount", runCount) 17 | return "", errors.New("invalid run count") 18 | } 19 | 20 | totalCount++ 21 | runCount-- 22 | if runCount == 0 { 23 | result := fmt.Sprintf("Child workflow execution completed after %v runs", totalCount) 24 | logger.Info("Child workflow completed.", "Result", result) 25 | return result, nil 26 | } 27 | 28 | logger.Info("Child workflow starting new run.", "RunCount", runCount, "TotalCount", totalCount) 29 | return "", workflow.NewContinueAsNewError(ctx, SampleChildWorkflow, totalCount, runCount) 30 | } 31 | 32 | // @@@SNIPEND 33 | -------------------------------------------------------------------------------- /child-workflow-continue-as-new/parent_workflow.go: -------------------------------------------------------------------------------- 1 | package child_workflow_continue_as_new 2 | 3 | import ( 4 | "fmt" 5 | 6 | "go.temporal.io/sdk/workflow" 7 | ) 8 | 9 | // @@@SNIPSTART samples-go-cw-cas-parent-workflow-definition 10 | // SampleParentWorkflow is a Workflow Definition 11 | func SampleParentWorkflow(ctx workflow.Context) error { 12 | logger := workflow.GetLogger(ctx) 13 | execution := workflow.GetInfo(ctx).WorkflowExecution 14 | // Parent Workflows can choose to specify Ids for child executions. 15 | // Make sure Ids are unique for each execution. 16 | // Do not specify if you want the Temporal Server to generate a unique ID for the child execution. 17 | childID := fmt.Sprintf("child_workflow:%v", execution.RunID) 18 | cwo := workflow.ChildWorkflowOptions{ 19 | WorkflowID: childID, 20 | } 21 | ctx = workflow.WithChildOptions(ctx, cwo) 22 | var result string 23 | err := workflow.ExecuteChildWorkflow(ctx, SampleChildWorkflow, 0, 5).Get(ctx, &result) 24 | if err != nil { 25 | logger.Error("Parent execution received child execution failure.", "Error", err) 26 | return err 27 | } 28 | 29 | logger.Info("Parent execution completed.", "Result", result) 30 | return nil 31 | } 32 | 33 | // @@@SNIPEND 34 | -------------------------------------------------------------------------------- /child-workflow-continue-as-new/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | "go.temporal.io/sdk/client" 9 | 10 | cw "github.com/temporalio/samples-go/child-workflow-continue-as-new" 11 | ) 12 | 13 | // @@@SNIPSTART samples-go-cw-cas-workflow-execution-starter 14 | func main() { 15 | // The client is a heavyweight object that should be created once per process. 16 | c, err := client.Dial(client.Options{ 17 | HostPort: client.DefaultHostPort, 18 | }) 19 | if err != nil { 20 | log.Fatalln("Unable to create client", err) 21 | } 22 | defer c.Close() 23 | 24 | // This workflow ID can be user business logic identifier as well. 25 | workflowID := "parent-workflow_" + uuid.New() 26 | workflowOptions := client.StartWorkflowOptions{ 27 | ID: workflowID, 28 | TaskQueue: "child-workflow-continue-as-new", 29 | } 30 | 31 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, cw.SampleParentWorkflow) 32 | if err != nil { 33 | log.Fatalln("Unable to execute workflow", err) 34 | } 35 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 36 | } 37 | 38 | // @@@SNIPEND 39 | -------------------------------------------------------------------------------- /child-workflow-continue-as-new/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | cw "github.com/temporalio/samples-go/child-workflow-continue-as-new" 10 | ) 11 | 12 | // @@@SNIPSTART samples-go-cw-cas-worker-starter 13 | func main() { 14 | // The client and worker are heavyweight objects that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | w := worker.New(c, "child-workflow-continue-as-new", worker.Options{}) 24 | 25 | w.RegisterWorkflow(cw.SampleParentWorkflow) 26 | w.RegisterWorkflow(cw.SampleChildWorkflow) 27 | 28 | err = w.Run(worker.InterruptCh()) 29 | if err != nil { 30 | log.Fatalln("Unable to start worker", err) 31 | } 32 | } 33 | 34 | // @@@SNIPEND 35 | -------------------------------------------------------------------------------- /child-workflow/README.md: -------------------------------------------------------------------------------- 1 | ## Child Workflow 2 | 3 | Make sure the [Temporal Server is running locally](https://learn.temporal.io/getting_started/go/dev_environment/#set-up-a-local-temporal-service-for-development-with-temporal-cli). 4 | 5 | From the root of the project, start a Worker: 6 | 7 | ```bash 8 | go run child-workflow/worker/main.go 9 | ``` 10 | 11 | Start the Workflow Execution: 12 | 13 | ```bash 14 | go run child-workflow/starter/main.go 15 | ``` 16 | 17 | -------------------------------------------------------------------------------- /child-workflow/child_workflow.go: -------------------------------------------------------------------------------- 1 | package child_workflow 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | // @@@SNIPSTART samples-go-child-workflow-example-child-workflow-definition 8 | // SampleChildWorkflow is a Workflow Definition 9 | func SampleChildWorkflow(ctx workflow.Context, name string) (string, error) { 10 | logger := workflow.GetLogger(ctx) 11 | greeting := "Hello " + name + "!" 12 | logger.Info("Child workflow execution: " + greeting) 13 | return greeting, nil 14 | } 15 | 16 | // @@@SNIPEND 17 | -------------------------------------------------------------------------------- /child-workflow/parent_workflow.go: -------------------------------------------------------------------------------- 1 | package child_workflow 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | // @@@SNIPSTART samples-go-child-workflow-example-parent-workflow-definition 8 | // SampleParentWorkflow is a Workflow Definition 9 | // This Workflow Definition demonstrates how to start a Child Workflow Execution from a Parent Workflow Execution. 10 | // Each Child Workflow Execution starts a new Run. 11 | // The Parent Workflow Execution is notified only after the completion of last Run of the Child Workflow Execution. 12 | func SampleParentWorkflow(ctx workflow.Context) (string, error) { 13 | logger := workflow.GetLogger(ctx) 14 | 15 | cwo := workflow.ChildWorkflowOptions{ 16 | WorkflowID: "ABC-SIMPLE-CHILD-WORKFLOW-ID", 17 | } 18 | ctx = workflow.WithChildOptions(ctx, cwo) 19 | 20 | var result string 21 | err := workflow.ExecuteChildWorkflow(ctx, SampleChildWorkflow, "World").Get(ctx, &result) 22 | if err != nil { 23 | logger.Error("Parent execution received child execution failure.", "Error", err) 24 | return "", err 25 | } 26 | 27 | logger.Info("Parent execution completed.", "Result", result) 28 | return result, nil 29 | } 30 | 31 | // @@@SNIPEND 32 | -------------------------------------------------------------------------------- /child-workflow/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | child_workflow "github.com/temporalio/samples-go/child-workflow" 10 | ) 11 | 12 | // @@@SNIPSTART samples-go-child-workflow-example-worker-starter 13 | func main() { 14 | // The client is a heavyweight object that should be created only once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | w := worker.New(c, "child-workflow", worker.Options{}) 24 | 25 | w.RegisterWorkflow(child_workflow.SampleParentWorkflow) 26 | w.RegisterWorkflow(child_workflow.SampleChildWorkflow) 27 | 28 | err = w.Run(worker.InterruptCh()) 29 | if err != nil { 30 | log.Fatalln("Unable to start worker", err) 31 | } 32 | } 33 | 34 | // @@@SNIPEND 35 | -------------------------------------------------------------------------------- /choice-exclusive/README.md: -------------------------------------------------------------------------------- 1 | ## Exclusive-Choice Sample 2 | 3 | This sample demonstrates how to run an activity based on a dynamic input. 4 | 5 | ### Steps to run this sample: 6 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 7 | 2) Run the following command to start the worker 8 | ``` 9 | go run choice-exclusive/worker/main.go 10 | ``` 11 | 3) Run the following command to start the exclusive choice workflow 12 | ``` 13 | go run choice-exclusive/starter/main.go 14 | ``` 15 | -------------------------------------------------------------------------------- /choice-exclusive/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | "go.temporal.io/sdk/client" 9 | 10 | choice "github.com/temporalio/samples-go/choice-exclusive" 11 | ) 12 | 13 | func main() { 14 | // The client is a heavyweight object that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | workflowOptions := client.StartWorkflowOptions{ 24 | ID: "exclusive_" + uuid.New(), 25 | TaskQueue: "choice", 26 | } 27 | 28 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, choice.ExclusiveChoiceWorkflow) 29 | if err != nil { 30 | log.Fatalln("Unable to execute workflow", err) 31 | } else { 32 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /choice-exclusive/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | choice "github.com/temporalio/samples-go/choice-exclusive" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "choice", worker.Options{}) 23 | 24 | w.RegisterWorkflow(choice.ExclusiveChoiceWorkflow) 25 | 26 | orderChoices := []string{ 27 | choice.OrderChoiceApple, 28 | choice.OrderChoiceBanana, 29 | choice.OrderChoiceCherry, 30 | choice.OrderChoiceOrange} 31 | w.RegisterActivity(&choice.OrderActivities{OrderChoices: orderChoices}) 32 | 33 | err = w.Run(worker.InterruptCh()) 34 | if err != nil { 35 | log.Fatalln("Unable to start worker", err) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /choice-multi/README.md: -------------------------------------------------------------------------------- 1 | ## Multi-Choice Sample 2 | 3 | This sample demonstrates how to run multiple activities in parallel based on a dynamic input. 4 | 5 | ### Steps to run this sample: 6 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 7 | 2) Run the following command to start the worker 8 | ``` 9 | go run choice-multi/worker/main.go 10 | ``` 11 | 3) Run the following command to start the multi choice workflow 12 | ``` 13 | go run choice-multi/starter/main.go 14 | ``` 15 | -------------------------------------------------------------------------------- /choice-multi/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | "go.temporal.io/sdk/client" 9 | 10 | choice_multi "github.com/temporalio/samples-go/choice-multi" 11 | ) 12 | 13 | func main() { 14 | // The client is a heavyweight object that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | workflowOptions := client.StartWorkflowOptions{ 24 | ID: "multi_choice_" + uuid.New(), 25 | TaskQueue: "choice-multi", 26 | } 27 | 28 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, choice_multi.MultiChoiceWorkflow) 29 | if err != nil { 30 | log.Fatalln("Unable to execute workflow", err) 31 | } 32 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /choice-multi/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | choice_multi "github.com/temporalio/samples-go/choice-multi" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "choice-multi", worker.Options{}) 23 | 24 | w.RegisterWorkflow(choice_multi.MultiChoiceWorkflow) 25 | 26 | orderChoices := []string{ 27 | choice_multi.OrderChoiceApple, 28 | choice_multi.OrderChoiceBanana, 29 | choice_multi.OrderChoiceCherry, 30 | choice_multi.OrderChoiceOrange} 31 | w.RegisterActivity(&choice_multi.OrderActivities{OrderChoices: orderChoices}) 32 | 33 | err = w.Run(worker.InterruptCh()) 34 | if err != nil { 35 | log.Fatalln("Unable to start worker", err) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /choice-multi/workflow_test.go: -------------------------------------------------------------------------------- 1 | package choice_multi 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/suite" 7 | "go.temporal.io/sdk/testsuite" 8 | ) 9 | 10 | type UnitTestSuite struct { 11 | suite.Suite 12 | testsuite.WorkflowTestSuite 13 | } 14 | 15 | func TestUnitTestSuite(t *testing.T) { 16 | suite.Run(t, new(UnitTestSuite)) 17 | } 18 | 19 | func (s *UnitTestSuite) Test_MultiChoiceWorkflow() { 20 | env := s.NewTestWorkflowEnvironment() 21 | 22 | orderChoices := []string{ 23 | OrderChoiceApple, 24 | OrderChoiceBanana, 25 | OrderChoiceOrange} 26 | env.RegisterActivity(&OrderActivities{OrderChoices: orderChoices}) 27 | 28 | env.ExecuteWorkflow(MultiChoiceWorkflow) 29 | 30 | s.True(env.IsWorkflowCompleted()) 31 | s.NoError(env.GetWorkflowError()) 32 | } 33 | -------------------------------------------------------------------------------- /codec-server/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | codecserver "github.com/temporalio/samples-go/codec-server" 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | ) 10 | 11 | func main() { 12 | // The client and worker are heavyweight objects that should be created once per process. 13 | c, err := client.Dial(client.Options{ 14 | // Set DataConverter here so that workflow and activity inputs/results will 15 | // be compressed as required. 16 | DataConverter: codecserver.DataConverter, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | w := worker.New(c, "codecserver", worker.Options{}) 24 | 25 | w.RegisterWorkflow(codecserver.Workflow) 26 | w.RegisterActivity(codecserver.Activity) 27 | 28 | err = w.Run(worker.InterruptCh()) 29 | if err != nil { 30 | log.Fatalln("Unable to start worker", err) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cron/README.md: -------------------------------------------------------------------------------- 1 | This sample demonstrates how to setup cron based workflow. 2 | 3 | Steps to run this sample: 4 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 5 | 2) Run 6 | ``` 7 | go run cron/worker/main.go 8 | ``` 9 | to start worker for cron workflow. 10 | 3) Run 11 | ``` 12 | go run cron/starter/main.go 13 | ``` 14 | to start workflow with cron expression scheduled to run every minute. 15 | -------------------------------------------------------------------------------- /cron/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | "go.temporal.io/sdk/client" 9 | 10 | "github.com/temporalio/samples-go/cron" 11 | ) 12 | 13 | func main() { 14 | // The client is a heavyweight object that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | // This workflow ID can be user business logic identifier as well. 24 | workflowID := "cron_" + uuid.New() 25 | workflowOptions := client.StartWorkflowOptions{ 26 | ID: workflowID, 27 | TaskQueue: "cron", 28 | CronSchedule: "* * * * *", 29 | } 30 | 31 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, cron.SampleCronWorkflow) 32 | if err != nil { 33 | log.Fatalln("Unable to execute workflow", err) 34 | } 35 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 36 | } 37 | -------------------------------------------------------------------------------- /cron/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/cron" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "cron", worker.Options{}) 23 | 24 | w.RegisterWorkflow(cron.SampleCronWorkflow) 25 | w.RegisterActivity(cron.DoSomething) 26 | 27 | err = w.Run(worker.InterruptCh()) 28 | if err != nil { 29 | log.Fatalln("Unable to start worker", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ctxpropagation/activities.go: -------------------------------------------------------------------------------- 1 | package ctxpropagation 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | func SampleActivity(ctx context.Context) (*Values, error) { 8 | if val := ctx.Value(PropagateKey); val != nil { 9 | vals := val.(Values) 10 | return &vals, nil 11 | } 12 | return nil, nil 13 | } 14 | -------------------------------------------------------------------------------- /ctxpropagation/tracer.go: -------------------------------------------------------------------------------- 1 | package ctxpropagation 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/opentracing/opentracing-go" 7 | "github.com/uber/jaeger-client-go" 8 | "github.com/uber/jaeger-client-go/config" 9 | ) 10 | 11 | func SetJaegerGlobalTracer() io.Closer { 12 | cfg := config.Configuration{ 13 | ServiceName: "ctx-propogation-sample", 14 | Sampler: &config.SamplerConfig{ 15 | Type: jaeger.SamplerTypeConst, 16 | Param: 1, 17 | }, 18 | Reporter: &config.ReporterConfig{ 19 | LogSpans: true, 20 | }, 21 | } 22 | tracer, closer, err := cfg.NewTracer( 23 | config.Logger(jaeger.StdLogger), 24 | ) 25 | if err != nil { 26 | panic(err) 27 | } 28 | opentracing.SetGlobalTracer(tracer) 29 | 30 | return closer 31 | } 32 | -------------------------------------------------------------------------------- /ctxpropagation/workflow.go: -------------------------------------------------------------------------------- 1 | package ctxpropagation 2 | 3 | import ( 4 | "time" 5 | 6 | "go.temporal.io/sdk/workflow" 7 | ) 8 | 9 | // CtxPropWorkflow workflow definition 10 | func CtxPropWorkflow(ctx workflow.Context) (err error) { 11 | ao := workflow.ActivityOptions{ 12 | StartToCloseTimeout: 2 * time.Second, // such a short timeout to make sample fail over very fast 13 | } 14 | ctx = workflow.WithActivityOptions(ctx, ao) 15 | 16 | if val := ctx.Value(PropagateKey); val != nil { 17 | vals := val.(Values) 18 | workflow.GetLogger(ctx).Info("custom context propagated to workflow", vals.Key, vals.Value) 19 | } 20 | 21 | var values Values 22 | if err = workflow.ExecuteActivity(ctx, SampleActivity).Get(ctx, &values); err != nil { 23 | workflow.GetLogger(ctx).Error("Workflow failed.", "Error", err) 24 | return err 25 | } 26 | workflow.GetLogger(ctx).Info("context propagated to activity", values.Key, values.Value) 27 | workflow.GetLogger(ctx).Info("Workflow completed.") 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /datadog/workflow.go: -------------------------------------------------------------------------------- 1 | package datadog 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "go.temporal.io/sdk/activity" 8 | "go.temporal.io/sdk/workflow" 9 | ) 10 | 11 | func Workflow(ctx workflow.Context, name string) error { 12 | workflow.GetLogger(ctx).Info("Executing Workflow.", "name", name) 13 | cwo := workflow.ChildWorkflowOptions{ 14 | WorkflowID: "DATADOG-CHILD-WORKFLOW-ID", 15 | } 16 | ctx = workflow.WithChildOptions(ctx, cwo) 17 | return workflow.ExecuteChildWorkflow(ctx, ChildWorkflow, name).Get(ctx, nil) 18 | } 19 | 20 | func ChildWorkflow(ctx workflow.Context, name string) error { 21 | workflow.GetLogger(ctx).Info("Executing ChildWorkflow.", "name", name) 22 | ao := workflow.ActivityOptions{ 23 | StartToCloseTimeout: 10 * time.Second, 24 | } 25 | ctx = workflow.WithActivityOptions(ctx, ao) 26 | return workflow.ExecuteActivity(ctx, Activity, name).Get(ctx, nil) 27 | } 28 | 29 | func Activity(ctx context.Context, name string) error { 30 | activity.GetLogger(ctx).Info("Executing Activity.", "name", name) 31 | time.Sleep(time.Second) 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /dsl/README.md: -------------------------------------------------------------------------------- 1 | This sample demonstrates how to implement a DSL workflow. In this sample, we provide 2 sample yaml files each defines a custom workflow that can be processed by this DSL workflow sample code. 2 | 3 | Steps to run this sample: 4 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 5 | 2) Run 6 | ``` 7 | go run dsl/worker/main.go 8 | ``` 9 | to start worker for dsl workflow. 10 | 3) Run 11 | ``` 12 | go run dsl/starter/main.go 13 | ``` 14 | to submit start request for workflow defined in `workflow1.yaml` file. 15 | 16 | Next: 17 | 1) You can run 18 | ``` 19 | go run dsl/starter/main.go -dslConfig=dsl/workflow2.yaml 20 | ``` 21 | to see the result. 22 | 2) You can also write your own yaml config to play with it. 23 | 3) You can replace the dummy activities to your own real activities to build real workflow based on this simple DSL workflow. 24 | -------------------------------------------------------------------------------- /dsl/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/dsl" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "dsl", worker.Options{}) 23 | 24 | w.RegisterWorkflow(dsl.SimpleDSLWorkflow) 25 | w.RegisterActivity(&dsl.SampleActivities{}) 26 | 27 | err = w.Run(worker.InterruptCh()) 28 | if err != nil { 29 | log.Fatalln("Unable to start worker", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /dsl/workflow1.yaml: -------------------------------------------------------------------------------- 1 | # This sample workflow execute 3 steps in sequence. 2 | # 1) sampleActivity1, takes arg1 as input, and put result as result1. 3 | # 2) sampleActivity2, takes result1 as input, and put result as result2. 4 | # 3) sampleActivity3, takes args2 and result2 as input, and put result as result3. 5 | 6 | variables: 7 | arg1: value1 8 | arg2: value2 9 | 10 | root: 11 | sequence: 12 | elements: 13 | - activity: 14 | name: SampleActivity1 15 | arguments: 16 | - arg1 17 | result: result1 18 | - activity: 19 | name: SampleActivity2 20 | arguments: 21 | - result1 22 | result: result2 23 | - activity: 24 | name: SampleActivity3 25 | arguments: 26 | - arg2 27 | - result2 28 | result: result3 29 | -------------------------------------------------------------------------------- /dynamic/README.md: -------------------------------------------------------------------------------- 1 | # Invoking Activities by Name 2 | 3 | The purpose of this sample is to demonstrate invocation of workflows and activities using name 4 | rather than strongly typed function. 5 | 6 | ### Steps to run this sample: 7 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 8 | 2) Run the following command to start the worker 9 | ``` 10 | go run dynamic/worker/main.go 11 | ``` 12 | 3) Run the following command to start the example 13 | ``` 14 | go run dynamic/starter/main.go 15 | ``` 16 | -------------------------------------------------------------------------------- /dynamic/activities.go: -------------------------------------------------------------------------------- 1 | package dynamic 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Activities struct{} 8 | 9 | // Get Name Activity. 10 | func (a *Activities) GetName() (string, error) { 11 | return "Temporal", nil 12 | } 13 | 14 | // Get Greeting Activity. 15 | func (a *Activities) GetGreeting() (string, error) { 16 | return "Hello", nil 17 | } 18 | 19 | // Say Greeting Activity. 20 | func (a *Activities) SayGreeting(greeting string, name string) (string, error) { 21 | result := fmt.Sprintf("Greeting: %s %s!\n", greeting, name) 22 | return result, nil 23 | } 24 | -------------------------------------------------------------------------------- /dynamic/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | "go.temporal.io/sdk/client" 9 | ) 10 | 11 | func main() { 12 | // The client is a heavyweight object that should be created once per process. 13 | c, err := client.Dial(client.Options{ 14 | HostPort: client.DefaultHostPort, 15 | }) 16 | if err != nil { 17 | log.Fatalln("Unable to create client", err) 18 | } 19 | defer c.Close() 20 | 21 | // This workflow ID can be user business logic identifier as well. 22 | workflowID := "dynamic_" + uuid.New() 23 | workflowOptions := client.StartWorkflowOptions{ 24 | ID: workflowID, 25 | TaskQueue: "dynamic", 26 | } 27 | 28 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, "SampleGreetingsWorkflow") 29 | if err != nil { 30 | log.Fatalln("Unable to execute workflow", err) 31 | } 32 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /dynamic/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/dynamic" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "dynamic", worker.Options{}) 23 | 24 | w.RegisterWorkflow(dynamic.SampleGreetingsWorkflow) 25 | w.RegisterActivity(&dynamic.Activities{}) 26 | 27 | err = w.Run(worker.InterruptCh()) 28 | if err != nil { 29 | log.Fatalln("Unable to start worker", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /dynamic/workflow.go: -------------------------------------------------------------------------------- 1 | package dynamic 2 | 3 | import ( 4 | "time" 5 | 6 | "go.temporal.io/sdk/workflow" 7 | ) 8 | 9 | // SampleGreetingsWorkflow Workflow. 10 | func SampleGreetingsWorkflow(ctx workflow.Context) error { 11 | ao := workflow.ActivityOptions{ 12 | StartToCloseTimeout: 10 * time.Second, 13 | } 14 | ctx = workflow.WithActivityOptions(ctx, ao) 15 | 16 | logger := workflow.GetLogger(ctx) 17 | var greetResult string 18 | err := workflow.ExecuteActivity(ctx, "GetGreeting").Get(ctx, &greetResult) 19 | if err != nil { 20 | logger.Error("Get greeting failed.", "Error", err) 21 | return err 22 | } 23 | 24 | // Get Name. 25 | var nameResult string 26 | err = workflow.ExecuteActivity(ctx, "GetName").Get(ctx, &nameResult) 27 | if err != nil { 28 | logger.Error("Get name failed.", "Error", err) 29 | return err 30 | } 31 | 32 | // Say Greeting. 33 | var sayResult string 34 | err = workflow.ExecuteActivity(ctx, "SayGreeting", greetResult, nameResult).Get(ctx, &sayResult) 35 | if err != nil { 36 | logger.Error("Marshalling failed with error.", "Error", err) 37 | return err 38 | } 39 | 40 | logger.Info("Workflow completed.", "Result", sayResult) 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /dynamic/workflow_test.go: -------------------------------------------------------------------------------- 1 | package dynamic 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "go.temporal.io/sdk/testsuite" 9 | ) 10 | 11 | func TestDynamicWorkflow(t *testing.T) { 12 | s := testsuite.WorkflowTestSuite{} 13 | env := s.NewTestWorkflowEnvironment() 14 | env.RegisterActivity(&Activities{}) 15 | var a *Activities 16 | env.OnActivity(a.GetGreeting).Return("Greet", nil).Times(1) 17 | env.OnActivity(a.GetName).Return("Name", nil).Times(1) 18 | env.OnActivity(a.SayGreeting, "Greet", "Name").Return("Greet Name", nil).Times(1) 19 | 20 | env.ExecuteWorkflow(SampleGreetingsWorkflow) 21 | 22 | assert.True(t, env.IsWorkflowCompleted()) 23 | assert.NoError(t, env.GetWorkflowError()) 24 | env.AssertExpectations(t) 25 | } 26 | -------------------------------------------------------------------------------- /dynamicconfig/development-sql.yaml: -------------------------------------------------------------------------------- 1 | limit.maxIDLength: 2 | - value: 255 3 | constraints: {} 4 | system.forceSearchAttributesCacheRefreshOnRead: 5 | - value: true # Dev setup only. Please don't turn this on in production. 6 | constraints: {} 7 | frontend.enableUpdateWorkflowExecution: 8 | - value: true 9 | -------------------------------------------------------------------------------- /dynamicmtls/README.md: -------------------------------------------------------------------------------- 1 | This sample shows how to connect a client to Temporal using mtls where the certificates are dynamically loaded. This allows the credentials to be replaced without restarting the worker. 2 | 3 | ### Steps to run this sample: 4 | 1) Configure a [Temporal Server](https://github.com/temporalio/samples-go/tree/main/#how-to-use) (such as Temporal Cloud) with mTLS. 5 | 6 | 2) Run the following command to start the worker 7 | ``` 8 | go run ./dynamicmtls/worker -target-host my.namespace.tmprl.cloud:7233 -namespace my.namespace -client-cert path/to/cert.pem -client-key path/to/key.pem 9 | ``` 10 | 3) Run the following command to start the example 11 | ``` 12 | go run ./dynamicmtls/starter -target-host my.namespace.tmprl.cloud:7233 -namespace my.namespace -client-cert path/to/cert.pem -client-key path/to/key.pem 13 | ``` 14 | 15 | Note: 16 | 17 | If the server uses self-signed certificates and does not have the SAN set to the actual host, pass one of the following two options when starting the worker or the example above: 18 | 1. `-server-name` and provide the common name contained in the self-signed server certificate 19 | 2. `-insecure-skip-verify` which disables certificate and host name validation 20 | -------------------------------------------------------------------------------- /dynamicmtls/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/temporalio/samples-go/dynamicmtls" 8 | "github.com/temporalio/samples-go/helloworld" 9 | "go.temporal.io/sdk/client" 10 | "go.temporal.io/sdk/worker" 11 | ) 12 | 13 | func main() { 14 | // The client and worker are heavyweight objects that should be created once per process. 15 | clientOptions, err := dynamicmtls.ParseClientOptionFlags(os.Args[1:]) 16 | if err != nil { 17 | log.Fatalf("Invalid arguments: %v", err) 18 | } 19 | c, err := client.Dial(clientOptions) 20 | if err != nil { 21 | log.Fatalln("Unable to create client", err) 22 | } 23 | defer c.Close() 24 | 25 | w := worker.New(c, "hello-world-mtls", worker.Options{}) 26 | 27 | w.RegisterWorkflow(helloworld.Workflow) 28 | w.RegisterActivity(helloworld.Activity) 29 | 30 | err = w.Run(worker.InterruptCh()) 31 | if err != nil { 32 | log.Fatalln("Unable to start worker", err) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /eager-workflow-start/README.md: -------------------------------------------------------------------------------- 1 | ### Eager Workflow Start Sample 2 | 3 | Eager Workflow Start (EWS) is an experimental latency optimization with the goal of reducing the time it takes to start a workflow. 4 | 5 | When the starter and worker are collocated and aware of each other, they can interact while bypassing the server, saving a few time-intensive operations. 6 | 7 | Here we modify the `helloworld` sample, ensuring that starter and worker run in the same process, and share the client. Also, the request eager mode flag is set in the start workflow options. 8 | 9 | ### Steps to run this sample: 10 | 11 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use) with eager workflow start enabled by a dynamic config value. 12 | ``` 13 | temporal server start-dev --dynamic-config-value system.enableEagerWorkflowStart=true 14 | ``` 15 | 2) Run the following command to start the combined worker and example 16 | ``` 17 | go run eager-workflow-start/main.go 18 | ``` 19 | 20 | -------------------------------------------------------------------------------- /early-return/README.md: -------------------------------------------------------------------------------- 1 | ### Early-Return Sample 2 | 3 | This sample demonstrates an early-return from a workflow. 4 | 5 | By utilizing Update-with-Start, a client can start a new workflow and synchronously receive 6 | a response mid-workflow, while the workflow continues to run to completion. 7 | 8 | See [shopping cart](https://github.com/temporalio/samples-go/tree/main/shoppingcart) 9 | for Update-with-Start being used for lazy initialization. 10 | 11 | ### Steps to run this sample: 12 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 13 | 14 | NOTE: frontend.enableExecuteMultiOperation=true must be configured for the server 15 | in order to use Update-with-Start. For example: 16 | ``` 17 | temporal server start-dev --dynamic-config-value frontend.enableExecuteMultiOperation=true 18 | ``` 19 | 20 | 2) Run the following command to start the worker 21 | ``` 22 | go run early-return/worker/main.go 23 | ``` 24 | 3) Run the following command to start the example 25 | ``` 26 | go run early-return/starter/main.go 27 | ``` 28 | -------------------------------------------------------------------------------- /early-return/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/temporalio/samples-go/early-return" 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | ) 10 | 11 | func main() { 12 | // The client and worker are heavyweight objects that should be created once per process. 13 | c, err := client.Dial(client.Options{}) 14 | if err != nil { 15 | log.Fatalln("Unable to create client", err) 16 | } 17 | defer c.Close() 18 | 19 | w := worker.New(c, earlyreturn.TaskQueueName, worker.Options{}) 20 | 21 | w.RegisterWorkflow(earlyreturn.Workflow) 22 | w.RegisterActivity(earlyreturn.CompleteTransaction) 23 | w.RegisterActivity(earlyreturn.CancelTransaction) 24 | 25 | err = w.Run(worker.InterruptCh()) 26 | if err != nil { 27 | log.Fatalln("Unable to start worker", err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /encryption/README.md: -------------------------------------------------------------------------------- 1 | ### Steps to run this sample: 2 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 3 | 2) Run the following command to start the remote codec server 4 | ``` 5 | go run ./codec-server 6 | ``` 7 | 3) Run the following command to start the worker 8 | ``` 9 | go run worker/main.go 10 | ``` 11 | 4) Run the following command to start the example 12 | ``` 13 | go run starter/main.go 14 | ``` 15 | 5) Run the following command and see the payloads cannot be decoded 16 | ``` 17 | tctl workflow show --wid encryption_workflowID 18 | ``` 19 | 6) Run the following command and see the decoded payloads 20 | ``` 21 | tctl --codec_endpoint 'http://localhost:8081/' workflow show --wid encryption_workflowID 22 | ``` 23 | 24 | Note: The codec server provided in this sample does not support decoding payloads for the Temporal Web UI, only tctl. 25 | Please see the [codec-server](../codec-server/) sample for a more complete example of a codec server which provides UI decoding and oauth. 26 | -------------------------------------------------------------------------------- /encryption/crypt.go: -------------------------------------------------------------------------------- 1 | package encryption 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "crypto/rand" 7 | "fmt" 8 | "io" 9 | ) 10 | 11 | func encrypt(plainData []byte, key []byte) ([]byte, error) { 12 | c, err := aes.NewCipher(key) 13 | if err != nil { 14 | return nil, err 15 | } 16 | 17 | gcm, err := cipher.NewGCM(c) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | nonce := make([]byte, gcm.NonceSize()) 23 | if _, err = io.ReadFull(rand.Reader, nonce); err != nil { 24 | return nil, err 25 | } 26 | 27 | return gcm.Seal(nonce, nonce, plainData, nil), nil 28 | } 29 | 30 | func decrypt(encryptedData []byte, key []byte) ([]byte, error) { 31 | c, err := aes.NewCipher(key) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | gcm, err := cipher.NewGCM(c) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | nonceSize := gcm.NonceSize() 42 | if len(encryptedData) < nonceSize { 43 | return nil, fmt.Errorf("ciphertext too short: %v", encryptedData) 44 | } 45 | 46 | nonce, encryptedData := encryptedData[:nonceSize], encryptedData[nonceSize:] 47 | return gcm.Open(nil, nonce, encryptedData, nil) 48 | } 49 | -------------------------------------------------------------------------------- /encryption/data_converter_test.go: -------------------------------------------------------------------------------- 1 | package encryption 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | "go.temporal.io/sdk/converter" 9 | ) 10 | 11 | func Test_DataConverter(t *testing.T) { 12 | defaultDc := converter.GetDefaultDataConverter() 13 | 14 | ctx := context.Background() 15 | ctx = context.WithValue(ctx, PropagateKey, CryptContext{KeyID: "test"}) 16 | 17 | cryptDc := NewEncryptionDataConverter( 18 | converter.GetDefaultDataConverter(), 19 | DataConverterOptions{}, 20 | ) 21 | cryptDcWc := cryptDc.WithContext(ctx) 22 | 23 | defaultPayloads, err := defaultDc.ToPayloads("Testing") 24 | require.NoError(t, err) 25 | 26 | encryptedPayloads, err := cryptDcWc.ToPayloads("Testing") 27 | require.NoError(t, err) 28 | 29 | require.NotEqual(t, defaultPayloads.Payloads[0].GetData(), encryptedPayloads.Payloads[0].GetData()) 30 | 31 | var result string 32 | err = cryptDc.FromPayloads(encryptedPayloads, &result) 33 | require.NoError(t, err) 34 | 35 | require.Equal(t, "Testing", result) 36 | } 37 | -------------------------------------------------------------------------------- /encryption/workflow_test.go: -------------------------------------------------------------------------------- 1 | package encryption 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/mock" 7 | "github.com/stretchr/testify/require" 8 | "go.temporal.io/sdk/testsuite" 9 | ) 10 | 11 | func Test_Workflow(t *testing.T) { 12 | testSuite := &testsuite.WorkflowTestSuite{} 13 | env := testSuite.NewTestWorkflowEnvironment() 14 | 15 | // Mock activity implementation 16 | env.OnActivity(Activity, mock.Anything, mock.Anything).Return("Hello Temporal!", nil) 17 | 18 | env.ExecuteWorkflow(Workflow, "Temporal") 19 | 20 | require.True(t, env.IsWorkflowCompleted()) 21 | require.NoError(t, env.GetWorkflowError()) 22 | var result string 23 | require.NoError(t, env.GetWorkflowResult(&result)) 24 | require.Equal(t, "Hello Temporal!", result) 25 | } 26 | -------------------------------------------------------------------------------- /expense/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | "go.temporal.io/sdk/client" 9 | 10 | "github.com/temporalio/samples-go/expense" 11 | ) 12 | 13 | func main() { 14 | // The client is a heavyweight object that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | expenseID := uuid.New() 24 | workflowOptions := client.StartWorkflowOptions{ 25 | ID: "expense_" + expenseID, 26 | TaskQueue: "expense", 27 | } 28 | 29 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, expense.SampleExpenseWorkflow, expenseID) 30 | if err != nil { 31 | log.Fatalln("Unable to execute workflow", err) 32 | } 33 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 34 | 35 | } 36 | -------------------------------------------------------------------------------- /expense/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/expense" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "expense", worker.Options{}) 23 | 24 | w.RegisterWorkflow(expense.SampleExpenseWorkflow) 25 | w.RegisterActivity(expense.CreateExpenseActivity) 26 | w.RegisterActivity(expense.WaitForDecisionActivity) 27 | w.RegisterActivity(expense.PaymentActivity) 28 | 29 | err = w.Run(worker.InterruptCh()) 30 | if err != nil { 31 | log.Fatalln("Unable to start worker", err) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /fileprocessing/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | "go.temporal.io/sdk/client" 9 | 10 | "github.com/temporalio/samples-go/fileprocessing" 11 | ) 12 | 13 | func main() { 14 | // The client is a heavyweight object that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | fileID := uuid.New() 24 | workflowOptions := client.StartWorkflowOptions{ 25 | ID: "fileprocessing_" + fileID, 26 | TaskQueue: "fileprocessing", 27 | } 28 | 29 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, fileprocessing.SampleFileProcessingWorkflow, fileID) 30 | if err != nil { 31 | log.Fatalln("Unable to execute workflow", err) 32 | } 33 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 34 | } 35 | -------------------------------------------------------------------------------- /fileprocessing/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/fileprocessing" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | workerOptions := worker.Options{ 23 | EnableSessionWorker: true, // Important for a worker to participate in the session 24 | } 25 | w := worker.New(c, "fileprocessing", workerOptions) 26 | 27 | w.RegisterWorkflow(fileprocessing.SampleFileProcessingWorkflow) 28 | w.RegisterActivity(&fileprocessing.Activities{BlobStore: &fileprocessing.BlobStore{}}) 29 | 30 | err = w.Run(worker.InterruptCh()) 31 | if err != nil { 32 | log.Fatalln("Unable to start worker", err) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /fileprocessing/workflow_test.go: -------------------------------------------------------------------------------- 1 | package fileprocessing 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/mock" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/stretchr/testify/suite" 10 | "go.temporal.io/sdk/testsuite" 11 | ) 12 | 13 | type UnitTestSuite struct { 14 | suite.Suite 15 | testsuite.WorkflowTestSuite 16 | } 17 | 18 | func TestUnitTestSuite(t *testing.T) { 19 | suite.Run(t, new(UnitTestSuite)) 20 | } 21 | 22 | func (s *UnitTestSuite) Test_SampleFileProcessingWorkflow() { 23 | env := s.NewTestWorkflowEnvironment() 24 | env.SetWorkerOptions(worker.Options{ 25 | EnableSessionWorker: true, // Important for a worker to participate in the session 26 | }) 27 | var a *Activities 28 | 29 | env.OnActivity(a.DownloadFileActivity, mock.Anything, "file1").Return("file2", nil) 30 | env.OnActivity(a.ProcessFileActivity, mock.Anything, "file2").Return("file3", nil) 31 | env.OnActivity(a.UploadFileActivity, mock.Anything, "file3").Return(nil) 32 | 33 | env.RegisterActivity(a) 34 | 35 | env.ExecuteWorkflow(SampleFileProcessingWorkflow, "file1") 36 | 37 | s.True(env.IsWorkflowCompleted()) 38 | s.NoError(env.GetWorkflowError()) 39 | 40 | env.AssertExpectations(s.T()) 41 | } 42 | -------------------------------------------------------------------------------- /goroutine/README.md: -------------------------------------------------------------------------------- 1 | This sample Workflow Definition demonstrates how to use multiple workflow safe goroutines (instead of native ones) to 2 | process multiple sequences of activities in parallel. 3 | In Temporal Workflow Definition, you should not use `go` keyword to start goroutines. Instead, you use the `workflow.Go` 4 | function, which spawns a coroutine that is never run in parallel, but instead deterministically. 5 | 6 | To see more information on goroutines and multithreading, see our 7 | [docs on Go SDK multithreading](https://docs.temporal.io/develop/go/go-sdk-multithreading). 8 | 9 | ### Steps to run this sample: 10 | 11 | 1) Run a [Temporal Service](https://github.com/temporalio/samples-go/tree/main/#how-to-use) 12 | 2) Run the following command to start the worker 13 | 14 | ``` 15 | go run goroutine/worker/main.go 16 | ``` 17 | 18 | 3) Run the following command to start the example 19 | 20 | ``` 21 | go run goroutine/starter/main.go 22 | ``` 23 | -------------------------------------------------------------------------------- /goroutine/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "github.com/temporalio/samples-go/goroutine" 6 | "log" 7 | 8 | "github.com/pborman/uuid" 9 | "go.temporal.io/sdk/client" 10 | ) 11 | 12 | func main() { 13 | // The client is a heavyweight object that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | workflowOptions := client.StartWorkflowOptions{ 23 | ID: "goroutine-" + uuid.New(), 24 | TaskQueue: "goroutine", 25 | } 26 | 27 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, goroutine.SampleGoroutineWorkflow, 5) 28 | if err != nil { 29 | log.Fatalln("Unable to execute workflow", err) 30 | } 31 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 32 | } 33 | -------------------------------------------------------------------------------- /goroutine/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/temporalio/samples-go/goroutine" 5 | "log" 6 | 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | ) 10 | 11 | func main() { 12 | // The client and worker are heavyweight objects that should be created once per process. 13 | c, err := client.Dial(client.Options{ 14 | HostPort: client.DefaultHostPort, 15 | }) 16 | if err != nil { 17 | log.Fatalln("Unable to create client", err) 18 | } 19 | defer c.Close() 20 | 21 | w := worker.New(c, "goroutine", worker.Options{}) 22 | 23 | w.RegisterWorkflow(goroutine.SampleGoroutineWorkflow) 24 | w.RegisterActivity(goroutine.Step1) 25 | w.RegisterActivity(goroutine.Step2) 26 | 27 | err = w.Run(worker.InterruptCh()) 28 | if err != nil { 29 | log.Fatalln("Unable to start worker", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /greetings/README.md: -------------------------------------------------------------------------------- 1 | ### Steps to run this sample: 2 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 3 | 2) Run the following command to start the worker 4 | ``` 5 | go run greetings/worker/main.go 6 | ``` 7 | 3) Run the following command to start the example 8 | ``` 9 | go run greetings/starter/main.go 10 | ``` 11 | -------------------------------------------------------------------------------- /greetings/activities.go: -------------------------------------------------------------------------------- 1 | package greetings 2 | 3 | import "fmt" 4 | 5 | // @@@SNIPSTART samples-go-dependency-sharing-activities 6 | type Activities struct { 7 | Name string 8 | Greeting string 9 | } 10 | 11 | // GetGreeting Activity. 12 | func (a *Activities) GetGreeting() (string, error) { 13 | return a.Greeting, nil 14 | } 15 | 16 | // @@@SNIPEND 17 | 18 | // GetName Activity. 19 | func (a *Activities) GetName() (string, error) { 20 | return a.Name, nil 21 | } 22 | 23 | // SayGreeting Activity. 24 | func (a *Activities) SayGreeting(greeting string, name string) (string, error) { 25 | result := fmt.Sprintf("Greeting: %s %s!\n", greeting, name) 26 | return result, nil 27 | } 28 | -------------------------------------------------------------------------------- /greetings/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | "go.temporal.io/sdk/client" 9 | 10 | "github.com/temporalio/samples-go/greetings" 11 | ) 12 | 13 | func main() { 14 | // The client is a heavyweight object that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | workflowOptions := client.StartWorkflowOptions{ 24 | ID: "greetings_" + uuid.New(), 25 | TaskQueue: "greetings", 26 | } 27 | 28 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, greetings.GreetingSample) 29 | if err != nil { 30 | log.Fatalln("Unable to execute workflow", err) 31 | } 32 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 33 | 34 | // Synchronously wait for the workflow completion. 35 | var result string 36 | err = we.Get(context.Background(), &result) 37 | if err != nil { 38 | log.Fatalln("Unable get workflow result", err) 39 | } 40 | log.Println("Workflow result:", result) 41 | } 42 | -------------------------------------------------------------------------------- /greetings/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/greetings" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "greetings", worker.Options{}) 23 | 24 | w.RegisterWorkflow(greetings.GreetingSample) 25 | activities := &greetings.Activities{Name: "Temporal", Greeting: "Hello"} 26 | w.RegisterActivity(activities) 27 | 28 | err = w.Run(worker.InterruptCh()) 29 | if err != nil { 30 | log.Fatalln("Unable to start worker", err) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /greetings/workflow_test.go: -------------------------------------------------------------------------------- 1 | package greetings 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/suite" 7 | "go.temporal.io/sdk/testsuite" 8 | ) 9 | 10 | type UnitTestSuite struct { 11 | suite.Suite 12 | testsuite.WorkflowTestSuite 13 | } 14 | 15 | func TestUnitTestSuite(t *testing.T) { 16 | suite.Run(t, new(UnitTestSuite)) 17 | } 18 | 19 | func (s *UnitTestSuite) Test_SampleGreetingsWorkflow() { 20 | env := s.NewTestWorkflowEnvironment() 21 | var a *Activities 22 | //env.RegisterActivity(a) 23 | 24 | env.OnActivity(a.GetGreeting).Return("Hello", nil) 25 | env.OnActivity(a.GetName).Return("World", nil) 26 | env.OnActivity(a.SayGreeting, "Hello", "World").Return("Hello World!", nil) 27 | 28 | env.ExecuteWorkflow(GreetingSample) 29 | 30 | s.True(env.IsWorkflowCompleted()) 31 | s.NoError(env.GetWorkflowError()) 32 | 33 | env.AssertExpectations(s.T()) 34 | } 35 | -------------------------------------------------------------------------------- /greetingslocal/README.md: -------------------------------------------------------------------------------- 1 | ### Steps to run this sample: 2 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 3 | 2) Run the following command to start the worker 4 | ``` 5 | go run greetingslocal/worker/main.go 6 | ``` 7 | 3) Run the following command to start the example 8 | ``` 9 | go run greetingslocal/starter/main.go 10 | ``` 11 | -------------------------------------------------------------------------------- /greetingslocal/activities.go: -------------------------------------------------------------------------------- 1 | package greetingslocal 2 | 3 | import "fmt" 4 | 5 | type Activities struct { 6 | Name string 7 | Greeting string 8 | } 9 | 10 | // GetName Activity. 11 | func (a *Activities) GetName() (string, error) { 12 | return a.Name, nil 13 | } 14 | 15 | // GetGreeting Activity. 16 | func (a *Activities) GetGreeting() (string, error) { 17 | return a.Greeting, nil 18 | } 19 | 20 | // SayGreeting Activity. 21 | func (a *Activities) SayGreeting(greeting string, name string) (string, error) { 22 | result := fmt.Sprintf("Greeting: %s %s!\n", greeting, name) 23 | return result, nil 24 | } 25 | -------------------------------------------------------------------------------- /greetingslocal/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | "github.com/temporalio/samples-go/greetingslocal" 9 | "go.temporal.io/sdk/client" 10 | ) 11 | 12 | func main() { 13 | // The client is a heavyweight object that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | workflowOptions := client.StartWorkflowOptions{ 23 | ID: "greetings_" + uuid.New(), 24 | TaskQueue: "greetings-local", 25 | } 26 | 27 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, greetingslocal.GreetingSample) 28 | if err != nil { 29 | log.Fatalln("Unable to execute workflow", err) 30 | } 31 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 32 | } 33 | -------------------------------------------------------------------------------- /greetingslocal/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/temporalio/samples-go/greetingslocal" 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | ) 10 | 11 | func main() { 12 | // The client and worker are heavyweight objects that should be created once per process. 13 | c, err := client.Dial(client.Options{ 14 | HostPort: client.DefaultHostPort, 15 | }) 16 | if err != nil { 17 | log.Fatalln("Unable to create client", err) 18 | } 19 | defer c.Close() 20 | 21 | w := worker.New(c, "greetings-local", worker.Options{}) 22 | 23 | w.RegisterWorkflow(greetingslocal.GreetingSample) 24 | activities := &greetingslocal.Activities{Name: "Temporal", Greeting: "Hello"} 25 | w.RegisterActivity(activities) 26 | 27 | err = w.Run(worker.InterruptCh()) 28 | if err != nil { 29 | log.Fatalln("Unable to start worker", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /greetingslocal/workflow_test.go: -------------------------------------------------------------------------------- 1 | package greetingslocal 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/suite" 7 | "go.temporal.io/sdk/testsuite" 8 | ) 9 | 10 | type UnitTestSuite struct { 11 | suite.Suite 12 | testsuite.WorkflowTestSuite 13 | } 14 | 15 | func TestUnitTestSuite(t *testing.T) { 16 | suite.Run(t, new(UnitTestSuite)) 17 | } 18 | 19 | func (s *UnitTestSuite) Test_SampleGreetingsWorkflow() { 20 | env := s.NewTestWorkflowEnvironment() 21 | var a *Activities 22 | //env.RegisterActivity(a) 23 | 24 | env.OnActivity(a.GetGreeting).Return("Hello", nil) 25 | env.OnActivity(a.GetName).Return("World", nil) 26 | env.OnActivity(a.SayGreeting, "Hello", "World").Return("Hello World!", nil) 27 | 28 | env.ExecuteWorkflow(GreetingSample) 29 | 30 | s.True(env.IsWorkflowCompleted()) 31 | s.NoError(env.GetWorkflowError()) 32 | 33 | env.AssertExpectations(s.T()) 34 | } 35 | -------------------------------------------------------------------------------- /grpc-proxy/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | grpcproxy "github.com/temporalio/samples-go/grpc-proxy" 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | ) 10 | 11 | func main() { 12 | // The client and worker are heavyweight objects that should be created once per process. 13 | c, err := client.Dial(client.Options{ 14 | HostPort: "localhost:8081", 15 | }) 16 | if err != nil { 17 | log.Fatalln("Unable to create client", err) 18 | } 19 | defer c.Close() 20 | 21 | w := worker.New(c, "grpcproxy", worker.Options{}) 22 | 23 | w.RegisterWorkflow(grpcproxy.Workflow) 24 | w.RegisterActivity(grpcproxy.Activity) 25 | 26 | err = w.Run(worker.InterruptCh()) 27 | if err != nil { 28 | log.Fatalln("Unable to start worker", err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /helloworld-apiKey/README.md: -------------------------------------------------------------------------------- 1 | ### Steps to run this sample: 2 | 1) Configure a [Temporal Server](https://github.com/temporalio/samples-go/tree/main/#how-to-use) (such as Temporal Cloud) with apiKey. 3 | 4 | 2) Run the following command to start the worker 5 | ``` 6 | go run ./helloworld-apiKey/worker \ 7 | -target-host my.namespace.tmprl.cloud:7233 \ 8 | -namespace my.namespace \ 9 | -api-key CLIENT_API_KEY 10 | ``` 11 | 3) Run the following command to start the example 12 | ``` 13 | go run ./helloworld-apiKey/starter \ 14 | -target-host my.namespace.tmprl.cloud:7233 \ 15 | -namespace my.namespace \ 16 | -api-key CLIENT_API_KEY 17 | ``` -------------------------------------------------------------------------------- /helloworld-apiKey/helloworld_test.go: -------------------------------------------------------------------------------- 1 | package helloworldapiKey 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/mock" 7 | "github.com/stretchr/testify/require" 8 | "go.temporal.io/sdk/testsuite" 9 | ) 10 | 11 | func Test_Workflow(t *testing.T) { 12 | testSuite := &testsuite.WorkflowTestSuite{} 13 | env := testSuite.NewTestWorkflowEnvironment() 14 | 15 | // Mock activity implementation 16 | env.OnActivity(Activity, mock.Anything, "Temporal").Return("Hello Temporal!", nil) 17 | 18 | env.ExecuteWorkflow(Workflow, "Temporal") 19 | 20 | require.True(t, env.IsWorkflowCompleted()) 21 | require.NoError(t, env.GetWorkflowError()) 22 | var result string 23 | require.NoError(t, env.GetWorkflowResult(&result)) 24 | require.Equal(t, "Hello Temporal!", result) 25 | } 26 | 27 | func Test_Activity(t *testing.T) { 28 | testSuite := &testsuite.WorkflowTestSuite{} 29 | env := testSuite.NewTestActivityEnvironment() 30 | env.RegisterActivity(Activity) 31 | 32 | val, err := env.ExecuteActivity(Activity, "World") 33 | require.NoError(t, err) 34 | 35 | var res string 36 | require.NoError(t, val.Get(&res)) 37 | require.Equal(t, "Hello World!", res) 38 | } 39 | -------------------------------------------------------------------------------- /helloworld-apiKey/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/temporalio/samples-go/helloworld-apiKey" 8 | "go.temporal.io/sdk/client" 9 | "go.temporal.io/sdk/worker" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | clientOptions, err := helloworldapiKey.ParseClientOptionFlags(os.Args[1:]) 15 | if err != nil { 16 | log.Fatalf("Invalid arguments: %v", err) 17 | } 18 | c, err := client.Dial(clientOptions) 19 | if err != nil { 20 | log.Fatalln("Unable to create client", err) 21 | } 22 | defer c.Close() 23 | 24 | w := worker.New(c, "hello-world-apiKey", worker.Options{}) 25 | 26 | w.RegisterWorkflow(helloworldapiKey.Workflow) 27 | w.RegisterActivity(helloworldapiKey.Activity) 28 | 29 | err = w.Run(worker.InterruptCh()) 30 | if err != nil { 31 | log.Fatalln("Unable to start worker", err) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /helloworld/README.md: -------------------------------------------------------------------------------- 1 | ### Steps to run this sample: 2 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 3 | 2) Run the following command to start the worker 4 | ``` 5 | go run helloworld/worker/main.go 6 | ``` 7 | 3) Run the following command to start the example 8 | ``` 9 | go run helloworld/starter/main.go 10 | ``` 11 | -------------------------------------------------------------------------------- /helloworld/helloworld.go: -------------------------------------------------------------------------------- 1 | package helloworld 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "go.temporal.io/sdk/activity" 8 | "go.temporal.io/sdk/workflow" 9 | ) 10 | 11 | // Workflow is a Hello World workflow definition. 12 | func Workflow(ctx workflow.Context, name string) (string, error) { 13 | ao := workflow.ActivityOptions{ 14 | StartToCloseTimeout: 10 * time.Second, 15 | } 16 | ctx = workflow.WithActivityOptions(ctx, ao) 17 | 18 | logger := workflow.GetLogger(ctx) 19 | logger.Info("HelloWorld workflow started", "name", name) 20 | 21 | var result string 22 | err := workflow.ExecuteActivity(ctx, Activity, name).Get(ctx, &result) 23 | if err != nil { 24 | logger.Error("Activity failed.", "Error", err) 25 | return "", err 26 | } 27 | 28 | logger.Info("HelloWorld workflow completed.", "result", result) 29 | 30 | return result, nil 31 | } 32 | 33 | func Activity(ctx context.Context, name string) (string, error) { 34 | logger := activity.GetLogger(ctx) 35 | logger.Info("Activity", "name", name) 36 | return "Hello " + name + "!", nil 37 | } 38 | -------------------------------------------------------------------------------- /helloworld/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "go.temporal.io/sdk/client" 8 | 9 | "github.com/temporalio/samples-go/helloworld" 10 | ) 11 | 12 | func main() { 13 | // The client is a heavyweight object that should be created once per process. 14 | c, err := client.Dial(client.Options{}) 15 | if err != nil { 16 | log.Fatalln("Unable to create client", err) 17 | } 18 | defer c.Close() 19 | 20 | workflowOptions := client.StartWorkflowOptions{ 21 | ID: "hello_world_workflowID", 22 | TaskQueue: "hello-world", 23 | } 24 | 25 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, helloworld.Workflow, "Temporal") 26 | if err != nil { 27 | log.Fatalln("Unable to execute workflow", err) 28 | } 29 | 30 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 31 | 32 | // Synchronously wait for the workflow completion. 33 | var result string 34 | err = we.Get(context.Background(), &result) 35 | if err != nil { 36 | log.Fatalln("Unable get workflow result", err) 37 | } 38 | log.Println("Workflow result:", result) 39 | } 40 | -------------------------------------------------------------------------------- /helloworld/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/helloworld" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{}) 15 | if err != nil { 16 | log.Fatalln("Unable to create client", err) 17 | } 18 | defer c.Close() 19 | 20 | w := worker.New(c, "hello-world", worker.Options{}) 21 | 22 | w.RegisterWorkflow(helloworld.Workflow) 23 | w.RegisterActivity(helloworld.Activity) 24 | 25 | err = w.Run(worker.InterruptCh()) 26 | if err != nil { 27 | log.Fatalln("Unable to start worker", err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /helloworldmtls/README.md: -------------------------------------------------------------------------------- 1 | ### Steps to run this sample: 2 | 1) Configure a [Temporal Server](https://github.com/temporalio/samples-go/tree/main/#how-to-use) (such as Temporal Cloud) with mTLS. 3 | 4 | 2) Run the following command to start the worker 5 | ``` 6 | go run ./helloworldmtls/worker -target-host my.namespace.tmprl.cloud:7233 -namespace my.namespace -client-cert path/to/cert.pem -client-key path/to/key.pem 7 | ``` 8 | 3) Run the following command to start the example 9 | ``` 10 | go run ./helloworldmtls/starter -target-host my.namespace.tmprl.cloud:7233 -namespace my.namespace -client-cert path/to/cert.pem -client-key path/to/key.pem 11 | ``` 12 | 13 | If the server uses self-signed certificates and does not have the SAN set to the actual host, pass one of the following two options when starting the worker or the example above: 14 | 1. `-server-name` and provide the common name contained in the self-signed server certificate 15 | 2. `-insecure-skip-verify` which disables certificate and host name validation 16 | -------------------------------------------------------------------------------- /helloworldmtls/helloworld_test.go: -------------------------------------------------------------------------------- 1 | package helloworldmtls 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/mock" 7 | "github.com/stretchr/testify/require" 8 | "go.temporal.io/sdk/testsuite" 9 | ) 10 | 11 | func Test_Workflow(t *testing.T) { 12 | testSuite := &testsuite.WorkflowTestSuite{} 13 | env := testSuite.NewTestWorkflowEnvironment() 14 | 15 | // Mock activity implementation 16 | env.OnActivity(Activity, mock.Anything, "Temporal").Return("Hello Temporal!", nil) 17 | 18 | env.ExecuteWorkflow(Workflow, "Temporal") 19 | 20 | require.True(t, env.IsWorkflowCompleted()) 21 | require.NoError(t, env.GetWorkflowError()) 22 | var result string 23 | require.NoError(t, env.GetWorkflowResult(&result)) 24 | require.Equal(t, "Hello Temporal!", result) 25 | } 26 | 27 | func Test_Activity(t *testing.T) { 28 | testSuite := &testsuite.WorkflowTestSuite{} 29 | env := testSuite.NewTestActivityEnvironment() 30 | env.RegisterActivity(Activity) 31 | 32 | val, err := env.ExecuteActivity(Activity, "World") 33 | require.NoError(t, err) 34 | 35 | var res string 36 | require.NoError(t, val.Get(&res)) 37 | require.Equal(t, "Hello World!", res) 38 | } 39 | -------------------------------------------------------------------------------- /helloworldmtls/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/temporalio/samples-go/helloworldmtls" 8 | "go.temporal.io/sdk/client" 9 | "go.temporal.io/sdk/worker" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | clientOptions, err := helloworldmtls.ParseClientOptionFlags(os.Args[1:]) 15 | if err != nil { 16 | log.Fatalf("Invalid arguments: %v", err) 17 | } 18 | c, err := client.Dial(clientOptions) 19 | if err != nil { 20 | log.Fatalln("Unable to create client", err) 21 | } 22 | defer c.Close() 23 | 24 | w := worker.New(c, "hello-world-mtls", worker.Options{}) 25 | 26 | w.RegisterWorkflow(helloworldmtls.Workflow) 27 | w.RegisterActivity(helloworldmtls.Activity) 28 | 29 | err = w.Run(worker.InterruptCh()) 30 | if err != nil { 31 | log.Fatalln("Unable to start worker", err) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /logger-interceptor/README.md: -------------------------------------------------------------------------------- 1 | # Interceptor Sample 2 | 3 | This sample shows how to make a worker interceptor that intercepts workflow and activity `GetLogger` calls to customize 4 | the logger. 5 | 6 | ### Steps to run this sample: 7 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 8 | 2) Run the following command to start the worker 9 | ``` 10 | go run ./logger-interceptor/worker 11 | ``` 12 | 3) Run the following command to start the example 13 | ``` 14 | go run ./logger-interceptor/starter 15 | ``` 16 | 17 | Notice the log output has the `WorkflowStartTime`/`ActivityStartTime` tags on the logs. 18 | -------------------------------------------------------------------------------- /logger-interceptor/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/temporalio/samples-go/logger-interceptor" 8 | "go.temporal.io/sdk/client" 9 | ) 10 | 11 | func main() { 12 | // The client is a heavyweight object that should be created once per process. 13 | c, err := client.Dial(client.Options{}) 14 | if err != nil { 15 | log.Fatalln("Unable to create client", err) 16 | } 17 | defer c.Close() 18 | 19 | workflowOptions := client.StartWorkflowOptions{ 20 | ID: "interceptor_workflowID", 21 | TaskQueue: "logger-interceptor", 22 | } 23 | 24 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, logger_interceptor.Workflow, "Temporal") 25 | if err != nil { 26 | log.Fatalln("Unable to execute workflow", err) 27 | } 28 | 29 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 30 | 31 | // Synchronously wait for the workflow completion. 32 | var result string 33 | err = we.Get(context.Background(), &result) 34 | if err != nil { 35 | log.Fatalln("Unable get workflow result", err) 36 | } 37 | log.Println("Workflow result:", result) 38 | } 39 | -------------------------------------------------------------------------------- /logger-interceptor/workflow.go: -------------------------------------------------------------------------------- 1 | package logger_interceptor 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "go.temporal.io/sdk/activity" 8 | "go.temporal.io/sdk/workflow" 9 | ) 10 | 11 | func Workflow(ctx workflow.Context, name string) (string, error) { 12 | ao := workflow.ActivityOptions{ 13 | StartToCloseTimeout: 10 * time.Second, 14 | } 15 | ctx = workflow.WithActivityOptions(ctx, ao) 16 | 17 | logger := workflow.GetLogger(ctx) 18 | logger.Info("HelloWorld workflow started", "name", name) 19 | 20 | var result string 21 | err := workflow.ExecuteActivity(ctx, Activity, name).Get(ctx, &result) 22 | if err != nil { 23 | logger.Error("Activity failed.", "Error", err) 24 | return "", err 25 | } 26 | 27 | logger.Info("HelloWorld workflow completed", "result", result) 28 | 29 | return result, nil 30 | } 31 | 32 | func Activity(ctx context.Context, name string) (string, error) { 33 | logger := activity.GetLogger(ctx) 34 | logger.Info("Activity", "name", name) 35 | return "Hello " + name + "!", nil 36 | } 37 | -------------------------------------------------------------------------------- /memo/README.md: -------------------------------------------------------------------------------- 1 | ### Steps to run this sample: 2 | 3 | 1) Before running this, you need to run [Temporal Server 1.18+](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 4 | 5 | 2) Run the following command to start the worker: 6 | ``` 7 | go run ./memo/worker/main.go 8 | ``` 9 | 10 | 3) Run the following command to start the example: 11 | ``` 12 | go run ./memo/starter/main.go 13 | ``` 14 | 15 | 4) Observe memo in the worker log: 16 | ``` 17 | ... 18 | 2022/09/12 12:36:20 INFO Current memo values: 19 | description=Test upsert memo workflow 20 | Namespace default TaskQueue memo WorkerID 18670@Rodrigo-Zhous-MacBook-Pro.local@ WorkflowType MemoWorkflow WorkflowID memo_b1326cd2-5123-4a61-b417-435285dd7214 RunID 38128800-4c41-4d85-ba1a-a26730ebcb47 Attempt 1 21 | 2022/09/12 12:36:20 INFO Workflow completed. Namespace default TaskQueue memo WorkerID 18670@Rodrigo-Zhous-MacBook-Pro.local@ WorkflowType MemoWorkflow WorkflowID memo_b1326cd2-5123-4a61-b417-435285dd7214 RunID 38128800-4c41-4d85-ba1a-a26730ebcb47 Attempt 1 22 | ``` 23 | -------------------------------------------------------------------------------- /memo/memo_workflow_test.go: -------------------------------------------------------------------------------- 1 | package memo 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/mock" 7 | "github.com/stretchr/testify/require" 8 | workflowpb "go.temporal.io/api/workflow/v1" 9 | "go.temporal.io/sdk/testsuite" 10 | ) 11 | 12 | func Test_Workflow(t *testing.T) { 13 | testSuite := &testsuite.WorkflowTestSuite{} 14 | env := testSuite.NewTestWorkflowEnvironment() 15 | env.RegisterActivity(DescribeWorkflow) 16 | 17 | // mock search attributes on start 18 | _ = env.SetMemoOnStart(map[string]interface{}{"description": "Test memo workflow"}) 19 | 20 | // mock upsert operations 21 | memo := map[string]interface{}{ 22 | "description": "Test upsert memo workflow", 23 | } 24 | env.OnUpsertMemo(memo).Return(nil).Once() 25 | 26 | // mock activity 27 | env.OnActivity(DescribeWorkflow, mock.Anything, mock.Anything).Return(&workflowpb.WorkflowExecutionInfo{}, nil).Once() 28 | 29 | env.ExecuteWorkflow(MemoWorkflow) 30 | require.True(t, env.IsWorkflowCompleted()) 31 | require.NoError(t, env.GetWorkflowError()) 32 | } 33 | -------------------------------------------------------------------------------- /memo/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | "go.temporal.io/sdk/client" 9 | 10 | "github.com/temporalio/samples-go/memo" 11 | ) 12 | 13 | func main() { 14 | // The client is a heavyweight object that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | workflowOptions := client.StartWorkflowOptions{ 24 | ID: "memo_" + uuid.New(), 25 | TaskQueue: "memo", 26 | Memo: map[string]interface{}{ 27 | "description": "Test memo workflow", 28 | }, 29 | } 30 | 31 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, memo.MemoWorkflow) 32 | if err != nil { 33 | log.Fatalln("Unable to execute workflow", err) 34 | } 35 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 36 | } 37 | -------------------------------------------------------------------------------- /memo/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | 10 | "github.com/temporalio/samples-go/memo" 11 | ) 12 | 13 | func main() { 14 | // The client and worker are heavyweight objects that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | ctx := context.WithValue(context.Background(), memo.ClientCtxKey, c) 24 | 25 | w := worker.New(c, "memo", worker.Options{ 26 | BackgroundActivityContext: ctx, 27 | }) 28 | 29 | w.RegisterWorkflow(memo.MemoWorkflow) 30 | w.RegisterActivity(memo.DescribeWorkflow) 31 | 32 | err = w.Run(worker.InterruptCh()) 33 | if err != nil { 34 | log.Fatalln("Unable to start worker", err) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /metrics/README.md: -------------------------------------------------------------------------------- 1 | ### Steps to run this sample: 2 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 3 | 2) Run the following command to start the worker 4 | ``` 5 | go run metrics/worker/main.go 6 | ``` 7 | 3) Run the following command to start the example 8 | ``` 9 | go run metrics/starter/main.go 10 | ``` 11 | 4) Check metrics at http://localhost:9090/metrics (this is where the Prometheus agent scrapes it from). 12 | -------------------------------------------------------------------------------- /metrics/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "go.temporal.io/sdk/client" 8 | 9 | "github.com/temporalio/samples-go/metrics" 10 | ) 11 | 12 | func main() { 13 | // The client is a heavyweight object that should be created once per process. 14 | c, err := client.Dial(client.Options{}) 15 | if err != nil { 16 | log.Fatalln("Unable to create client.", err) 17 | } 18 | defer c.Close() 19 | 20 | workflowOptions := client.StartWorkflowOptions{ 21 | ID: "metrics_workflowID", 22 | TaskQueue: "metrics", 23 | } 24 | 25 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, metrics.Workflow) 26 | if err != nil { 27 | log.Fatalln("Unable to execute workflow.", err) 28 | } 29 | 30 | log.Println("Started workflow.", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 31 | 32 | // Synchronously wait for the workflow completion. 33 | err = we.Get(context.Background(), nil) 34 | if err != nil { 35 | log.Fatalln("Unable to wait for workflow completition.", err) 36 | } 37 | 38 | log.Println("Check metrics at http://localhost:9090/metrics") 39 | } 40 | -------------------------------------------------------------------------------- /multi-history-replay/README.md: -------------------------------------------------------------------------------- 1 | This sample demonstrates getting multiple workflow histories and replaying them. 2 | 3 | ### Steps to run this sample: 4 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 5 | 2) Run the following command to start the worker 6 | ```shell script 7 | go run multi-history-replay/worker/main.go 8 | ``` 9 | 3) Run the following command to start mutiple parallel workflows. This simulates having existing workflows 10 | on the server. 11 | ```shell script 12 | go run multi-history-replay/starter/main.go 13 | ``` 14 | 4) Run the following command to replay the histories generated in step 3 15 | ```shell script 16 | go run multi-history-replay/replayer/main.go 17 | ``` 18 | -------------------------------------------------------------------------------- /multi-history-replay/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/helloworld" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{}) 15 | if err != nil { 16 | log.Fatalln("Unable to create client", err) 17 | } 18 | defer c.Close() 19 | 20 | w := worker.New(c, "multiple-history-replay", worker.Options{}) 21 | 22 | w.RegisterWorkflow(helloworld.Workflow) 23 | w.RegisterActivity(helloworld.Activity) 24 | 25 | err = w.Run(worker.InterruptCh()) 26 | if err != nil { 27 | log.Fatalln("Unable to start worker", err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /mutex/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | 10 | "github.com/temporalio/samples-go/mutex" 11 | ) 12 | 13 | func main() { 14 | // The client and worker are heavyweight objects that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | w := worker.New(c, "mutex", worker.Options{ 24 | BackgroundActivityContext: context.WithValue(context.Background(), mutex.ClientContextKey, c), 25 | }) 26 | 27 | w.RegisterActivity(mutex.SignalWithStartMutexWorkflowActivity) 28 | w.RegisterWorkflow(mutex.MutexWorkflow) 29 | w.RegisterWorkflow(mutex.SampleWorkflowWithMutex) 30 | 31 | err = w.Run(worker.InterruptCh()) 32 | if err != nil { 33 | log.Fatalln("Unable to start worker", err) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /nexus-cancelation/README.md: -------------------------------------------------------------------------------- 1 | # Nexus Cancellation 2 | 3 | This sample shows how to cancel a Nexus operation from a caller workflow. 4 | 5 | For more details on Nexus and how to set up to run this sample, please see the [Nexus Sample](../nexus/README.md). 6 | 7 | ### Running the sample 8 | 9 | In separate terminal windows: 10 | 11 | ### Nexus handler worker 12 | 13 | ``` 14 | cd handler 15 | go run ./worker \ 16 | -target-host localhost:7233 \ 17 | -namespace my-target-namespace 18 | ``` 19 | 20 | ### Nexus caller worker 21 | 22 | ``` 23 | cd caller 24 | go run ./worker \ 25 | -target-host localhost:7233 \ 26 | -namespace my-caller-namespace 27 | ``` 28 | 29 | ### Start caller workflow 30 | 31 | ``` 32 | cd caller 33 | go run ./starter \ 34 | -target-host localhost:7233 \ 35 | -namespace my-caller-namespace 36 | ``` 37 | 38 | ### Output 39 | 40 | which should result in: 41 | ``` 42 | 2025/02/27 12:57:40 Started workflow WorkflowID nexus_hello_caller_workflow_20240723195740 RunID c9789128-2fcd-4083-829d-95e43279f6d7 43 | 2025/02/27 12:57:40 Workflow result: ¡Hola! Nexus 👋 44 | ``` 45 | -------------------------------------------------------------------------------- /nexus-cancelation/caller/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | 10 | "github.com/temporalio/samples-go/nexus-cancelation/caller" 11 | "github.com/temporalio/samples-go/nexus/options" 12 | ) 13 | 14 | func main() { 15 | // The client and worker are heavyweight objects that should be created once per process. 16 | clientOptions, err := options.ParseClientOptionFlags(os.Args[1:]) 17 | if err != nil { 18 | log.Fatalf("Invalid arguments: %v", err) 19 | } 20 | c, err := client.Dial(clientOptions) 21 | if err != nil { 22 | log.Fatalln("Unable to create client", err) 23 | } 24 | defer c.Close() 25 | 26 | w := worker.New(c, caller.TaskQueue, worker.Options{}) 27 | w.RegisterWorkflow(caller.HelloCallerWorkflow) 28 | err = w.Run(worker.InterruptCh()) 29 | if err != nil { 30 | log.Fatalln("Unable to start worker", err) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /nexus-context-propagation/README.md: -------------------------------------------------------------------------------- 1 | # Nexus Context Propagation 2 | 3 | This sample shows how to propagate context through client calls, workflows, and Nexus headers. 4 | 5 | For more details on Nexus and how to set up to run this sample, please see the [Nexus Sample](../nexus/README.md). 6 | 7 | ### Running the sample 8 | 9 | In separate terminal windows: 10 | 11 | ### Nexus handler worker 12 | 13 | ``` 14 | cd handler 15 | go run ./worker \ 16 | -target-host localhost:7233 \ 17 | -namespace my-target-namespace 18 | ``` 19 | 20 | ### Nexus caller worker 21 | 22 | ``` 23 | cd caller 24 | go run ./worker \ 25 | -target-host localhost:7233 \ 26 | -namespace my-caller-namespace 27 | ``` 28 | 29 | ### Start caller workflow 30 | 31 | ``` 32 | cd caller 33 | go run ./starter \ 34 | -target-host localhost:7233 \ 35 | -namespace my-caller-namespace 36 | ``` 37 | 38 | ### Output 39 | 40 | which should result in: 41 | ``` 42 | 2025/02/27 12:57:40 Started workflow WorkflowID nexus_hello_caller_workflow_20240723195740 RunID c9789128-2fcd-4083-829d-95e43279f6d7 43 | 2025/02/27 12:57:40 Workflow result: ¡Hola! Nexus, caller-id: samples-go 👋 44 | ``` 45 | -------------------------------------------------------------------------------- /nexus/caller/worker/main.go: -------------------------------------------------------------------------------- 1 | // @@@SNIPSTART samples-go-nexus-caller-worker 2 | package main 3 | 4 | import ( 5 | "log" 6 | "os" 7 | 8 | "github.com/temporalio/samples-go/nexus/caller" 9 | "github.com/temporalio/samples-go/nexus/options" 10 | 11 | "go.temporal.io/sdk/client" 12 | "go.temporal.io/sdk/worker" 13 | ) 14 | 15 | func main() { 16 | // The client and worker are heavyweight objects that should be created once per process. 17 | clientOptions, err := options.ParseClientOptionFlags(os.Args[1:]) 18 | if err != nil { 19 | log.Fatalf("Invalid arguments: %v", err) 20 | } 21 | c, err := client.Dial(clientOptions) 22 | if err != nil { 23 | log.Fatalln("Unable to create client", err) 24 | } 25 | defer c.Close() 26 | 27 | w := worker.New(c, caller.TaskQueue, worker.Options{}) 28 | 29 | w.RegisterWorkflow(caller.EchoCallerWorkflow) 30 | w.RegisterWorkflow(caller.HelloCallerWorkflow) 31 | 32 | err = w.Run(worker.InterruptCh()) 33 | if err != nil { 34 | log.Fatalln("Unable to start worker", err) 35 | } 36 | } 37 | // @@@SNIPEND -------------------------------------------------------------------------------- /nexus/service/api.go: -------------------------------------------------------------------------------- 1 | // @@@SNIPSTART samples-go-nexus-service 2 | package service 3 | 4 | const HelloServiceName = "my-hello-service" 5 | 6 | // Echo operation 7 | const EchoOperationName = "echo" 8 | 9 | type EchoInput struct { 10 | Message string 11 | } 12 | 13 | type EchoOutput EchoInput 14 | 15 | // Hello operation 16 | const HelloOperationName = "say-hello" 17 | 18 | type Language string 19 | 20 | const ( 21 | EN Language = "en" 22 | FR Language = "fr" 23 | DE Language = "de" 24 | ES Language = "es" 25 | TR Language = "tr" 26 | ) 27 | 28 | type HelloInput struct { 29 | Name string 30 | Language Language 31 | } 32 | 33 | type HelloOutput struct { 34 | Message string 35 | } 36 | // @@@SNIPEND -------------------------------------------------------------------------------- /nexus/service/description.md: -------------------------------------------------------------------------------- 1 | ## Service: [my-hello-service](https://github.com/temporalio/samples-go/blob/main/nexus/service/api.go) 2 | - operation: `echo` 3 | - operation: `say-hello` 4 | 5 | See https://github.com/temporalio/samples-go/blob/main/nexus/service/api.go for Input / Output types. 6 | -------------------------------------------------------------------------------- /opentelemetry/honeycomb_traces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/samples-go/31e215be9ee3e8db6b3e649480aa098bbdd8fc5f/opentelemetry/honeycomb_traces.png -------------------------------------------------------------------------------- /opentelemetry/opentelemetry_setup.go: -------------------------------------------------------------------------------- 1 | package opentelemetry 2 | 3 | import ( 4 | "go.opentelemetry.io/otel" 5 | "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" 6 | "go.opentelemetry.io/otel/propagation" 7 | "go.opentelemetry.io/otel/sdk/resource" 8 | sdktrace "go.opentelemetry.io/otel/sdk/trace" 9 | semconv "go.opentelemetry.io/otel/semconv/v1.17.0" 10 | ) 11 | 12 | func InitializeGlobalTracerProvider() (*sdktrace.TracerProvider, error) { 13 | // Initialize tracer 14 | exp, err := stdouttrace.New(stdouttrace.WithPrettyPrint()) 15 | if err != nil { 16 | return nil, err 17 | } 18 | tp := sdktrace.NewTracerProvider( 19 | sdktrace.WithBatcher(exp), 20 | sdktrace.WithResource(resource.NewWithAttributes( 21 | semconv.SchemaURL, 22 | semconv.ServiceName("temporal-example"), 23 | semconv.ServiceVersion("0.0.1"), 24 | )), 25 | ) 26 | otel.SetTracerProvider(tp) 27 | 28 | otel.SetTextMapPropagator( 29 | propagation.NewCompositeTextMapPropagator( 30 | propagation.TraceContext{}, 31 | propagation.Baggage{}, 32 | ), 33 | ) 34 | 35 | return tp, nil 36 | } 37 | -------------------------------------------------------------------------------- /pickfirst/README.md: -------------------------------------------------------------------------------- 1 | ### Steps to run this sample: 2 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 3 | 2) Run the following command to start the worker 4 | ``` 5 | go run pickfirst/worker/main.go 6 | ``` 7 | 3) Run the following command to start the example 8 | ``` 9 | go run pickfirst/starter/main.go 10 | ``` 11 | -------------------------------------------------------------------------------- /pickfirst/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | "go.temporal.io/sdk/client" 9 | 10 | "github.com/temporalio/samples-go/pickfirst" 11 | ) 12 | 13 | func main() { 14 | // The client is a heavyweight object that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | workflowOptions := client.StartWorkflowOptions{ 24 | ID: "pick-first_" + uuid.New(), 25 | TaskQueue: "pick-first", 26 | } 27 | 28 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, pickfirst.SamplePickFirstWorkflow) 29 | if err != nil { 30 | log.Fatalln("Unable to execute workflow", err) 31 | } 32 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 33 | } 34 | -------------------------------------------------------------------------------- /pickfirst/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/pickfirst" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "pick-first", worker.Options{}) 23 | 24 | w.RegisterWorkflow(pickfirst.SamplePickFirstWorkflow) 25 | w.RegisterActivity(pickfirst.SampleActivity) 26 | 27 | err = w.Run(worker.InterruptCh()) 28 | if err != nil { 29 | log.Fatalln("Unable to start worker", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /polling/README.md: -------------------------------------------------------------------------------- 1 | # Polling 2 | 3 | These samples show three different best practices for polling. 4 | 5 | 1. [Frequently Polling Activity](frequent/README.md) 6 | 2. [Infrequently Polling Activity](infrequent/README.md) 7 | 3. [Periodic Polling of a sequence of activities](periodic_sequence/README.md) 8 | 9 | The samples are based on [this](https://community.temporal.io/t/what-is-the-best-practice-for-a-polling-activity/328/2) community forum thread. -------------------------------------------------------------------------------- /polling/frequent/README.md: -------------------------------------------------------------------------------- 1 | ## Frequent polling 2 | 3 | This sample shows how we can implement frequent polling (1 second or faster) inside our Activity. 4 | The implementation is a loop that polls our service and then sleeps for the poll interval (1 second in the sample). 5 | 6 | To ensure that polling activity is restarted in a timely manner, we make sure that it heartbeats on every iteration. 7 | Note that heartbeating only works if we set the HeartBeatTimeout to a shorter value than the activity 8 | StartToClose timeout. 9 | 10 | ### Steps to run this sample: 11 | 1) You need a Temporal service running. See details in README.md 12 | 2) Run the following command to start the worker 13 | ``` 14 | go run frequent/worker/main.go 15 | ``` 16 | 3) Run the following command to start the example 17 | ``` 18 | go run frequent/starter/main.go 19 | ``` 20 | -------------------------------------------------------------------------------- /polling/frequent/activities.go: -------------------------------------------------------------------------------- 1 | package frequent 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/temporalio/samples-go/polling" 8 | "go.temporal.io/sdk/activity" 9 | ) 10 | 11 | type PollingActivities struct { 12 | TestService *polling.TestService 13 | PollInterval time.Duration 14 | } 15 | 16 | // DoPoll Activity. 17 | // In this activity polling is implemented within the activity itself and not the workflow, 18 | // using the heartbeat mechanism to keep the activity alive 19 | // This activity intentionally hides underlying error and always retry until the context is closed 20 | // A more sophisticated implementation would distinguish between intermittent failures and catastrophic 21 | // failures from the underlying service 22 | func (a *PollingActivities) DoPoll(ctx context.Context) (string, error) { 23 | for { 24 | res, err := a.TestService.GetServiceResult(ctx) 25 | if err == nil { 26 | return res, err 27 | } 28 | activity.RecordHeartbeat(ctx) 29 | select { 30 | case <-ctx.Done(): 31 | return "", ctx.Err() 32 | case <-time.After(a.PollInterval): 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /polling/frequent/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/temporalio/samples-go/polling/frequent" 8 | 9 | "github.com/pborman/uuid" 10 | "go.temporal.io/sdk/client" 11 | ) 12 | 13 | func main() { 14 | // The client is a heavyweight object that should be created once per process. 15 | c, err := client.Dial(client.Options{}) 16 | if err != nil { 17 | log.Fatalln("Unable to create client", err) 18 | } 19 | defer c.Close() 20 | 21 | workflowOptions := client.StartWorkflowOptions{ 22 | ID: "FrequentPollingSampleWorkflow" + uuid.New(), 23 | TaskQueue: frequent.TaskQueueName, 24 | } 25 | 26 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, frequent.FrequentPolling) 27 | if err != nil { 28 | log.Fatalln("Unable to execute workflow", err) 29 | } 30 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 31 | } 32 | -------------------------------------------------------------------------------- /polling/frequent/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "time" 6 | 7 | "github.com/temporalio/samples-go/polling" 8 | "github.com/temporalio/samples-go/polling/frequent" 9 | 10 | "go.temporal.io/sdk/client" 11 | "go.temporal.io/sdk/worker" 12 | ) 13 | 14 | func main() { 15 | // The client and worker are heavyweight objects that should be created once per process. 16 | c, err := client.Dial(client.Options{ 17 | HostPort: client.DefaultHostPort, 18 | }) 19 | if err != nil { 20 | log.Fatalln("Unable to create client", err) 21 | } 22 | defer c.Close() 23 | 24 | w := worker.New(c, frequent.TaskQueueName, worker.Options{}) 25 | 26 | w.RegisterWorkflow(frequent.FrequentPolling) 27 | testService := polling.NewTestService(5) 28 | activities := &frequent.PollingActivities{ 29 | TestService: &testService, 30 | PollInterval: 1 * time.Second, 31 | } 32 | w.RegisterActivity(activities) 33 | 34 | err = w.Run(worker.InterruptCh()) 35 | if err != nil { 36 | log.Fatalln("Unable to start worker", err) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /polling/frequent/workflow.go: -------------------------------------------------------------------------------- 1 | package frequent 2 | 3 | import ( 4 | "time" 5 | 6 | "go.temporal.io/sdk/workflow" 7 | ) 8 | 9 | const ( 10 | TaskQueueName = "pollingFrequentlySampleQueue" 11 | ) 12 | 13 | func FrequentPolling(ctx workflow.Context) (string, error) { 14 | logger := workflow.GetLogger(ctx) 15 | 16 | ao := workflow.ActivityOptions{ 17 | StartToCloseTimeout: 24 * time.Hour, 18 | HeartbeatTimeout: 30 * time.Second, 19 | } 20 | ctx = workflow.WithActivityOptions(ctx, ao) 21 | 22 | var a *PollingActivities // use a nil struct pointer to call activities that are part of a structure 23 | 24 | var pollResult string 25 | err := workflow.ExecuteActivity(ctx, a.DoPoll).Get(ctx, &pollResult) 26 | if err != nil { 27 | logger.Error("Frequent polling activity failed.", "Error", err) 28 | return "", err 29 | } 30 | 31 | return pollResult, err 32 | } 33 | -------------------------------------------------------------------------------- /polling/frequent/workflow_test.go: -------------------------------------------------------------------------------- 1 | package frequent 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | "github.com/temporalio/samples-go/polling" 10 | 11 | "go.temporal.io/sdk/testsuite" 12 | ) 13 | 14 | func TestFrequentPollingWorkflow(t *testing.T) { 15 | var s testsuite.WorkflowTestSuite 16 | env := s.NewTestWorkflowEnvironment() 17 | testService := polling.NewTestService(5) 18 | a := &PollingActivities{ 19 | TestService: &testService, 20 | PollInterval: 100 * time.Millisecond, // Beware of test timeouts if you change this 21 | } 22 | env.RegisterActivity(a) 23 | env.ExecuteWorkflow(FrequentPolling) 24 | 25 | require.True(t, env.IsWorkflowCompleted()) 26 | require.NoError(t, env.GetWorkflowError()) 27 | var pollResult string 28 | require.NoError(t, env.GetWorkflowResult(&pollResult)) 29 | require.Equalf(t, pollResult, "OK", "The polling has returned the wrong result") 30 | 31 | env.AssertExpectations(t) 32 | } 33 | -------------------------------------------------------------------------------- /polling/infrequent/activities.go: -------------------------------------------------------------------------------- 1 | package infrequent 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/temporalio/samples-go/polling" 7 | ) 8 | 9 | type PollingActivities struct { 10 | TestService *polling.TestService 11 | } 12 | 13 | // DoPoll Activity. 14 | func (a *PollingActivities) DoPoll(cmd context.Context) (string, error) { 15 | return a.TestService.GetServiceResult(cmd) 16 | } 17 | -------------------------------------------------------------------------------- /polling/infrequent/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/temporalio/samples-go/polling/infrequent" 8 | 9 | "github.com/pborman/uuid" 10 | "go.temporal.io/sdk/client" 11 | ) 12 | 13 | func main() { 14 | // The client is a heavyweight object that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | workflowOptions := client.StartWorkflowOptions{ 24 | ID: "InfrequentPollingSampleWorkflow" + uuid.New(), 25 | TaskQueue: infrequent.TaskQueueName, 26 | } 27 | 28 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, infrequent.InfrequentPolling) 29 | if err != nil { 30 | log.Fatalln("Unable to execute workflow", err) 31 | } 32 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 33 | } 34 | -------------------------------------------------------------------------------- /polling/infrequent/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/temporalio/samples-go/polling" 7 | "github.com/temporalio/samples-go/polling/infrequent" 8 | 9 | "go.temporal.io/sdk/client" 10 | "go.temporal.io/sdk/worker" 11 | ) 12 | 13 | func main() { 14 | // The client and worker are heavyweight objects that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | w := worker.New(c, infrequent.TaskQueueName, worker.Options{}) 24 | 25 | w.RegisterWorkflow(infrequent.InfrequentPolling) 26 | testService := polling.NewTestService(5) 27 | activities := &infrequent.PollingActivities{ 28 | TestService: &testService, 29 | } 30 | w.RegisterActivity(activities) 31 | 32 | err = w.Run(worker.InterruptCh()) 33 | if err != nil { 34 | log.Fatalln("Unable to start worker", err) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /polling/infrequent/workflow_test.go: -------------------------------------------------------------------------------- 1 | package infrequent 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | "github.com/temporalio/samples-go/polling" 9 | 10 | "go.temporal.io/sdk/testsuite" 11 | ) 12 | 13 | func Test_InfrequentPollingWorkflow(t *testing.T) { 14 | s := testsuite.WorkflowTestSuite{} 15 | env := s.NewTestWorkflowEnvironment() 16 | testService := polling.NewTestService(5) 17 | a := &PollingActivities{ 18 | TestService: &testService, 19 | } 20 | env.RegisterActivity(a) 21 | 22 | env.ExecuteWorkflow(InfrequentPolling) 23 | 24 | require.True(t, env.IsWorkflowCompleted()) 25 | require.NoError(t, env.GetWorkflowError()) 26 | var pollResult string 27 | require.NoError(t, env.GetWorkflowResult(&pollResult)) 28 | require.Equalf(t, pollResult, "OK", "The polling has returned the wrong result") 29 | env.AssertExpectations(t) 30 | } 31 | -------------------------------------------------------------------------------- /polling/periodic_sequence/README.md: -------------------------------------------------------------------------------- 1 | ## Periodic sequence 2 | 3 | This samples shows periodic polling via Child Workflow. 4 | 5 | This is a rare scenario where polling requires execution of a sequence of Activities, or Activity arguments need to change between polling retries. 6 | 7 | For this case we use a Child Workflow to call polling Activities a set number of times in a loop and then periodically calls continue-as-new. 8 | 9 | The Parent Workflow is not aware about the Child Workflow calling continue-as-new and it gets notified when it completes (or fails). 10 | 11 | ### Steps to run this sample: 12 | 1) You need a Temporal service running. See details in README.md 13 | 2) Run the following command to start the worker 14 | ``` 15 | go run periodic_sequence/worker/main.go 16 | ``` 17 | 3) Run the following command to start the example 18 | ``` 19 | go run periodic_sequence/starter/main.go 20 | ``` 21 | -------------------------------------------------------------------------------- /polling/periodic_sequence/activities.go: -------------------------------------------------------------------------------- 1 | package periodic_sequence 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/temporalio/samples-go/polling" 7 | ) 8 | 9 | type PollingActivities struct { 10 | TestService *polling.TestService 11 | } 12 | 13 | // DoPoll Activity. 14 | func (a *PollingActivities) DoPoll(ctx context.Context) (string, error) { 15 | return a.TestService.GetServiceResult(ctx) 16 | } 17 | -------------------------------------------------------------------------------- /polling/periodic_sequence/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "time" 7 | 8 | "github.com/pborman/uuid" 9 | "go.temporal.io/sdk/client" 10 | 11 | "github.com/temporalio/samples-go/polling/periodic_sequence" 12 | ) 13 | 14 | func main() { 15 | // The client is a heavyweight object that should be created once per process. 16 | c, err := client.Dial(client.Options{ 17 | HostPort: client.DefaultHostPort, 18 | }) 19 | if err != nil { 20 | log.Fatalln("Unable to create client", err) 21 | } 22 | defer c.Close() 23 | 24 | workflowOptions := client.StartWorkflowOptions{ 25 | ID: "pollingSampleQueue_" + uuid.New(), 26 | TaskQueue: periodic_sequence.TaskQueueName, 27 | } 28 | 29 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, periodic_sequence.PeriodicSequencePolling, 1*time.Second) 30 | if err != nil { 31 | log.Fatalln("Unable to execute workflow", err) 32 | } 33 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 34 | } 35 | -------------------------------------------------------------------------------- /polling/periodic_sequence/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/polling" 10 | "github.com/temporalio/samples-go/polling/periodic_sequence" 11 | ) 12 | 13 | func main() { 14 | // The client and worker are heavyweight objects that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | w := worker.New(c, periodic_sequence.TaskQueueName, worker.Options{}) 24 | 25 | w.RegisterWorkflow(periodic_sequence.PeriodicSequencePolling) 26 | w.RegisterWorkflow(periodic_sequence.PollingChildWorkflow) 27 | testService := polling.NewTestService(50) 28 | activities := &periodic_sequence.PollingActivities{ 29 | TestService: &testService, 30 | } 31 | w.RegisterActivity(activities) 32 | 33 | err = w.Run(worker.InterruptCh()) 34 | if err != nil { 35 | log.Fatalln("Unable to start worker", err) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /polling/periodic_sequence/workflow.go: -------------------------------------------------------------------------------- 1 | package periodic_sequence 2 | 3 | import ( 4 | "time" 5 | 6 | "go.temporal.io/sdk/workflow" 7 | ) 8 | 9 | const ( 10 | TaskQueueName = "pollingPeriodicSequenceSampleQueue" 11 | ) 12 | 13 | func PeriodicSequencePolling(ctx workflow.Context, pollingInterval time.Duration) (string, error) { 14 | cwo := workflow.ChildWorkflowOptions{} 15 | ctx = workflow.WithChildOptions(ctx, cwo) 16 | params := ChildWorkflowParams{ 17 | PollingInterval: pollingInterval, 18 | } 19 | res := workflow.ExecuteChildWorkflow(ctx, PollingChildWorkflow, params) 20 | var result string 21 | err := res.Get(ctx, &result) 22 | return result, err 23 | 24 | } 25 | -------------------------------------------------------------------------------- /polling/periodic_sequence/workflow_test.go: -------------------------------------------------------------------------------- 1 | package periodic_sequence 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | "github.com/temporalio/samples-go/polling" 10 | 11 | "go.temporal.io/sdk/testsuite" 12 | ) 13 | 14 | func Test_PeriodicPollingWorkflow(t *testing.T) { 15 | s := testsuite.WorkflowTestSuite{} 16 | env := s.NewTestWorkflowEnvironment() 17 | testService := polling.NewTestService(5) 18 | a := &PollingActivities{ 19 | TestService: &testService, 20 | } 21 | env.RegisterActivity(a) 22 | env.RegisterWorkflow(PollingChildWorkflow) 23 | 24 | env.ExecuteWorkflow(PeriodicSequencePolling, 100*time.Millisecond) 25 | 26 | require.True(t, env.IsWorkflowCompleted()) 27 | require.NoError(t, env.GetWorkflowError()) 28 | 29 | env.AssertExpectations(t) 30 | } 31 | -------------------------------------------------------------------------------- /polling/testservice.go: -------------------------------------------------------------------------------- 1 | package polling 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | ) 7 | 8 | type TestService struct { 9 | tryAttempts int 10 | errorAttempts int 11 | } 12 | 13 | func NewTestService(errorAttempts int) TestService { 14 | return TestService{ 15 | tryAttempts: 0, 16 | errorAttempts: errorAttempts, 17 | } 18 | } 19 | 20 | func (testService *TestService) GetServiceResult(ctx context.Context) (string, error) { 21 | testService.tryAttempts += 1 22 | if testService.tryAttempts%testService.errorAttempts == 0 { 23 | return "OK", nil 24 | } 25 | return "", errors.New("service is down") 26 | } 27 | -------------------------------------------------------------------------------- /pso/activities.go: -------------------------------------------------------------------------------- 1 | package pso 2 | 3 | import ( 4 | "context" 5 | 6 | "go.temporal.io/sdk/activity" 7 | ) 8 | 9 | /** 10 | * Sample activities used by file processing sample workflow. 11 | */ 12 | const ( 13 | InitParticleActivityName = "initParticleActivityName" 14 | UpdateParticleActivityName = "updateParticleActivityName" 15 | ) 16 | 17 | func InitParticleActivity(ctx context.Context, swarm Swarm) (Particle, error) { 18 | logger := activity.GetLogger(ctx) 19 | logger.Info("initParticleActivity started.") 20 | 21 | particle := NewParticle(&swarm) 22 | particle.UpdateFitness(&swarm) 23 | 24 | return *particle, nil 25 | } 26 | 27 | func UpdateParticleActivity(ctx context.Context, swarm Swarm, particleIdx int) (Particle, error) { 28 | logger := activity.GetLogger(ctx) 29 | logger.Info("updateParticleActivity started.") 30 | 31 | particle := swarm.Particles[particleIdx] 32 | particle.UpdateLocation(&swarm) 33 | particle.UpdateFitness(&swarm) 34 | 35 | return *particle, nil 36 | } 37 | -------------------------------------------------------------------------------- /pso/position.go: -------------------------------------------------------------------------------- 1 | package pso 2 | 3 | import "math/rand" 4 | 5 | type Vector []float64 6 | 7 | type Position struct { 8 | Location Vector 9 | Fitness float64 10 | } 11 | 12 | func NewPosition(dim int) *Position { 13 | loc := make([]float64, dim) 14 | return &Position{ 15 | Location: loc, 16 | // Fitness: EvaluateFunction(settings.Function.Evaluate, loc), 17 | } 18 | } 19 | 20 | func RandomPosition(function ObjectiveFunction, rng *rand.Rand) *Position { 21 | pos := NewPosition(function.dim) 22 | xLo := function.xLo 23 | xHi := function.xHi 24 | for i := 0; i < len(pos.Location); i++ { 25 | pos.Location[i] = xLo + (xHi-xLo)*rng.Float64() 26 | } 27 | // pos.Fitness = EvaluateFunction(settings.Function.Evaluate, pos.Location) 28 | return pos 29 | } 30 | 31 | func (position *Position) Copy() *Position { 32 | newPosition := NewPosition(len(position.Location)) 33 | copy(newPosition.Location, position.Location) 34 | newPosition.Fitness = position.Fitness 35 | return newPosition 36 | } 37 | 38 | func (position *Position) IsBetterThan(other *Position) bool { 39 | return position.Fitness < other.Fitness 40 | } 41 | -------------------------------------------------------------------------------- /pso/query/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "log" 7 | 8 | "go.temporal.io/sdk/client" 9 | 10 | "github.com/temporalio/samples-go/pso" 11 | ) 12 | 13 | func main() { 14 | var workflowID, runID, queryType string 15 | flag.StringVar(&workflowID, "w", "", "WorkflowID") 16 | flag.StringVar(&runID, "r", "", "RunID") 17 | flag.StringVar(&queryType, "t", "__stack_trace", "Query type is one of [__stack_trace, child, __open_sessions]") 18 | flag.Parse() 19 | 20 | // The client is a heavyweight object that should be created once per process. 21 | c, err := client.Dial(client.Options{ 22 | HostPort: client.DefaultHostPort, 23 | DataConverter: pso.NewJSONDataConverter(), 24 | }) 25 | if err != nil { 26 | log.Fatalln("Unable to create client", err) 27 | } 28 | defer c.Close() 29 | 30 | resp, err := c.QueryWorkflow(context.Background(), workflowID, runID, queryType) 31 | if err != nil { 32 | log.Fatalln("Unable to query workflow", err) 33 | } 34 | var result interface{} 35 | if err := resp.Get(&result); err != nil { 36 | log.Fatalln("Unable to decode query result", err) 37 | } 38 | log.Println("Received query result", "Result", result) 39 | } 40 | -------------------------------------------------------------------------------- /pso/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "log" 7 | 8 | "github.com/pborman/uuid" 9 | "go.temporal.io/sdk/client" 10 | 11 | "github.com/temporalio/samples-go/pso" 12 | ) 13 | 14 | func main() { 15 | var functionName string 16 | flag.StringVar(&functionName, "f", "sphere", "One of [sphere, rosenbrock, griewank]") 17 | flag.Parse() 18 | 19 | // The client is a heavyweight object that should be created once per process. 20 | c, err := client.Dial(client.Options{ 21 | HostPort: client.DefaultHostPort, 22 | DataConverter: pso.NewJSONDataConverter(), 23 | }) 24 | if err != nil { 25 | log.Fatalln("Unable to create client", err) 26 | } 27 | defer c.Close() 28 | 29 | workflowOptions := client.StartWorkflowOptions{ 30 | ID: "PSO_" + uuid.New(), 31 | TaskQueue: "pso", 32 | } 33 | 34 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, pso.PSOWorkflow, functionName) 35 | if err != nil { 36 | log.Fatalln("Unable to execute workflow", err) 37 | } 38 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 39 | } 40 | -------------------------------------------------------------------------------- /pso/utils.go: -------------------------------------------------------------------------------- 1 | package pso 2 | 3 | import ( 4 | "math" 5 | ) 6 | 7 | func CalculateSwarmSize(dim, max_size int) int { 8 | s := 10. + 2.*math.Sqrt(float64(dim)) 9 | size := int(math.Floor(s + 0.5)) 10 | if size > max_size { 11 | return max_size 12 | } else { 13 | return size 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /query/README.md: -------------------------------------------------------------------------------- 1 | This sample workflow demos how to use query API to get the current state of running workflow. 2 | 3 | `query_workflow.go` shows how to setup a custom workflow query handler 4 | 5 | `query_workflow_test.go` shows how to unit-test query functionality 6 | 7 | Steps to run this sample: 8 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 9 | 2) Run the following command to start worker 10 | ``` 11 | go run query/worker/main.go 12 | ``` 13 | 3) Run the following command to trigger a workflow execution. You should see workflowID and runID print out on screen. 14 | ``` 15 | go run query/starter/main.go 16 | ``` 17 | 4) Run the following command to see current workflow state on the screen. 18 | ``` 19 | go run query/query/main.go 20 | ``` 21 | 5) You could also specify the query type "__stack_trace" to dump the call stack for the workflow. 22 | ``` 23 | go run query/query/main.go -t __stack_trace 24 | ``` 25 | -------------------------------------------------------------------------------- /query/query/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "log" 7 | 8 | "go.temporal.io/sdk/client" 9 | ) 10 | 11 | func main() { 12 | var workflowID, queryType string 13 | flag.StringVar(&workflowID, "w", "query_workflow", "WorkflowID.") 14 | flag.StringVar(&queryType, "t", "state", "Query type [state|__stack_trace].") 15 | flag.Parse() 16 | 17 | // The client is a heavyweight object that should be created once per process. 18 | c, err := client.Dial(client.Options{ 19 | HostPort: client.DefaultHostPort, 20 | }) 21 | if err != nil { 22 | log.Fatalln("Unable to create client", err) 23 | } 24 | defer c.Close() 25 | 26 | resp, err := c.QueryWorkflow(context.Background(), workflowID, "", queryType) 27 | if err != nil { 28 | log.Fatalln("Unable to query workflow", err) 29 | } 30 | var result interface{} 31 | if err := resp.Get(&result); err != nil { 32 | log.Fatalln("Unable to decode query result", err) 33 | } 34 | log.Println("Received query result", "Result", result) 35 | } 36 | -------------------------------------------------------------------------------- /query/query_workflow.go: -------------------------------------------------------------------------------- 1 | package query 2 | 3 | import ( 4 | "time" 5 | 6 | "go.temporal.io/sdk/workflow" 7 | ) 8 | 9 | // Workflow is to demo how to setup query handler 10 | func QueryWorkflow(ctx workflow.Context) error { 11 | queryResult := "started" 12 | logger := workflow.GetLogger(ctx) 13 | logger.Info("QueryWorkflow started") 14 | // setup query handler for query type "state" 15 | err := workflow.SetQueryHandler(ctx, "state", func(input []byte) (string, error) { 16 | return queryResult, nil 17 | }) 18 | if err != nil { 19 | logger.Info("SetQueryHandler failed: " + err.Error()) 20 | return err 21 | } 22 | 23 | queryResult = "waiting on timer" 24 | // to simulate workflow been blocked on something, in reality, workflow could wait on anything like activity, signal or timer 25 | _ = workflow.NewTimer(ctx, time.Minute*2).Get(ctx, nil) 26 | logger.Info("Timer fired") 27 | 28 | queryResult = "done" 29 | logger.Info("QueryWorkflow completed") 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /query/query_workflow_test.go: -------------------------------------------------------------------------------- 1 | package query 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/require" 8 | "go.temporal.io/sdk/testsuite" 9 | ) 10 | 11 | func Test_QueryWorkflow(t *testing.T) { 12 | ts := &testsuite.WorkflowTestSuite{} 13 | env := ts.NewTestWorkflowEnvironment() 14 | 15 | w := false 16 | env.RegisterDelayedCallback(func() { 17 | queryAndVerify(t, env, "waiting on timer") 18 | w = true 19 | }, time.Minute*1) 20 | 21 | env.ExecuteWorkflow(QueryWorkflow) 22 | require.True(t, env.IsWorkflowCompleted()) 23 | require.NoError(t, env.GetWorkflowError()) 24 | require.True(t, w, "state at timer not verified") 25 | queryAndVerify(t, env, "done") 26 | } 27 | 28 | func queryAndVerify(t *testing.T, env *testsuite.TestWorkflowEnvironment, expectedState string) { 29 | result, err := env.QueryWorkflow("state") 30 | require.NoError(t, err) 31 | var state string 32 | err = result.Get(&state) 33 | require.NoError(t, err) 34 | require.Equal(t, expectedState, state) 35 | } 36 | -------------------------------------------------------------------------------- /query/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "go.temporal.io/sdk/client" 8 | 9 | "github.com/temporalio/samples-go/query" 10 | ) 11 | 12 | func main() { 13 | // The client is a heavyweight object that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | workflowOptions := client.StartWorkflowOptions{ 23 | ID: "query_workflow", 24 | TaskQueue: "query", 25 | } 26 | 27 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, query.QueryWorkflow) 28 | if err != nil { 29 | log.Fatalln("Unable to execute workflow", err) 30 | } 31 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 32 | } 33 | -------------------------------------------------------------------------------- /query/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/query" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "query", worker.Options{}) 23 | 24 | w.RegisterWorkflow(query.QueryWorkflow) 25 | 26 | err = w.Run(worker.InterruptCh()) 27 | if err != nil { 28 | log.Fatalln("Unable to start worker", err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /recovery/README.md: -------------------------------------------------------------------------------- 1 | 2 | ### Recovery Sample 3 | This sample implements a `RecoveryWorkflow` which is designed to restart all `TripWorkflow` executions which are currently 4 | outstanding and replay all signals from previous run. This is useful where a bad code change is rolled out which 5 | causes workflows to get stuck or state is corrupted. 6 | 7 | ### Steps to run this sample 8 | 1) Run the following command to start worker 9 | ``` 10 | go run recovery/worker/main.go 11 | ``` 12 | 2) Run the following command to start trip workflow 13 | ``` 14 | go run recovery/starter/main.go 15 | ``` 16 | 3) Run the following command to query trip workflow 17 | ``` 18 | go run recovery/query/main.go 19 | ``` 20 | 4) Run the following command to send signal to trip workflow 21 | ``` 22 | go run recovery/signal/main.go -s '{"ID": "Trip1", "Total": 10}' 23 | ``` 24 | 4) Run the following command to start recovery workflow 25 | ``` 26 | go run recovery/starter/main.go -w recovery_workflow -wt recoveryworkflow -i '{"Type": "TripWorkflow", "Concurrency": 2}' 27 | ``` -------------------------------------------------------------------------------- /recovery/query/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "log" 7 | 8 | "go.temporal.io/sdk/client" 9 | 10 | "github.com/temporalio/samples-go/recovery" 11 | ) 12 | 13 | func main() { 14 | var workflowID string 15 | flag.StringVar(&workflowID, "w", "trip_workflow", "WorkflowID.") 16 | flag.Parse() 17 | 18 | // The client is a heavyweight object that should be created once per process. 19 | c, err := client.Dial(client.Options{ 20 | HostPort: client.DefaultHostPort, 21 | }) 22 | if err != nil { 23 | log.Fatalln("Unable to create client", err) 24 | } 25 | defer c.Close() 26 | 27 | resp, err := c.QueryWorkflow(context.Background(), workflowID, "", recovery.QueryName) 28 | if err != nil { 29 | log.Fatalln("Unable to query workflow", err) 30 | } 31 | var result interface{} 32 | if err := resp.Get(&result); err != nil { 33 | log.Fatalln("Unable to decode query result", err) 34 | } 35 | log.Println("Received query result", "Result", result) 36 | } 37 | -------------------------------------------------------------------------------- /recovery/signal/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "flag" 7 | "log" 8 | 9 | "go.temporal.io/sdk/client" 10 | 11 | "github.com/temporalio/samples-go/recovery" 12 | ) 13 | 14 | func main() { 15 | var workflowID, signal string 16 | flag.StringVar(&workflowID, "w", "trip_workflow", "WorkflowID.") 17 | flag.StringVar(&signal, "s", `{}`, "Signal data.") 18 | flag.Parse() 19 | 20 | // The client is a heavyweight object that should be created once per process. 21 | c, err := client.Dial(client.Options{ 22 | HostPort: client.DefaultHostPort, 23 | }) 24 | if err != nil { 25 | log.Fatalln("Unable to create client", err) 26 | } 27 | defer c.Close() 28 | 29 | var tripEvent recovery.TripEvent 30 | if err := json.Unmarshal([]byte(signal), &tripEvent); err != nil { 31 | log.Fatalln("Unable to unmarshal signal input parameters", err) 32 | } 33 | 34 | err = c.SignalWorkflow(context.Background(), workflowID, "", recovery.TripSignalName, tripEvent) 35 | if err != nil { 36 | log.Fatalln("Unable to signal workflow", err) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /reqrespactivity/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/temporalio/samples-go/reqrespactivity" 8 | "go.temporal.io/sdk/client" 9 | ) 10 | 11 | func main() { 12 | c, err := client.Dial(client.Options{ 13 | HostPort: client.DefaultHostPort, 14 | }) 15 | if err != nil { 16 | log.Fatalln("Unable to create client", err) 17 | } 18 | defer c.Close() 19 | 20 | workflowOptions := client.StartWorkflowOptions{ 21 | ID: "reqrespactivity_workflow", 22 | TaskQueue: "reqrespactivity", 23 | } 24 | 25 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, reqrespactivity.UppercaseWorkflow) 26 | if err != nil { 27 | log.Fatalln("Unable to execute workflow", err) 28 | } 29 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 30 | } 31 | -------------------------------------------------------------------------------- /reqrespactivity/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/temporalio/samples-go/reqrespactivity" 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | ) 10 | 11 | func main() { 12 | c, err := client.Dial(client.Options{ 13 | HostPort: client.DefaultHostPort, 14 | }) 15 | if err != nil { 16 | log.Fatalln("Unable to create client", err) 17 | } 18 | defer c.Close() 19 | 20 | w := worker.New(c, "reqrespactivity", worker.Options{}) 21 | 22 | w.RegisterWorkflow(reqrespactivity.UppercaseWorkflow) 23 | w.RegisterActivity(reqrespactivity.UppercaseActivity) 24 | 25 | err = w.Run(worker.InterruptCh()) 26 | if err != nil { 27 | log.Fatalln("Unable to start worker", err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /reqrespquery/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/temporalio/samples-go/reqrespquery" 8 | "go.temporal.io/sdk/client" 9 | ) 10 | 11 | func main() { 12 | c, err := client.Dial(client.Options{ 13 | HostPort: client.DefaultHostPort, 14 | }) 15 | if err != nil { 16 | log.Fatalln("Unable to create client", err) 17 | } 18 | defer c.Close() 19 | 20 | workflowOptions := client.StartWorkflowOptions{ 21 | ID: "reqrespquery_workflow", 22 | TaskQueue: "reqrespquery", 23 | } 24 | 25 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, reqrespquery.UppercaseWorkflow) 26 | if err != nil { 27 | log.Fatalln("Unable to execute workflow", err) 28 | } 29 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 30 | } 31 | -------------------------------------------------------------------------------- /reqrespquery/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/temporalio/samples-go/reqrespquery" 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | ) 10 | 11 | func main() { 12 | c, err := client.Dial(client.Options{ 13 | HostPort: client.DefaultHostPort, 14 | }) 15 | if err != nil { 16 | log.Fatalln("Unable to create client", err) 17 | } 18 | defer c.Close() 19 | 20 | w := worker.New(c, "reqrespquery", worker.Options{}) 21 | 22 | w.RegisterWorkflow(reqrespquery.UppercaseWorkflow) 23 | w.RegisterActivity(reqrespquery.UppercaseActivity) 24 | 25 | err = w.Run(worker.InterruptCh()) 26 | if err != nil { 27 | log.Fatalln("Unable to start worker", err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /reqrespupdate/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/temporalio/samples-go/reqrespupdate" 8 | "go.temporal.io/sdk/client" 9 | ) 10 | 11 | func main() { 12 | c, err := client.Dial(client.Options{ 13 | HostPort: client.DefaultHostPort, 14 | }) 15 | if err != nil { 16 | log.Fatalln("Unable to create client", err) 17 | } 18 | defer c.Close() 19 | 20 | workflowOptions := client.StartWorkflowOptions{ 21 | ID: "reqrespupdate_workflow", 22 | TaskQueue: "reqrespupdate", 23 | } 24 | 25 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, reqrespupdate.UppercaseWorkflow, true) 26 | if err != nil { 27 | log.Fatalln("Unable to execute workflow", err) 28 | } 29 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 30 | } 31 | -------------------------------------------------------------------------------- /reqrespupdate/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/temporalio/samples-go/reqrespupdate" 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | ) 10 | 11 | func main() { 12 | c, err := client.Dial(client.Options{ 13 | HostPort: client.DefaultHostPort, 14 | }) 15 | if err != nil { 16 | log.Fatalln("Unable to create client", err) 17 | } 18 | defer c.Close() 19 | 20 | w := worker.New(c, "reqrespupdate", worker.Options{}) 21 | 22 | w.RegisterWorkflow(reqrespupdate.UppercaseWorkflow) 23 | w.RegisterActivity(reqrespupdate.UppercaseActivity) 24 | 25 | err = w.Run(worker.InterruptCh()) 26 | if err != nil { 27 | log.Fatalln("Unable to start worker", err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /retryactivity/README.md: -------------------------------------------------------------------------------- 1 | ### Steps to run this sample: 2 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 3 | 2) Run the following command to start the worker 4 | ``` 5 | go run retryactivity/worker/main.go 6 | ``` 7 | 3) Run the following command to start the example 8 | ``` 9 | go run retryactivity/starter/main.go 10 | ``` 11 | -------------------------------------------------------------------------------- /retryactivity/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | "go.temporal.io/sdk/client" 9 | 10 | "github.com/temporalio/samples-go/retryactivity" 11 | ) 12 | 13 | func main() { 14 | // The client is a heavyweight object that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | workflowOptions := client.StartWorkflowOptions{ 24 | ID: "retry_activity_" + uuid.New(), 25 | TaskQueue: "retry-activity", 26 | } 27 | 28 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, retryactivity.RetryWorkflow) 29 | if err != nil { 30 | log.Fatalln("Unable to execute workflow", err) 31 | } 32 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 33 | } 34 | -------------------------------------------------------------------------------- /retryactivity/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/retryactivity" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "retry-activity", worker.Options{}) 23 | 24 | w.RegisterWorkflow(retryactivity.RetryWorkflow) 25 | w.RegisterActivity(retryactivity.BatchProcessingActivity) 26 | 27 | err = w.Run(worker.InterruptCh()) 28 | if err != nil { 29 | log.Fatalln("Unable to start worker", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /safe_message_handler/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/safe_message_handler" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{}) 15 | if err != nil { 16 | log.Fatalln("Unable to create client", err) 17 | } 18 | defer c.Close() 19 | 20 | w := worker.New(c, "safe-message-handlers-task-queue", worker.Options{}) 21 | 22 | w.RegisterWorkflow(safe_message_handler.ClusterManagerWorkflow) 23 | w.RegisterActivity(safe_message_handler.AssignNodesToJobsActivity) 24 | w.RegisterActivity(safe_message_handler.UnassignNodesForJobActivity) 25 | w.RegisterActivity(safe_message_handler.FindBadNodesActivity) 26 | 27 | err = w.Run(worker.InterruptCh()) 28 | if err != nil { 29 | log.Fatalln("Unable to start worker", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /saga/README.md: -------------------------------------------------------------------------------- 1 | ### Steps to run this sample: 2 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 3 | 2) Run the following command to start the worker 4 | ``` 5 | go run saga/worker/main.go 6 | ``` 7 | 3) Run the following command to start the example 8 | ``` 9 | go run saga/start/main.go 10 | ``` 11 | 12 | Based on https://github.com/temporalio/money-transfer-project-template-go 13 | -------------------------------------------------------------------------------- /saga/shared.go: -------------------------------------------------------------------------------- 1 | package saga 2 | 3 | const TransferMoneyTaskQueue = "TRANSFER_MONEY_TASK_QUEUE" 4 | 5 | type TransferDetails struct { 6 | Amount float32 7 | FromAccount string 8 | ToAccount string 9 | ReferenceID string 10 | } 11 | -------------------------------------------------------------------------------- /saga/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/saga" 10 | ) 11 | 12 | func main() { 13 | // Create the client object just once per process 14 | c, err := client.Dial(client.Options{}) 15 | if err != nil { 16 | log.Fatalln("unable to create Temporal client", err) 17 | } 18 | defer c.Close() 19 | // This worker hosts both Workflow and Activity functions 20 | w := worker.New(c, saga.TransferMoneyTaskQueue, worker.Options{}) 21 | w.RegisterWorkflow(saga.TransferMoney) 22 | w.RegisterActivity(saga.Withdraw) 23 | w.RegisterActivity(saga.WithdrawCompensation) 24 | w.RegisterActivity(saga.Deposit) 25 | w.RegisterActivity(saga.DepositCompensation) 26 | w.RegisterActivity(saga.StepWithError) 27 | // Start listening to the Task Queue 28 | err = w.Run(worker.InterruptCh()) 29 | if err != nil { 30 | log.Fatalln("unable to start Worker", err) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /saga/workflow_test.go: -------------------------------------------------------------------------------- 1 | package saga 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/mock" 8 | "github.com/stretchr/testify/require" 9 | 10 | "go.temporal.io/sdk/testsuite" 11 | ) 12 | 13 | func Test_Workflow(t *testing.T) { 14 | testSuite := &testsuite.WorkflowTestSuite{} 15 | env := testSuite.NewTestWorkflowEnvironment() 16 | // Mock activity implementation 17 | testDetails := TransferDetails{ 18 | Amount: 1.00, 19 | FromAccount: "001-001", 20 | ToAccount: "002-002", 21 | ReferenceID: "1234", 22 | } 23 | env.OnActivity(Withdraw, mock.Anything, testDetails).Return(nil) 24 | env.OnActivity(WithdrawCompensation, mock.Anything, testDetails).Return(nil) 25 | env.OnActivity(Deposit, mock.Anything, testDetails).Return(nil) 26 | env.OnActivity(DepositCompensation, mock.Anything, testDetails).Return(nil) 27 | env.OnActivity(StepWithError, mock.Anything, testDetails).Return(errors.New("some error")) 28 | env.ExecuteWorkflow(TransferMoney, testDetails) 29 | require.True(t, env.IsWorkflowCompleted()) 30 | require.Error(t, env.GetWorkflowError()) 31 | } 32 | -------------------------------------------------------------------------------- /schedule/README.md: -------------------------------------------------------------------------------- 1 | This sample demonstrates how to setup a schedule to run a workflow 2 | 3 | Steps to run this sample: 4 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 5 | 2) Run 6 | ``` 7 | go run schedule/worker/main.go 8 | ``` 9 | to start worker for schedule workflow. 10 | 3) Run 11 | ``` 12 | go run schedule/starter/main.go 13 | ``` 14 | to start a schedule to run a workflow every second. 15 | -------------------------------------------------------------------------------- /schedule/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/schedule" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "schedule", worker.Options{}) 23 | 24 | w.RegisterWorkflow(schedule.SampleScheduleWorkflow) 25 | w.RegisterActivity(schedule.DoSomething) 26 | 27 | err = w.Run(worker.InterruptCh()) 28 | if err != nil { 29 | log.Fatalln("Unable to start worker", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /schedule/workflow_test.go: -------------------------------------------------------------------------------- 1 | package schedule 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/mock" 8 | "github.com/stretchr/testify/suite" 9 | "go.temporal.io/sdk/testsuite" 10 | ) 11 | 12 | type UnitTestSuite struct { 13 | suite.Suite 14 | testsuite.WorkflowTestSuite 15 | } 16 | 17 | func TestUnitTestSuite(t *testing.T) { 18 | suite.Run(t, new(UnitTestSuite)) 19 | } 20 | 21 | func (s *UnitTestSuite) Test_ScheduleWorkflow() { 22 | env := s.NewTestWorkflowEnvironment() 23 | 24 | env.RegisterWorkflow(SampleScheduleWorkflow) 25 | env.RegisterActivity(DoSomething) 26 | 27 | //lint:ignore SA1019 sample will be updated in the near future. 28 | err := env.SetSearchAttributesOnStart(map[string]interface{}{ 29 | "TemporalScheduledById": "schedule_test_ID", 30 | "TemporalScheduledStartTime": time.Now(), 31 | }) 32 | s.NoError(err) 33 | 34 | env.OnActivity(DoSomething, mock.Anything, mock.Anything, mock.Anything).Return(nil).Times(3) 35 | 36 | env.ExecuteWorkflow(SampleScheduleWorkflow) 37 | 38 | s.True(env.IsWorkflowCompleted()) 39 | err = env.GetWorkflowError() 40 | s.NoError(err) 41 | } 42 | -------------------------------------------------------------------------------- /searchattributes/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | "go.temporal.io/sdk/client" 9 | 10 | "github.com/temporalio/samples-go/searchattributes" 11 | ) 12 | 13 | func main() { 14 | // The client is a heavyweight object that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | workflowOptions := client.StartWorkflowOptions{ 24 | ID: "search_attributes_" + uuid.New(), 25 | TaskQueue: "search-attributes", 26 | SearchAttributes: map[string]interface{}{ // optional search attributes when start workflow 27 | "CustomIntField": 1, 28 | }, 29 | Memo: map[string]interface{}{ 30 | "description": "Test search attributes workflow", 31 | }, 32 | } 33 | 34 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, searchattributes.SearchAttributesWorkflow) 35 | if err != nil { 36 | log.Fatalln("Unable to execute workflow", err) 37 | } 38 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 39 | } 40 | -------------------------------------------------------------------------------- /searchattributes/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | 10 | "github.com/temporalio/samples-go/searchattributes" 11 | ) 12 | 13 | func main() { 14 | // The client and worker are heavyweight objects that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | ctx := context.WithValue(context.Background(), searchattributes.ClientCtxKey, c) 24 | 25 | w := worker.New(c, "search-attributes", worker.Options{ 26 | BackgroundActivityContext: ctx, 27 | }) 28 | 29 | w.RegisterWorkflow(searchattributes.SearchAttributesWorkflow) 30 | w.RegisterActivity(searchattributes.ListExecutions) 31 | 32 | err = w.Run(worker.InterruptCh()) 33 | if err != nil { 34 | log.Fatalln("Unable to start worker", err) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /serverjwtauth/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/helloworld" 10 | "github.com/temporalio/samples-go/serverjwtauth" 11 | ) 12 | 13 | func main() { 14 | key, jwk, err := serverjwtauth.ReadKey() 15 | if err != nil { 16 | log.Fatalln(err) 17 | } 18 | // The client and worker are heavyweight objects that should be created once per process. 19 | c, err := client.Dial(client.Options{ 20 | HeadersProvider: &serverjwtauth.JWTHeadersProvider{ 21 | Config: serverjwtauth.JWTConfig{ 22 | Key: key, 23 | KeyID: jwk.KeyID, 24 | Permissions: []string{ 25 | "default:read", 26 | "default:write", 27 | }, 28 | }, 29 | }, 30 | }) 31 | if err != nil { 32 | log.Fatalln("Unable to create client", err) 33 | } 34 | defer c.Close() 35 | 36 | w := worker.New(c, "server-jwt-auth", worker.Options{}) 37 | 38 | w.RegisterWorkflow(helloworld.Workflow) 39 | w.RegisterActivity(helloworld.Activity) 40 | 41 | err = w.Run(worker.InterruptCh()) 42 | if err != nil { 43 | log.Fatalln("Unable to start worker", err) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /session-failure/activities.go: -------------------------------------------------------------------------------- 1 | package fileprocessing 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "go.temporal.io/sdk/activity" 8 | ) 9 | 10 | /** 11 | * Sample activities used by session failure sample workflow. 12 | */ 13 | 14 | type Activities struct { 15 | } 16 | 17 | func (a *Activities) PrepareWorkerActivity(ctx context.Context) error { 18 | logger := activity.GetLogger(ctx) 19 | logger.Info("Preparing session worker") 20 | return nil 21 | } 22 | 23 | func (a *Activities) LongRunningActivity(ctx context.Context) error { 24 | logger := activity.GetLogger(ctx) 25 | logger.Info("Started running long running activity.") 26 | 27 | hbTicker := time.NewTicker(20 * time.Second) 28 | defer hbTicker.Stop() 29 | // Create a 5 minute timer to simulate an activity doing some long work 30 | timer := time.NewTimer(5 * time.Minute) 31 | defer timer.Stop() 32 | for { 33 | select { 34 | case <-hbTicker.C: 35 | activity.RecordHeartbeat(ctx) 36 | case <-timer.C: 37 | return ctx.Err() 38 | case <-ctx.Done(): 39 | return ctx.Err() 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /session-failure/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | "go.temporal.io/sdk/client" 9 | 10 | sessionfailure "github.com/temporalio/samples-go/session-failure" 11 | ) 12 | 13 | func main() { 14 | // The client is a heavyweight object that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | workflowOptions := client.StartWorkflowOptions{ 24 | ID: "session_failure_" + uuid.New(), 25 | TaskQueue: "session-failure", 26 | } 27 | 28 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, sessionfailure.SampleSessionFailureRecoveryWorkflow) 29 | if err != nil { 30 | log.Fatalln("Unable to execute workflow", err) 31 | } 32 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 33 | } 34 | -------------------------------------------------------------------------------- /session-failure/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | sessionfailure "github.com/temporalio/samples-go/session-failure" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | workerOptions := worker.Options{ 23 | EnableSessionWorker: true, // Important for a worker to participate in the session 24 | } 25 | w := worker.New(c, "session-failure", workerOptions) 26 | 27 | w.RegisterWorkflow(sessionfailure.SampleSessionFailureRecoveryWorkflow) 28 | w.RegisterActivity(&sessionfailure.Activities{}) 29 | 30 | err = w.Run(worker.InterruptCh()) 31 | if err != nil { 32 | log.Fatalln("Unable to start worker", err) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /session-failure/workflow_test.go: -------------------------------------------------------------------------------- 1 | package fileprocessing 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/mock" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/stretchr/testify/suite" 10 | "go.temporal.io/sdk/testsuite" 11 | ) 12 | 13 | type UnitTestSuite struct { 14 | suite.Suite 15 | testsuite.WorkflowTestSuite 16 | } 17 | 18 | func TestUnitTestSuite(t *testing.T) { 19 | suite.Run(t, new(UnitTestSuite)) 20 | } 21 | 22 | func (s *UnitTestSuite) Test_SampleFileProcessingWorkflow() { 23 | env := s.NewTestWorkflowEnvironment() 24 | env.SetWorkerOptions(worker.Options{ 25 | EnableSessionWorker: true, // Important for a worker to participate in the session 26 | }) 27 | var a *Activities 28 | 29 | env.OnActivity(a.PrepareWorkerActivity, mock.Anything).Return(nil) 30 | env.OnActivity(a.LongRunningActivity, mock.Anything).Return(nil) 31 | 32 | env.RegisterActivity(a) 33 | 34 | env.ExecuteWorkflow(SampleSessionFailureRecoveryWorkflow) 35 | 36 | s.True(env.IsWorkflowCompleted()) 37 | s.NoError(env.GetWorkflowError()) 38 | 39 | env.AssertExpectations(s.T()) 40 | } 41 | -------------------------------------------------------------------------------- /shoppingcart/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/shoppingcart" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, shoppingcart.TaskQueueName, worker.Options{}) 23 | 24 | w.RegisterWorkflow(shoppingcart.CartWorkflow) 25 | 26 | err = w.Run(worker.InterruptCh()) 27 | if err != nil { 28 | log.Fatalln("Unable to start worker", err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /sleep-for-days/README.md: -------------------------------------------------------------------------------- 1 | ### Sleep for days 2 | 3 | This sample demonstrates how to create a Temporal workflow that runs forever, sending an email every 30 days. 4 | 5 | ### Steps to run this sample: 6 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 7 | 2) Run the following command to start the worker 8 | ``` 9 | go run worker/main.go 10 | ``` 11 | 3) Run the following command to start the example 12 | ``` 13 | go run starter/main.go 14 | ``` 15 | 16 | This sample will run indefinitely until you send a `complete` signal to the workflow. See how to send a signal via Temporal CLI [here](https://docs.temporal.io/cli/workflow#signal). -------------------------------------------------------------------------------- /sleep-for-days/sleepfordays_workflow.go: -------------------------------------------------------------------------------- 1 | package sleepfordays 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "go.temporal.io/sdk/activity" 9 | "go.temporal.io/sdk/workflow" 10 | ) 11 | 12 | func SleepForDaysWorkflow(ctx workflow.Context) (string, error) { 13 | ctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ 14 | StartToCloseTimeout: 10 * time.Second, 15 | }) 16 | 17 | isComplete := false 18 | sigChan := workflow.GetSignalChannel(ctx, "complete") 19 | 20 | for !isComplete { 21 | workflow.ExecuteActivity(ctx, SendEmailActivity, "Sleeping for 30 days") 22 | selector := workflow.NewSelector(ctx) 23 | selector.AddFuture(workflow.NewTimer(ctx, time.Hour*24*30), func(f workflow.Future) {}) 24 | selector.AddReceive(sigChan, func(c workflow.ReceiveChannel, more bool) { 25 | isComplete = true 26 | }) 27 | selector.Select(ctx) 28 | } 29 | 30 | return "done", nil 31 | } 32 | 33 | // A stub Activity for sending an email. 34 | func SendEmailActivity(ctx context.Context, msg string) error { 35 | activity.GetLogger(ctx).Info(fmt.Sprintf(`Sending email: "%v"\n`, msg)) 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /sleep-for-days/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | sleepfordays "github.com/temporalio/samples-go/sleep-for-days" 8 | "go.temporal.io/sdk/client" 9 | ) 10 | 11 | func main() { 12 | c, err := client.Dial(client.Options{}) 13 | if err != nil { 14 | log.Fatalln("Unable to create client", err) 15 | } 16 | defer c.Close() 17 | 18 | workflowOptions := client.StartWorkflowOptions{ 19 | TaskQueue: "sleep-for-days", 20 | } 21 | 22 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, sleepfordays.SleepForDaysWorkflow) 23 | if err != nil { 24 | log.Fatalln("Unable to execute workflow", err) 25 | } 26 | 27 | log.Println("Started sleep-for-days workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 28 | 29 | // Synchronously wait for the workflow completion (will run indefinitely until it receives a signal) 30 | var result string 31 | err = we.Get(context.Background(), &result) 32 | if err != nil { 33 | log.Fatalln("Unable get workflow result", err) 34 | } 35 | log.Println("Workflow result:", result) 36 | } 37 | -------------------------------------------------------------------------------- /sleep-for-days/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | sleepfordays "github.com/temporalio/samples-go/sleep-for-days" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{}) 15 | if err != nil { 16 | log.Fatalln("Unable to create client", err) 17 | } 18 | defer c.Close() 19 | 20 | w := worker.New(c, "sleep-for-days", worker.Options{}) 21 | 22 | w.RegisterWorkflow(sleepfordays.SleepForDaysWorkflow) 23 | w.RegisterActivity(sleepfordays.SendEmailActivity) 24 | 25 | err = w.Run(worker.InterruptCh()) 26 | if err != nil { 27 | log.Fatalln("Unable to start worker", err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /slogadapter/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/temporalio/samples-go/slogadapter" 8 | "go.temporal.io/sdk/client" 9 | ) 10 | 11 | func main() { 12 | // The client is a heavyweight object that should be created once per process. 13 | c, err := client.Dial(client.Options{}) 14 | if err != nil { 15 | log.Fatalln("Unable to create client", err) 16 | } 17 | defer c.Close() 18 | 19 | workflowOptions := client.StartWorkflowOptions{ 20 | ID: "slog_logger_workflow_id", 21 | TaskQueue: "slog-logger", 22 | } 23 | 24 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, slogadapter.Workflow, "") 25 | if err != nil { 26 | log.Fatalln("Unable to execute workflow", err) 27 | } 28 | 29 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 30 | 31 | // Synchronously wait for the workflow completion. 32 | var result interface{} 33 | err = we.Get(context.Background(), &result) 34 | if err != nil { 35 | log.Fatalln("Unable get workflow result", err) 36 | } 37 | log.Println("Workflow completed. Check worker logs.") 38 | } 39 | -------------------------------------------------------------------------------- /slogadapter/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "log/slog" 8 | 9 | "github.com/temporalio/samples-go/slogadapter" 10 | "go.temporal.io/sdk/client" 11 | tlog "go.temporal.io/sdk/log" 12 | "go.temporal.io/sdk/worker" 13 | ) 14 | 15 | func main() { 16 | c, err := client.Dial(client.Options{ 17 | Logger: tlog.NewStructuredLogger( 18 | slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ 19 | AddSource: true, 20 | Level: slog.LevelDebug, 21 | }))), 22 | }) 23 | if err != nil { 24 | log.Fatalln("Unable to create client", err) 25 | } 26 | defer c.Close() 27 | 28 | w := worker.New(c, "slog-logger", worker.Options{}) 29 | 30 | w.RegisterWorkflow(slogadapter.Workflow) 31 | w.RegisterActivity(slogadapter.LoggingActivity) 32 | w.RegisterActivity(slogadapter.LoggingErrorAcctivity) 33 | 34 | err = w.Run(worker.InterruptCh()) 35 | if err != nil { 36 | log.Fatalln("Unable to start worker", err) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /snappycompress/README.md: -------------------------------------------------------------------------------- 1 | ### Steps to run this sample: 2 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 3 | 2) Compile the snappycompress plugin for tctl 4 | ``` 5 | go build -o ../bin/snappycompress-plugin plugin/main.go 6 | ``` 7 | 3) Run the following command to start the worker 8 | ``` 9 | go run worker/main.go 10 | ``` 11 | 4) Run the following command to start the example 12 | ``` 13 | go run starter/main.go 14 | ``` 15 | 5) Run the following command and see the compressed payloads 16 | ``` 17 | export PATH="../bin:$PATH" TEMPORAL_CLI_PLUGIN_DATA_CONVERTER=snappycompress-plugin 18 | tctl workflow show --wid snappycompress_workflowID 19 | ``` 20 | Note: plugins should normally be available in your PATH, we include the current directory in the path here for ease of testing. 21 | -------------------------------------------------------------------------------- /snappycompress/plugin/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/hashicorp/go-plugin" 5 | "github.com/temporalio/samples-go/snappycompress" 6 | cliplugin "github.com/temporalio/tctl/cli/plugin" 7 | ) 8 | 9 | func main() { 10 | var pluginMap = map[string]plugin.Plugin{ 11 | cliplugin.DataConverterPluginType: &cliplugin.DataConverterPlugin{ 12 | Impl: snappycompress.AlwaysCompressDataConverter, 13 | }, 14 | } 15 | 16 | plugin.Serve(&plugin.ServeConfig{ 17 | HandshakeConfig: cliplugin.PluginHandshakeConfig, 18 | Plugins: pluginMap, 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /snappycompress/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/temporalio/samples-go/snappycompress" 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | ) 10 | 11 | func main() { 12 | // The client and worker are heavyweight objects that should be created once per process. 13 | c, err := client.Dial(client.Options{ 14 | // Set DataConverter here so that workflow and activity inputs/results will 15 | // be compressed as required. 16 | DataConverter: snappycompress.AlwaysCompressDataConverter, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | w := worker.New(c, "snappycompress", worker.Options{}) 24 | 25 | w.RegisterWorkflow(snappycompress.Workflow) 26 | w.RegisterActivity(snappycompress.Activity) 27 | 28 | err = w.Run(worker.InterruptCh()) 29 | if err != nil { 30 | log.Fatalln("Unable to start worker", err) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /splitmerge-future/README.md: -------------------------------------------------------------------------------- 1 | This sample workflow demonstrates how to execute multiple activities in parallel and merge their results using futures. 2 | The futures are awaited using Get method in the same order the activities are invoked. See `split-merge-selector` sample 3 | to see how to process them in the order of activity completion instead. 4 | 5 | ### Steps to run this sample: 6 | 7 | 1) YRun a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 8 | 2) Run the following command to start the worker 9 | 10 | ``` 11 | go run splitmerge-future/worker/main.go 12 | ``` 13 | 14 | 3) Run the following command to start the example 15 | 16 | ``` 17 | go run splitmerge-future/starter/main.go 18 | ``` 19 | -------------------------------------------------------------------------------- /splitmerge-future/splitmerge_workflow_test.go: -------------------------------------------------------------------------------- 1 | package splitmerge_future 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/suite" 7 | "go.temporal.io/sdk/testsuite" 8 | ) 9 | 10 | type UnitTestSuite struct { 11 | suite.Suite 12 | testsuite.WorkflowTestSuite 13 | } 14 | 15 | func TestUnitTestSuite(t *testing.T) { 16 | suite.Run(t, new(UnitTestSuite)) 17 | } 18 | 19 | func (s *UnitTestSuite) Test_Workflow() { 20 | env := s.NewTestWorkflowEnvironment() 21 | env.RegisterActivity(ChunkProcessingActivity) 22 | 23 | workerCount := 5 24 | env.ExecuteWorkflow(SampleSplitMergeFutureWorkflow, workerCount) 25 | 26 | s.True(env.IsWorkflowCompleted()) 27 | s.NoError(env.GetWorkflowError()) 28 | 29 | var result ChunkResult 30 | _ = env.GetWorkflowResult(&result) 31 | 32 | totalItem, totalSum := 0, 0 33 | for i := 1; i <= workerCount; i++ { 34 | totalItem += i 35 | totalSum += i * i 36 | } 37 | 38 | s.Equal(totalItem, result.NumberOfItemsInChunk) 39 | s.Equal(totalSum, result.SumInChunk) 40 | } 41 | -------------------------------------------------------------------------------- /splitmerge-future/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | "go.temporal.io/sdk/client" 9 | 10 | "github.com/temporalio/samples-go/splitmerge-future" 11 | ) 12 | 13 | func main() { 14 | // The client is a heavyweight object that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | workflowOptions := client.StartWorkflowOptions{ 24 | ID: "split_merge_future_" + uuid.New(), 25 | TaskQueue: "split-merge-future", 26 | } 27 | 28 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, splitmerge_future.SampleSplitMergeFutureWorkflow, 5) 29 | if err != nil { 30 | log.Fatalln("Unable to execute workflow", err) 31 | } 32 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 33 | } 34 | -------------------------------------------------------------------------------- /splitmerge-future/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/splitmerge-future" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "split-merge-future", worker.Options{}) 23 | 24 | w.RegisterWorkflow(splitmerge_future.SampleSplitMergeFutureWorkflow) 25 | w.RegisterActivity(splitmerge_future.ChunkProcessingActivity) 26 | 27 | err = w.Run(worker.InterruptCh()) 28 | if err != nil { 29 | log.Fatalln("Unable to start worker", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /splitmerge-selector/README.md: -------------------------------------------------------------------------------- 1 | This sample workflow demonstrates how to execute multiple activities in parallel and merge their results using futures. 2 | The futures are awaited using Selector. It allows processing them as soon as they become ready. See `split-merge-future` 3 | sample to see how to process them without Selector in the order of activity invocation instead. 4 | 5 | ### Steps to run this sample: 6 | 7 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 8 | 2) Run the following command to start the worker 9 | 10 | ``` 11 | go run splitmerge-selector/worker/main.go 12 | ``` 13 | 14 | 3) Run the following command to start the example 15 | 16 | ``` 17 | go run splitmerge-selector/starter/main.go 18 | ``` 19 | -------------------------------------------------------------------------------- /splitmerge-selector/splitmerge_workflow_test.go: -------------------------------------------------------------------------------- 1 | package splitmerge_selector 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/suite" 7 | "go.temporal.io/sdk/testsuite" 8 | ) 9 | 10 | type UnitTestSuite struct { 11 | suite.Suite 12 | testsuite.WorkflowTestSuite 13 | } 14 | 15 | func TestUnitTestSuite(t *testing.T) { 16 | suite.Run(t, new(UnitTestSuite)) 17 | } 18 | 19 | func (s *UnitTestSuite) Test_Workflow() { 20 | env := s.NewTestWorkflowEnvironment() 21 | env.RegisterActivity(ChunkProcessingActivity) 22 | 23 | workerCount := 5 24 | env.ExecuteWorkflow(SampleSplitMergeSelectorWorkflow, workerCount) 25 | 26 | s.True(env.IsWorkflowCompleted()) 27 | s.NoError(env.GetWorkflowError()) 28 | 29 | var result ChunkResult 30 | _ = env.GetWorkflowResult(&result) 31 | 32 | totalItem, totalSum := 0, 0 33 | for i := 1; i <= workerCount; i++ { 34 | totalItem += i 35 | totalSum += i * i 36 | } 37 | 38 | s.Equal(totalItem, result.NumberOfItemsInChunk) 39 | s.Equal(totalSum, result.SumInChunk) 40 | } 41 | -------------------------------------------------------------------------------- /splitmerge-selector/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | splitmerge_selector "github.com/temporalio/samples-go/splitmerge-selector" 6 | "log" 7 | 8 | "github.com/pborman/uuid" 9 | "go.temporal.io/sdk/client" 10 | ) 11 | 12 | func main() { 13 | // The client is a heavyweight object that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | workflowOptions := client.StartWorkflowOptions{ 23 | ID: "split_merge_selector_" + uuid.New(), 24 | TaskQueue: "split-merge-selector", 25 | } 26 | 27 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, splitmerge_selector.SampleSplitMergeSelectorWorkflow, 5) 28 | if err != nil { 29 | log.Fatalln("Unable to execute workflow", err) 30 | } 31 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 32 | } 33 | -------------------------------------------------------------------------------- /splitmerge-selector/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | splitmerge_selector "github.com/temporalio/samples-go/splitmerge-selector" 5 | "log" 6 | 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | ) 10 | 11 | func main() { 12 | // The client and worker are heavyweight objects that should be created once per process. 13 | c, err := client.Dial(client.Options{ 14 | HostPort: client.DefaultHostPort, 15 | }) 16 | if err != nil { 17 | log.Fatalln("Unable to create client", err) 18 | } 19 | defer c.Close() 20 | 21 | w := worker.New(c, "split-merge-selector", worker.Options{}) 22 | 23 | w.RegisterWorkflow(splitmerge_selector.SampleSplitMergeSelectorWorkflow) 24 | w.RegisterActivity(splitmerge_selector.ChunkProcessingActivity) 25 | 26 | err = w.Run(worker.InterruptCh()) 27 | if err != nil { 28 | log.Fatalln("Unable to start worker", err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /start-delay/README.md: -------------------------------------------------------------------------------- 1 | This sample demonstrates how to setup a workflow to start after a delay. 2 | 3 | ### Steps to run this sample: 4 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 5 | 2) Run the following command to start the worker 6 | ``` 7 | go run start-delay/worker/main.go 8 | ``` 9 | 3) Run the following command to start the example 10 | ``` 11 | go run start-delay/starter/main.go 12 | ``` 13 | -------------------------------------------------------------------------------- /start-delay/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "time" 7 | 8 | "github.com/pborman/uuid" 9 | "github.com/temporalio/samples-go/helloworld" 10 | "go.temporal.io/sdk/client" 11 | ) 12 | 13 | func main() { 14 | // The client is a heavyweight object that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | workflowOptions := client.StartWorkflowOptions{ 24 | ID: "startdelay_" + uuid.New(), 25 | TaskQueue: "startdelay", 26 | // The first workflow task will be dispatched in 5 minutes 27 | StartDelay: 5 * time.Minute, 28 | } 29 | 30 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, helloworld.Workflow, "from a delayed workflow") 31 | if err != nil { 32 | log.Fatalln("Unable to execute workflow", err) 33 | } 34 | // ExecuteWorkflow will return immediately, but the workflow won't start executing till the StartDelay expires. 35 | log.Println("Scheduled workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 36 | } 37 | -------------------------------------------------------------------------------- /start-delay/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/helloworld" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "startdelay", worker.Options{}) 23 | 24 | w.RegisterWorkflow(helloworld.Workflow) 25 | w.RegisterActivity(helloworld.Activity) 26 | 27 | err = w.Run(worker.InterruptCh()) 28 | if err != nil { 29 | log.Fatalln("Unable to start worker", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /synchronous-proxy/README.md: -------------------------------------------------------------------------------- 1 | ## Synchronous Proxy Sample 2 | 3 | This sample demonstrates how to achieve synchronous interaction with a main workflow. 4 | 5 | We call this pattern a proxy workflow. The proxy workflow sends a signal to the main workflow and then blocks waiting for a signal in response. 6 | 7 | This mimics a synchronous SendAndReceiveSignal feature which Temporal does not currently provide natively. 8 | 9 | The flow of calls is outlined in the diagram below. 10 | 11 | ![Flow Diagram](flow.png) 12 | 13 | ### Steps to run this sample: 14 | 15 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 16 | 2) Run the following command to start the worker 17 | ```shell 18 | go run worker/main.go 19 | ``` 20 | 3) Run the following command to start the simple UI 21 | ```shell 22 | go run ui/main.go 23 | ``` 24 | 25 | Once the UI has exited you will be able to see delivery details in the worker output, as might have been emailed to you in a real implementation. 26 | -------------------------------------------------------------------------------- /synchronous-proxy/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/samples-go/31e215be9ee3e8db6b3e649480aa098bbdd8fc5f/synchronous-proxy/flow.png -------------------------------------------------------------------------------- /synchronous-proxy/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | synchronousproxy "github.com/temporalio/samples-go/synchronous-proxy" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{}) 15 | if err != nil { 16 | log.Fatalln("Unable to create client", err) 17 | } 18 | defer c.Close() 19 | 20 | w := worker.New(c, "ui-driven", worker.Options{}) 21 | 22 | w.RegisterWorkflow(synchronousproxy.OrderWorkflow) 23 | w.RegisterWorkflow(synchronousproxy.UpdateOrderWorkflow) 24 | w.RegisterWorkflow(synchronousproxy.ShippingWorkflow) 25 | w.RegisterActivity(synchronousproxy.RegisterEmail) 26 | w.RegisterActivity(synchronousproxy.ValidateSize) 27 | w.RegisterActivity(synchronousproxy.ValidateColor) 28 | w.RegisterActivity(synchronousproxy.ScheduleDelivery) 29 | w.RegisterActivity(synchronousproxy.SendDeliveryEmail) 30 | 31 | err = w.Run(worker.InterruptCh()) 32 | if err != nil { 33 | log.Fatalln("Unable to start worker", err) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /temporal-fixtures/README.md: -------------------------------------------------------------------------------- 1 | # Temporal Fixtures 2 | 3 | Temporal Fixtures are not meant to be educational code samples. 4 | 5 | Instead, their purpose is to quickly create and share typical benchmarks for testing Temporal Web and other Temporal utilities. 6 | 7 | This way, we can have an easy-to-reference baseline for PRs and communication. -------------------------------------------------------------------------------- /temporal-fixtures/large-event-history/README.md: -------------------------------------------------------------------------------- 1 | This fixture starts a single workflow that adds a lot of event histories `(LengthOfHistory)` and then fails `(WillFailOrNot)`. 2 | 3 | Our UI should handle this properly by showing that this workflow has failed, despite the failure event being on the next page. 4 | 5 | Used in: 6 | - https://github.com/temporalio/web/issues/300 7 | -------------------------------------------------------------------------------- /temporal-fixtures/large-event-history/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | largeeventhistory "github.com/temporalio/samples-go/temporal-fixtures/large-event-history" 9 | "go.temporal.io/sdk/client" 10 | ) 11 | 12 | var ( 13 | LengthOfHistory = 1000 14 | WillFailOrNot = true 15 | ) 16 | 17 | func main() { 18 | // The client is a heavyweight object that should be created once per process. 19 | c, err := client.Dial(client.Options{ 20 | HostPort: client.DefaultHostPort, 21 | }) 22 | if err != nil { 23 | log.Fatalln("Unable to create client", err) 24 | } 25 | defer c.Close() 26 | 27 | id := uuid.New()[0:4] 28 | workflowOptions := client.StartWorkflowOptions{ 29 | ID: "largeeventhistory_" + id, 30 | TaskQueue: "largeeventhistory", 31 | } 32 | 33 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, 34 | largeeventhistory.LargeEventHistoryWorkflow, LengthOfHistory, WillFailOrNot) 35 | if err != nil { 36 | log.Fatalln("Unable to execute workflow", err) 37 | } 38 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 39 | } 40 | -------------------------------------------------------------------------------- /temporal-fixtures/large-event-history/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | largeeventhistory "github.com/temporalio/samples-go/temporal-fixtures/large-event-history" 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | ) 10 | 11 | func main() { 12 | // The client and worker are heavyweight objects that should be created once per process. 13 | c, err := client.Dial(client.Options{ 14 | HostPort: client.DefaultHostPort, 15 | }) 16 | if err != nil { 17 | log.Fatalln("Unable to create client", err) 18 | } 19 | defer c.Close() 20 | 21 | w := worker.New(c, "largeeventhistory", worker.Options{}) 22 | 23 | w.RegisterWorkflow(largeeventhistory.LargeEventHistoryWorkflow) 24 | w.RegisterActivity(largeeventhistory.Activity) 25 | 26 | err = w.Run(worker.InterruptCh()) 27 | if err != nil { 28 | log.Fatalln("Unable to start worker", err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /temporal-fixtures/large-event-history/workflow.go: -------------------------------------------------------------------------------- 1 | package largeeventhistory 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | "context" 8 | 9 | "go.temporal.io/sdk/workflow" 10 | ) 11 | 12 | // LargeEventHistoryWorkflow workflow definition 13 | func LargeEventHistoryWorkflow(ctx workflow.Context, LengthOfHistory int, WillFailOrNot bool) (err error) { 14 | ao := workflow.ActivityOptions{ 15 | StartToCloseTimeout: time.Minute, 16 | } 17 | ctx = workflow.WithActivityOptions(ctx, ao) 18 | 19 | var data []byte 20 | i := 1 21 | 22 | countActivities := LengthOfHistory / 6 23 | for i <= countActivities { 24 | err = workflow.ExecuteActivity(ctx, Activity, data).Get(ctx, nil) 25 | i++ 26 | } 27 | if err != nil { 28 | return errors.New("unexpected Activity failure") 29 | } 30 | 31 | if WillFailOrNot { 32 | return errors.New("intentional workflow failure due to WillFailOrNot parameter") 33 | } 34 | return nil 35 | } 36 | 37 | func Activity(ctx context.Context) error { 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /temporal-fixtures/large-event-history/workflow_test.go: -------------------------------------------------------------------------------- 1 | package largeeventhistory 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/mock" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/stretchr/testify/suite" 10 | "go.temporal.io/sdk/testsuite" 11 | ) 12 | 13 | type UnitTestSuite struct { 14 | suite.Suite 15 | testsuite.WorkflowTestSuite 16 | } 17 | 18 | func TestUnitTestSuite(t *testing.T) { 19 | suite.Run(t, new(UnitTestSuite)) 20 | } 21 | 22 | func (s *UnitTestSuite) Test_LargeEventHistoryWorkflow() { 23 | env := s.NewTestWorkflowEnvironment() 24 | env.SetWorkerOptions(worker.Options{}) 25 | 26 | env.OnActivity(Activity, mock.Anything).Return(nil) 27 | 28 | env.RegisterActivity(Activity) 29 | 30 | env.ExecuteWorkflow(LargeEventHistoryWorkflow, 1024, false) 31 | 32 | s.True(env.IsWorkflowCompleted()) 33 | s.NoError(env.GetWorkflowError()) 34 | } 35 | -------------------------------------------------------------------------------- /temporal-fixtures/largepayload/README.md: -------------------------------------------------------------------------------- 1 | This fixture starts several workflows `(NumberOfWorkflows)` with payloads of size 1mb `(PayloadSize)`. These payloads include: 2 | 3 | - memo 4 | - activity input 5 | - activity result 6 | 7 | Used in: 8 | - Web UI performance testing 9 | -------------------------------------------------------------------------------- /temporal-fixtures/largepayload/activities.go: -------------------------------------------------------------------------------- 1 | package largepayload 2 | 3 | import ( 4 | "context" 5 | "crypto/rand" 6 | 7 | "go.temporal.io/sdk/activity" 8 | ) 9 | 10 | /** 11 | * Sample activities used by large payloads fixture workflow. 12 | */ 13 | 14 | type Activities struct { 15 | } 16 | 17 | func (a *Activities) CreateLargeResultActivity(ctx context.Context, sizeBytes int) ([]byte, error) { 18 | logger := activity.GetLogger(ctx) 19 | logger.Info("Creating large result payload...", sizeBytes) 20 | 21 | token := make([]byte, sizeBytes) 22 | _, err := rand.Read(token) 23 | return token, err 24 | } 25 | 26 | func (a *Activities) ProcessLargeInputActivity(ctx context.Context, input []byte) error { 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /temporal-fixtures/largepayload/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/temporal-fixtures/largepayload" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "largepayload", worker.Options{}) 23 | 24 | w.RegisterWorkflow(largepayload.LargePayloadWorkflow) 25 | w.RegisterActivity(&largepayload.Activities{}) 26 | 27 | err = w.Run(worker.InterruptCh()) 28 | if err != nil { 29 | log.Fatalln("Unable to start worker", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /temporal-fixtures/largepayload/workflow.go: -------------------------------------------------------------------------------- 1 | package largepayload 2 | 3 | import ( 4 | "time" 5 | 6 | "go.temporal.io/sdk/workflow" 7 | ) 8 | 9 | // LargePayloadWorkflow workflow definition 10 | func LargePayloadWorkflow(ctx workflow.Context, payloadSize int) (err error) { 11 | ao := workflow.ActivityOptions{ 12 | StartToCloseTimeout: time.Minute, 13 | } 14 | ctx = workflow.WithActivityOptions(ctx, ao) 15 | 16 | var data []byte 17 | var a *Activities 18 | err = workflow.ExecuteActivity(ctx, a.CreateLargeResultActivity, payloadSize).Get(ctx, &data) 19 | if err != nil { 20 | return err 21 | } 22 | 23 | err = workflow.ExecuteActivity(ctx, a.ProcessLargeInputActivity, data).Get(ctx, nil) 24 | 25 | if err != nil { 26 | workflow.GetLogger(ctx).Error("Workflow failed.", "Error", err.Error()) 27 | } else { 28 | workflow.GetLogger(ctx).Info("Workflow completed.") 29 | } 30 | return err 31 | } 32 | -------------------------------------------------------------------------------- /temporal-fixtures/largepayload/workflow_test.go: -------------------------------------------------------------------------------- 1 | package largepayload 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/mock" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/stretchr/testify/suite" 10 | "go.temporal.io/sdk/testsuite" 11 | ) 12 | 13 | type UnitTestSuite struct { 14 | suite.Suite 15 | testsuite.WorkflowTestSuite 16 | } 17 | 18 | func TestUnitTestSuite(t *testing.T) { 19 | suite.Run(t, new(UnitTestSuite)) 20 | } 21 | 22 | func (s *UnitTestSuite) Test_LargePayloadWorkflow() { 23 | env := s.NewTestWorkflowEnvironment() 24 | env.SetWorkerOptions(worker.Options{}) 25 | var a *Activities 26 | 27 | data := []byte{} 28 | env.OnActivity(a.CreateLargeResultActivity, mock.Anything, 1*1024).Return(data, nil) 29 | env.OnActivity(a.ProcessLargeInputActivity, mock.Anything, data).Return(nil) 30 | 31 | env.RegisterActivity(a) 32 | 33 | env.ExecuteWorkflow(LargePayloadWorkflow, 1*1024) 34 | 35 | s.True(env.IsWorkflowCompleted()) 36 | s.NoError(env.GetWorkflowError()) 37 | } 38 | -------------------------------------------------------------------------------- /temporal-fixtures/namespaces/README.md: -------------------------------------------------------------------------------- 1 | This fixuture registers several namespaces (`NumberOfNamespaces`). 2 | 3 | Used in: 4 | 5 | - Web UI testing 6 | -------------------------------------------------------------------------------- /temporal-fixtures/openNclosed/README.md: -------------------------------------------------------------------------------- 1 | This fixuture starts several workflows `(NumberOfWorkflows)`- all either open for 10 minutes, or closed right away `(KeepOpen flag)`. 2 | 3 | Used in: 4 | 5 | - https://github.com/temporalio/web/pull/315 6 | -------------------------------------------------------------------------------- /temporal-fixtures/openNclosed/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | // "fmt" 6 | "log" 7 | 8 | "github.com/pborman/uuid" 9 | "github.com/temporalio/samples-go/temporal-fixtures/openNclosed" 10 | 11 | "strconv" 12 | 13 | "go.temporal.io/sdk/client" 14 | ) 15 | 16 | var ( 17 | NumberOfWorkflows = 5 18 | KeepOpen = true 19 | ) 20 | 21 | func main() { 22 | // The client is a heavyweight object that should be created once per process. 23 | c, err := client.Dial(client.Options{Namespace: "default"}) 24 | if err != nil { 25 | log.Fatalln("Unable to create client", err) 26 | } 27 | defer c.Close() 28 | 29 | uuidvar := uuid.New() 30 | i := 1 31 | for i <= NumberOfWorkflows { 32 | id := uuidvar[:8] + "###___" + strconv.Itoa(i) 33 | i++ 34 | 35 | workflowOptions := client.StartWorkflowOptions{ 36 | ID: id, 37 | TaskQueue: "open-n-closed", 38 | } 39 | 40 | _, err := c.ExecuteWorkflow(context.Background(), workflowOptions, 41 | openNclosed.OpenNClosedWorkflow, KeepOpen) 42 | if err != nil { 43 | log.Fatalln("Unable to execute workflow", err) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /temporal-fixtures/openNclosed/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/temporal-fixtures/openNclosed" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "open-n-closed", worker.Options{}) 23 | 24 | w.RegisterWorkflow(openNclosed.OpenNClosedWorkflow) 25 | w.RegisterActivity(openNclosed.Activity) 26 | 27 | err = w.Run(worker.InterruptCh()) 28 | if err != nil { 29 | log.Fatalln("Unable to start worker", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /temporal-fixtures/openNclosed/workflow_test.go: -------------------------------------------------------------------------------- 1 | package openNclosed 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/mock" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/stretchr/testify/suite" 10 | "go.temporal.io/sdk/testsuite" 11 | ) 12 | 13 | type UnitTestSuite struct { 14 | suite.Suite 15 | testsuite.WorkflowTestSuite 16 | } 17 | 18 | func TestUnitTestSuite(t *testing.T) { 19 | suite.Run(t, new(UnitTestSuite)) 20 | } 21 | 22 | func (s *UnitTestSuite) Test_LargePayloadWorkflow() { 23 | env := s.NewTestWorkflowEnvironment() 24 | env.SetWorkerOptions(worker.Options{}) 25 | 26 | keepOpen := false 27 | env.OnActivity(Activity, mock.Anything, keepOpen).Return("hello", nil) 28 | 29 | env.RegisterActivity(Activity) 30 | 31 | env.ExecuteWorkflow(OpenNClosedWorkflow, keepOpen) 32 | 33 | s.True(env.IsWorkflowCompleted()) 34 | s.NoError(env.GetWorkflowError()) 35 | } 36 | -------------------------------------------------------------------------------- /temporal-fixtures/rainbow-statuses/README.md: -------------------------------------------------------------------------------- 1 | This fixture starts few sets of workflows (`NumberOfSets`) 2 | 3 | - each set contains workflows of each Status (ie. Open, Terminated, ..) 4 | - each workflow contains custom search attributes 5 | - each workflow receives a signal 6 | 7 | ![CleanShot 2021-08-11 at 15 35 22@2x](https://user-images.githubusercontent.com/6764957/129112490-40e41641-93ed-4df2-8f58-3a0bbf4f16e6.png) 8 | 9 | ![CleanShot 2021-08-11 at 15 35 49@2x](https://user-images.githubusercontent.com/6764957/129112578-636208e0-e0c6-489d-acf5-487b02ce7f31.png) 10 | 11 | Used in: 12 | 13 | - Web UI testing 14 | -------------------------------------------------------------------------------- /temporal-fixtures/rainbow-statuses/activities.go: -------------------------------------------------------------------------------- 1 | package rainbowstatuses 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "time" 7 | ) 8 | 9 | /** 10 | * Sample activities used by large payloads fixture workflow. 11 | */ 12 | 13 | type Activities struct { 14 | } 15 | 16 | func (a *Activities) CompletedActivity(ctx context.Context) error { 17 | return nil 18 | } 19 | 20 | func (a *Activities) FailedActivity(ctx context.Context) error { 21 | return errors.New("manual failure") 22 | } 23 | 24 | func (a *Activities) LongActivity(ctx context.Context) error { 25 | time.Sleep(24 * time.Hour) 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /temporal-fixtures/rainbow-statuses/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | rainbowstatuses "github.com/temporalio/samples-go/temporal-fixtures/rainbow-statuses" 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | ) 10 | 11 | func main() { 12 | // The client and worker are heavyweight objects that should be created once per process. 13 | c, err := client.Dial(client.Options{ 14 | HostPort: client.DefaultHostPort, 15 | }) 16 | if err != nil { 17 | log.Fatalln("Unable to create client", err) 18 | } 19 | defer c.Close() 20 | 21 | w := worker.New(c, "rainbow-statuses", worker.Options{}) 22 | 23 | w.RegisterWorkflow(rainbowstatuses.RainbowStatusesWorkflow) 24 | w.RegisterActivity(&rainbowstatuses.Activities{}) 25 | 26 | err = w.Run(worker.InterruptCh()) 27 | if err != nil { 28 | log.Fatalln("Unable to start worker", err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /temporal-fixtures/stuck-workflows/README.md: -------------------------------------------------------------------------------- 1 | This fixture starts few stuck workflows: 2 | 3 | - no running worker listening on a task queue 4 | - activity that fails and is being infinitely retried 5 | 6 | Used in: 7 | 8 | - Web UI development 9 | -------------------------------------------------------------------------------- /temporal-fixtures/stuck-workflows/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | stuckworkflows "github.com/temporalio/samples-go/temporal-fixtures/stuck-workflows" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "stuck-workflows", worker.Options{}) 23 | 24 | w.RegisterWorkflow(stuckworkflows.StuckWorkflow) 25 | w.RegisterActivity(stuckworkflows.StuckWorkflowActivity) 26 | 27 | err = w.Run(worker.InterruptCh()) 28 | if err != nil { 29 | log.Fatalln("Unable to start worker", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /temporal-fixtures/stuck-workflows/workflow_test.go: -------------------------------------------------------------------------------- 1 | package stuckworkflows 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/mock" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/stretchr/testify/suite" 10 | "go.temporal.io/sdk/testsuite" 11 | ) 12 | 13 | type UnitTestSuite struct { 14 | suite.Suite 15 | testsuite.WorkflowTestSuite 16 | } 17 | 18 | func TestUnitTestSuite(t *testing.T) { 19 | suite.Run(t, new(UnitTestSuite)) 20 | } 21 | 22 | func (s *UnitTestSuite) Test_LargePayloadWorkflow() { 23 | env := s.NewTestWorkflowEnvironment() 24 | env.SetWorkerOptions(worker.Options{}) 25 | 26 | env.OnActivity(StuckWorkflowActivity, mock.Anything).Return(nil) 27 | 28 | env.RegisterActivity(StuckWorkflowActivity) 29 | 30 | env.ExecuteWorkflow(StuckWorkflow) 31 | 32 | s.True(env.IsWorkflowCompleted()) 33 | s.NoError(env.GetWorkflowError()) 34 | } 35 | -------------------------------------------------------------------------------- /timer/README.md: -------------------------------------------------------------------------------- 1 | ### Steps to run this sample: 2 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 3 | 2) Run the following command to start the worker 4 | ``` 5 | go run timer/worker/main.go 6 | ``` 7 | 3) Run the following command to start the example 8 | ``` 9 | go run timer/starter/main.go 10 | ``` 11 | -------------------------------------------------------------------------------- /timer/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "time" 7 | 8 | "github.com/pborman/uuid" 9 | "go.temporal.io/sdk/client" 10 | 11 | "github.com/temporalio/samples-go/timer" 12 | ) 13 | 14 | func main() { 15 | // The client is a heavyweight object that should be created once per process. 16 | c, err := client.Dial(client.Options{ 17 | HostPort: client.DefaultHostPort, 18 | }) 19 | if err != nil { 20 | log.Fatalln("Unable to create client", err) 21 | } 22 | defer c.Close() 23 | 24 | workflowOptions := client.StartWorkflowOptions{ 25 | ID: "timer_" + uuid.New(), 26 | TaskQueue: "timer", 27 | } 28 | 29 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, timer.SampleTimerWorkflow, time.Second*3) 30 | if err != nil { 31 | log.Fatalln("Unable to execute workflow", err) 32 | } 33 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 34 | } 35 | -------------------------------------------------------------------------------- /timer/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | "github.com/temporalio/samples-go/timer" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "timer", worker.Options{ 23 | MaxConcurrentActivityExecutionSize: 3, 24 | }) 25 | 26 | w.RegisterWorkflow(timer.SampleTimerWorkflow) 27 | w.RegisterActivity(timer.OrderProcessingActivity) 28 | w.RegisterActivity(timer.SendEmailActivity) 29 | 30 | err = w.Run(worker.InterruptCh()) 31 | if err != nil { 32 | log.Fatalln("Unable to start worker", err) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /typed-searchattributes/README.md: -------------------------------------------------------------------------------- 1 | ### Typed Search Attributes Sample 2 | 3 | This sample shows how to use and test the Typed Search Attributes API. 4 | 5 | ### Steps to run this sample: 6 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 7 | 2) Run the following command to start the worker: 8 | ``` 9 | go run typed-searchattributes/worker/main.go 10 | ``` 11 | 3) Run the following command to start the example: 12 | ``` 13 | go run typed-searchattributes/starter/main.go 14 | ``` 15 | -------------------------------------------------------------------------------- /typed-searchattributes/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/pborman/uuid" 8 | "go.temporal.io/sdk/client" 9 | "go.temporal.io/sdk/temporal" 10 | 11 | typedsearchattributes "github.com/temporalio/samples-go/typed-searchattributes" 12 | ) 13 | 14 | func main() { 15 | // The client is a heavyweight object that should be created once per process. 16 | c, err := client.Dial(client.Options{ 17 | HostPort: client.DefaultHostPort, 18 | }) 19 | if err != nil { 20 | log.Fatalln("Unable to create client", err) 21 | } 22 | defer c.Close() 23 | 24 | workflowOptions := client.StartWorkflowOptions{ 25 | ID: "typed-search_attributes_" + uuid.New(), 26 | TaskQueue: "typed-search-attributes", 27 | TypedSearchAttributes: temporal.NewSearchAttributes(typedsearchattributes.CustomIntKey.ValueSet(1)), 28 | } 29 | 30 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, typedsearchattributes.SearchAttributesWorkflow) 31 | if err != nil { 32 | log.Fatalln("Unable to execute workflow", err) 33 | } 34 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 35 | } 36 | -------------------------------------------------------------------------------- /typed-searchattributes/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "go.temporal.io/sdk/client" 7 | "go.temporal.io/sdk/worker" 8 | 9 | typedsearchattributes "github.com/temporalio/samples-go/typed-searchattributes" 10 | ) 11 | 12 | func main() { 13 | // The client and worker are heavyweight objects that should be created once per process. 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | w := worker.New(c, "typed-search-attributes", worker.Options{}) 23 | 24 | w.RegisterWorkflow(typedsearchattributes.SearchAttributesWorkflow) 25 | 26 | err = w.Run(worker.InterruptCh()) 27 | if err != nil { 28 | log.Fatalln("Unable to start worker", err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /updatabletimer/README.md: -------------------------------------------------------------------------------- 1 | # Updatable Timer Sample 2 | 3 | A helper structure that supports blocking sleep that can be rescheduled at any moment. 4 | 5 | Demonstrates: 6 | 7 | * Timer and its cancellation 8 | * Signal Channel 9 | * Selector used to wait on both timer and channel 10 | 11 | ### Steps to run this sample: 12 | 13 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 14 | 2) Run the following command to start the worker 15 | 16 | ``` 17 | go run updatabletimer/worker/main.go 18 | ``` 19 | 20 | 3) Run the following command to start the example 21 | 22 | ``` 23 | go run updatabletimer/starter/main.go 24 | ``` 25 | 26 | 4) Run the following command to update the timer wake-up time 27 | 28 | ``` 29 | go run updatabletimer/updater/main.go 30 | ``` -------------------------------------------------------------------------------- /updatabletimer/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "github.com/temporalio/samples-go/updatabletimer" 6 | "log" 7 | "time" 8 | 9 | "go.temporal.io/sdk/client" 10 | ) 11 | 12 | // Starts updatable timer workflow with initial wake-up time in 30 seconds. 13 | func main() { 14 | c, err := client.Dial(client.Options{ 15 | HostPort: client.DefaultHostPort, 16 | }) 17 | if err != nil { 18 | log.Fatalln("Unable to create client", err) 19 | } 20 | defer c.Close() 21 | 22 | workflowOptions := client.StartWorkflowOptions{ 23 | ID: updatabletimer.WorkflowID, 24 | TaskQueue: updatabletimer.TaskQueue, 25 | } 26 | 27 | wakeUpTime := time.Now().Add(30 * time.Second) 28 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, updatabletimer.Workflow, wakeUpTime) 29 | if err != nil { 30 | log.Fatalln("Unable to start workflow", err) 31 | } 32 | log.Println("Started workflow that is going to block on an updatable timer", 33 | "WorkflowID", we.GetID(), "RunID", we.GetRunID(), "WakeUpTime", wakeUpTime) 34 | } 35 | -------------------------------------------------------------------------------- /updatabletimer/updater/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "github.com/temporalio/samples-go/updatabletimer" 6 | "log" 7 | "time" 8 | 9 | "go.temporal.io/sdk/client" 10 | ) 11 | 12 | // Signals updatable timer workflow to change wake-up time to 20 seconds from now. 13 | func main() { 14 | // The client is a heavyweight object that should be created once per process. 15 | c, err := client.Dial(client.Options{ 16 | HostPort: client.DefaultHostPort, 17 | }) 18 | if err != nil { 19 | log.Fatalln("Unable to create client", err) 20 | } 21 | defer c.Close() 22 | 23 | wakeUpTime := time.Now().Add(20 * time.Second) 24 | 25 | err = c.SignalWorkflow(context.Background(), updatabletimer.WorkflowID, "", updatabletimer.SignalType, wakeUpTime) 26 | if err != nil { 27 | log.Fatalln("Unable to signale workflow", err) 28 | } 29 | log.Println("Signaled workflow to update wake-up time", 30 | "WorkflowID", updatabletimer.WorkflowID, "WakeUpTime", wakeUpTime) 31 | } 32 | -------------------------------------------------------------------------------- /updatabletimer/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/temporalio/samples-go/updatabletimer" 5 | "log" 6 | 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | ) 10 | 11 | func main() { 12 | // The client and worker are heavyweight objects that should be created once per process. 13 | c, err := client.Dial(client.Options{ 14 | HostPort: client.DefaultHostPort, 15 | }) 16 | if err != nil { 17 | log.Fatalln("Unable to create client", err) 18 | } 19 | defer c.Close() 20 | 21 | w := worker.New(c, updatabletimer.TaskQueue, worker.Options{}) 22 | 23 | w.RegisterWorkflow(updatabletimer.Workflow) 24 | 25 | err = w.Run(worker.InterruptCh()) 26 | if err != nil { 27 | log.Fatalln("Unable to start worker", err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /update/README.md: -------------------------------------------------------------------------------- 1 | ### Update Sample 2 | 3 | Here we show an example of a workflow representing a single integer counter 4 | value that can be mutated via an update called `fetch_and_add` which adds its 5 | argument to the counter and returns the original value of the counter. Negative 6 | arguments will be rejected by the `fetch_and_add`'s associated validator and 7 | thus will not be included in the workflow history. 8 | 9 | ### Steps to run this sample: 10 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 11 | 2) Run the following command to start the worker 12 | ``` 13 | go run update/worker/main.go 14 | ``` 15 | 3) Run the following command to start the example 16 | ``` 17 | go run update/starter/main.go 18 | ``` 19 | -------------------------------------------------------------------------------- /update/update.go: -------------------------------------------------------------------------------- 1 | package update 2 | 3 | import ( 4 | "fmt" 5 | 6 | "go.temporal.io/sdk/workflow" 7 | ) 8 | 9 | const ( 10 | FetchAndAdd = "fetch_and_add" 11 | Done = "done" 12 | ) 13 | 14 | func Counter(ctx workflow.Context) (int, error) { 15 | log := workflow.GetLogger(ctx) 16 | counter := 0 17 | 18 | if err := workflow.SetUpdateHandlerWithOptions( 19 | ctx, 20 | FetchAndAdd, 21 | func(ctx workflow.Context, i int) (int, error) { 22 | tmp := counter 23 | counter += i 24 | log.Info("counter updated", "addend", i, "new-value", counter) 25 | return tmp, nil 26 | }, 27 | workflow.UpdateHandlerOptions{Validator: nonNegative}, 28 | ); err != nil { 29 | return 0, err 30 | } 31 | 32 | _ = workflow.GetSignalChannel(ctx, Done).Receive(ctx, nil) 33 | return counter, ctx.Err() 34 | } 35 | 36 | func nonNegative(ctx workflow.Context, i int) error { 37 | log := workflow.GetLogger(ctx) 38 | if i < 0 { 39 | log.Debug("Rejecting negative update", "addend", i) 40 | return fmt.Errorf("addend must be non-negative (%v)", i) 41 | } 42 | log.Debug("Accepting update", "addend", i) 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /update/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/temporalio/samples-go/update" 7 | "go.temporal.io/sdk/client" 8 | "go.temporal.io/sdk/worker" 9 | ) 10 | 11 | func main() { 12 | // The client and worker are heavyweight objects that should be created once per process. 13 | c, err := client.Dial(client.Options{}) 14 | if err != nil { 15 | log.Fatalln("Unable to create client", err) 16 | } 17 | defer c.Close() 18 | 19 | w := worker.New(c, "update", worker.Options{}) 20 | 21 | w.RegisterWorkflow(update.Counter) 22 | 23 | err = w.Run(worker.InterruptCh()) 24 | if err != nil { 25 | log.Fatalln("Unable to start worker", err) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /worker-specific-task-queues/worker_specific_task_queue.go: -------------------------------------------------------------------------------- 1 | package worker_specific_task_queues 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type WorkerSpecificTaskQueue struct { 8 | TaskQueue string 9 | } 10 | 11 | // GetWorkerSpecificTaskQueue is an activity to get a hosts unique task queue. 12 | func (q WorkerSpecificTaskQueue) GetWorkerSpecificTaskQueue(ctx context.Context) (string, error) { 13 | return q.TaskQueue, nil 14 | } 15 | -------------------------------------------------------------------------------- /workflow-security-interceptor/README.md: -------------------------------------------------------------------------------- 1 | # Child Workflow Type Validation Interceptor Sample 2 | 3 | This sample shows how to make a worker interceptor that intercepts child workflow requests. 4 | The requests are validated using an activity. 5 | Most of the sample complexity is in creating a custom implementation of a ChildWorkflowResult. 6 | 7 | 8 | ### Steps to run this sample: 9 | 1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). 10 | 2) Run the following command to start the worker 11 | ``` 12 | go run ./workflow-security-interceptor/worker 13 | ``` 14 | 3) Run the following command to start the example 15 | ``` 16 | go run ./workflow-security-interceptor/starter 17 | ``` 18 | The expected output is workflow failure with the following message: 19 | ``` 20 | Child workflow type "UnallowedChildWorkflow" not allowed (type: not-allowed, retryable: true) 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /workflow-security-interceptor/security_interceptor_test.go: -------------------------------------------------------------------------------- 1 | package workflow_security_interceptor_test 2 | 3 | import ( 4 | "go.temporal.io/sdk/interceptor" 5 | "go.temporal.io/sdk/worker" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | "go.temporal.io/sdk/testsuite" 10 | 11 | wsi "github.com/temporalio/samples-go/workflow-security-interceptor" 12 | ) 13 | 14 | func TestSecurityInterceptorWorkflow(t *testing.T) { 15 | var ts testsuite.WorkflowTestSuite 16 | env := ts.NewTestWorkflowEnvironment() 17 | env.SetWorkerOptions(worker.Options{ 18 | Interceptors: []interceptor.WorkerInterceptor{wsi.NewWorkerInterceptor()}, 19 | }) 20 | env.RegisterWorkflow(wsi.Workflow) 21 | env.RegisterWorkflow(wsi.ChildWorkflow) 22 | env.RegisterWorkflow(wsi.ProhibitedChildWorkflow) 23 | env.RegisterActivity(wsi.ValidateChildWorkflowTypeActivity) 24 | 25 | env.ExecuteWorkflow(wsi.Workflow) 26 | err := env.GetWorkflowError() 27 | 28 | require.Error(t, err, "expected prohibited child workflow to fail") 29 | require.Contains(t, err.Error(), "Child workflow type \"ProhibitedChildWorkflow\" not allowed", 30 | "expected error to contain prohibited child workflow type message") 31 | } 32 | -------------------------------------------------------------------------------- /workflow-security-interceptor/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | securityinterceptor "github.com/temporalio/samples-go/logger-interceptor" 6 | "log" 7 | 8 | "go.temporal.io/sdk/client" 9 | ) 10 | 11 | func main() { 12 | // The client is a heavyweight object that should be created once per process. 13 | c, err := client.Dial(client.Options{}) 14 | if err != nil { 15 | log.Fatalln("Unable to create client", err) 16 | } 17 | defer c.Close() 18 | 19 | workflowOptions := client.StartWorkflowOptions{ 20 | TaskQueue: "security-interceptor", 21 | } 22 | 23 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, securityinterceptor.Workflow, "Temporal") 24 | if err != nil { 25 | log.Fatalln("Unable to execute workflow", err) 26 | } 27 | 28 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 29 | 30 | // Synchronously wait for the workflow completion. 31 | var result string 32 | err = we.Get(context.Background(), &result) 33 | if err != nil { 34 | log.Fatalln("Unable get workflow result", err) 35 | } 36 | log.Println("Workflow result:", result) 37 | } 38 | -------------------------------------------------------------------------------- /workflow-security-interceptor/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | securityinterceptor "github.com/temporalio/samples-go/workflow-security-interceptor" 5 | "go.temporal.io/sdk/client" 6 | sdkinterceptor "go.temporal.io/sdk/interceptor" 7 | "go.temporal.io/sdk/worker" 8 | "log" 9 | ) 10 | 11 | func main() { 12 | // The client and worker are heavyweight objects that should be created once per process. 13 | c, err := client.Dial(client.Options{}) 14 | if err != nil { 15 | log.Fatalln("Unable to create client", err) 16 | } 17 | defer c.Close() 18 | 19 | w := worker.New(c, "security-interceptor", worker.Options{ 20 | Interceptors: []sdkinterceptor.WorkerInterceptor{securityinterceptor.NewWorkerInterceptor()}, 21 | }) 22 | w.RegisterWorkflow(securityinterceptor.Workflow) 23 | w.RegisterWorkflow(securityinterceptor.ChildWorkflow) 24 | w.RegisterWorkflow(securityinterceptor.ProhibitedChildWorkflow) 25 | // Activity used by the interceptor 26 | w.RegisterActivity(securityinterceptor.ValidateChildWorkflowTypeActivity) 27 | 28 | err = w.Run(worker.InterruptCh()) 29 | if err != nil { 30 | log.Fatalln("Unable to start worker", err) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /workflow-security-interceptor/workflow.go: -------------------------------------------------------------------------------- 1 | package workflow_security_interceptor 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | func ChildWorkflow(ctx workflow.Context) (string, error) { 8 | return "OK", nil 9 | } 10 | 11 | func ProhibitedChildWorkflow(ctx workflow.Context) (string, error) { 12 | return "OK", nil 13 | } 14 | 15 | func Workflow(ctx workflow.Context) error { 16 | err := workflow.ExecuteChildWorkflow(ctx, ChildWorkflow).Get(ctx, nil) 17 | if err != nil { 18 | return err 19 | } 20 | // This will fail because the child workflow type is not allowed 21 | err = workflow.ExecuteChildWorkflow(ctx, ProhibitedChildWorkflow).Get(ctx, nil) 22 | if err != nil { 23 | return err 24 | } 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /zapadapter/starter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/temporalio/samples-go/zapadapter" 8 | "go.temporal.io/sdk/client" 9 | ) 10 | 11 | func main() { 12 | // The client is a heavyweight object that should be created once per process. 13 | c, err := client.Dial(client.Options{}) 14 | if err != nil { 15 | log.Fatalln("Unable to create client", err) 16 | } 17 | defer c.Close() 18 | 19 | workflowOptions := client.StartWorkflowOptions{ 20 | ID: "zap_logger_workflow_id", 21 | TaskQueue: "zap-logger", 22 | } 23 | 24 | we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, zapadapter.Workflow, "") 25 | if err != nil { 26 | log.Fatalln("Unable to execute workflow", err) 27 | } 28 | 29 | log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) 30 | 31 | // Synchronously wait for the workflow completion. 32 | var result interface{} 33 | err = we.Get(context.Background(), &result) 34 | if err != nil { 35 | log.Fatalln("Unable get workflow result", err) 36 | } 37 | log.Println("Workflow completed. Check worker logs.") 38 | } 39 | --------------------------------------------------------------------------------