Skip to content

Commit 27fd027

Browse files
committed
feat: Restructure activities and operations with generator pattern
Signed-off-by: Diana Zawadzki <[email protected]>
1 parent 2fd7776 commit 27fd027

File tree

15 files changed

+610
-87
lines changed

15 files changed

+610
-87
lines changed

new_samples/activities/README.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<!-- THIS IS A GENERATED FILE -->
2+
<!-- PLEASE DO NOT EDIT -->
3+
4+
# Dynamic Activity Sample
5+
6+
## Prerequisites
7+
8+
0. Install Cadence CLI. See instruction [here](https://cadenceworkflow.io/docs/cli/).
9+
1. Run the Cadence server:
10+
1. Clone the [Cadence](https://github.com/cadence-workflow/cadence) repository if you haven't done already: `git clone https://github.com/cadence-workflow/cadence.git`
11+
2. Run `docker compose -f docker/docker-compose.yml up` to start Cadence server
12+
3. See more details at https://github.com/uber/cadence/blob/master/README.md
13+
2. Once everything is up and running in Docker, open [localhost:8088](localhost:8088) to view Cadence UI.
14+
3. Register the `cadence-samples` domain:
15+
16+
```bash
17+
cadence --env development --domain cadence-samples domain register
18+
```
19+
20+
Refresh the [domains page](http://localhost:8088/domains) from step 2 to verify `cadence-samples` is registered.
21+
22+
## Steps to run sample
23+
24+
Inside the folder this sample is defined, run the following command:
25+
26+
```bash
27+
go run .
28+
```
29+
30+
This will call the main function in main.go which starts the worker, which will be execute the sample workflow code
31+
32+
## Dynamic Activity Workflow
33+
34+
This sample demonstrates invoking activities by string name rather than function reference.
35+
36+
### Start the Workflow
37+
38+
```bash
39+
cadence --env development \
40+
--domain cadence-samples \
41+
workflow start \
42+
--tl cadence-samples-worker \
43+
--et 60 \
44+
--workflow_type cadence_samples.DynamicWorkflow \
45+
--input '{"message":"Cadence"}'
46+
```
47+
48+
Verify that your workflow started and completed successfully.
49+
50+
### Key Concept
51+
52+
Instead of passing the function directly:
53+
```go
54+
workflow.ExecuteActivity(ctx, MyActivity, input)
55+
```
56+
57+
Pass the activity name as a string:
58+
```go
59+
workflow.ExecuteActivity(ctx, "cadence_samples.DynamicGreetingActivity", input)
60+
```
61+
62+
This is useful for plugin systems or configuration-driven workflows.
63+
64+
65+
## References
66+
67+
* The website: https://cadenceworkflow.io
68+
* Cadence's server: https://github.com/uber/cadence
69+
* Cadence's Go client: https://github.com/uber-go/cadence-client
70+

new_samples/activities/dynamic_workflow.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
1-
package workflows
1+
package main
22

33
import (
44
"context"
5+
"time"
6+
57
"go.uber.org/cadence/activity"
68
"go.uber.org/cadence/workflow"
79
"go.uber.org/zap"
8-
"time"
910
)
1011

12+
// DynamicGreetingActivityName is the registered name for the activity.
13+
// This demonstrates how to invoke activities by string name rather than function reference.
1114
const DynamicGreetingActivityName = "cadence_samples.DynamicGreetingActivity"
1215

1316
type dynamicWorkflowInput struct {
1417
Message string `json:"message"`
1518
}
1619

20+
// DynamicWorkflow demonstrates calling activities using string names for dynamic behavior.
21+
// Instead of passing the function directly to ExecuteActivity, we pass the activity name.
22+
// This is useful for plugin systems or configuration-driven workflows.
1723
func DynamicWorkflow(ctx workflow.Context, input dynamicWorkflowInput) (string, error) {
1824
ao := workflow.ActivityOptions{
1925
ScheduleToStartTimeout: time.Minute,
@@ -25,6 +31,7 @@ func DynamicWorkflow(ctx workflow.Context, input dynamicWorkflowInput) (string,
2531
logger.Info("DynamicWorkflow started")
2632

2733
var greetingMsg string
34+
// Note: We pass the activity NAME (string) instead of the function reference
2835
err := workflow.ExecuteActivity(ctx, DynamicGreetingActivityName, input.Message).Get(ctx, &greetingMsg)
2936
if err != nil {
3037
logger.Error("DynamicGreetingActivity failed", zap.Error(err))
@@ -35,8 +42,10 @@ func DynamicWorkflow(ctx workflow.Context, input dynamicWorkflowInput) (string,
3542
return greetingMsg, nil
3643
}
3744

45+
// DynamicGreetingActivity is a simple activity that returns a greeting message.
3846
func DynamicGreetingActivity(ctx context.Context, message string) (string, error) {
3947
logger := activity.GetLogger(ctx)
4048
logger.Info("DynamicGreetingActivity started.")
4149
return "Hello, " + message, nil
4250
}
51+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!-- THIS IS A GENERATED FILE -->
2+
<!-- PLEASE DO NOT EDIT -->
3+
4+
# Sample Generator
5+
6+
This folder is NOT part of the actual sample. It exists only for contributors who work on this sample. Please disregard it if you are trying to learn about Cadence.
7+
8+
To create a better learning experience for Cadence users, each sample folder is designed to be self contained. Users can view every part of writing and running workflows, including:
9+
10+
* Cadence client initialization
11+
* Worker with workflow and activity registrations
12+
* Workflow starter
13+
* and the workflow code itself
14+
15+
Some samples may have more or fewer parts depending on what they need to demonstrate.
16+
17+
In most cases, the workflow code (e.g. `workflow.go`) is the part that users care about. The rest is boilerplate needed to run that workflow. For each sample folder, the workflow code should be written by hand. The boilerplate can be generated. Keeping all parts inside one folder gives early learners more value because they can see everything together rather than jumping across directories.
18+
19+
## Contributing
20+
21+
* When creating a new sample, follow the steps mentioned in the README file in the main samples folder.
22+
* To update the sample workflow code, edit the workflow file directly.
23+
* To update the worker, client, or other boilerplate logic, edit the generator file. If your change applies to all samples, update the common generator file inside the `template` folder. Edit the generator file in this folder only when the change should affect this sample alone.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
## Dynamic Activity Workflow
2+
3+
This sample demonstrates invoking activities by string name rather than function reference.
4+
5+
### Start the Workflow
6+
7+
```bash
8+
cadence --env development \
9+
--domain cadence-samples \
10+
workflow start \
11+
--tl cadence-samples-worker \
12+
--et 60 \
13+
--workflow_type cadence_samples.DynamicWorkflow \
14+
--input '{"message":"Cadence"}'
15+
```
16+
17+
Verify that your workflow started and completed successfully.
18+
19+
### Key Concept
20+
21+
Instead of passing the function directly:
22+
```go
23+
workflow.ExecuteActivity(ctx, MyActivity, input)
24+
```
25+
26+
Pass the activity name as a string:
27+
```go
28+
workflow.ExecuteActivity(ctx, "cadence_samples.DynamicGreetingActivity", input)
29+
```
30+
31+
This is useful for plugin systems or configuration-driven workflows.
32+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package main
2+
3+
import "github.com/uber-common/cadence-samples/new_samples/template"
4+
5+
func main() {
6+
// Define the data for Dynamic Activity sample
7+
data := template.TemplateData{
8+
SampleName: "Dynamic Activity",
9+
Workflows: []string{"DynamicWorkflow"},
10+
Activities: []string{"DynamicGreetingActivity"},
11+
}
12+
13+
template.GenerateAll(data)
14+
}
15+
16+
// Implement custom generator below
17+

new_samples/activities/main.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// THIS IS A GENERATED FILE
2+
// PLEASE DO NOT EDIT
3+
4+
package main
5+
6+
import (
7+
"fmt"
8+
"os"
9+
"os/signal"
10+
"syscall"
11+
)
12+
13+
func main() {
14+
StartWorker()
15+
16+
done := make(chan os.Signal, 1)
17+
signal.Notify(done, syscall.SIGINT)
18+
fmt.Println("Cadence worker started, press ctrl+c to terminate...")
19+
<-done
20+
}

new_samples/activities/parallel_pick_first_workflow.go

Lines changed: 0 additions & 71 deletions
This file was deleted.

new_samples/activities/worker.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// THIS IS A GENERATED FILE
2+
// PLEASE DO NOT EDIT
3+
4+
// Package worker implements a Cadence worker with basic configurations.
5+
package main
6+
7+
import (
8+
"github.com/uber-go/tally"
9+
apiv1 "github.com/uber/cadence-idl/go/proto/api/v1"
10+
"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient"
11+
"go.uber.org/cadence/activity"
12+
"go.uber.org/cadence/compatibility"
13+
"go.uber.org/cadence/worker"
14+
"go.uber.org/cadence/workflow"
15+
"go.uber.org/yarpc"
16+
"go.uber.org/yarpc/peer"
17+
yarpchostport "go.uber.org/yarpc/peer/hostport"
18+
"go.uber.org/yarpc/transport/grpc"
19+
"go.uber.org/zap"
20+
"go.uber.org/zap/zapcore"
21+
)
22+
23+
const (
24+
HostPort = "127.0.0.1:7833"
25+
Domain = "cadence-samples"
26+
// TaskListName identifies set of client workflows, activities, and workers.
27+
// It could be your group or client or application name.
28+
TaskListName = "cadence-samples-worker"
29+
ClientName = "cadence-samples-worker"
30+
CadenceService = "cadence-frontend"
31+
)
32+
33+
// StartWorker creates and starts a basic Cadence worker.
34+
func StartWorker() {
35+
logger, cadenceClient := BuildLogger(), BuildCadenceClient()
36+
workerOptions := worker.Options{
37+
Logger: logger,
38+
MetricsScope: tally.NewTestScope(TaskListName, nil),
39+
}
40+
41+
w := worker.New(
42+
cadenceClient,
43+
Domain,
44+
TaskListName,
45+
workerOptions)
46+
// HelloWorld workflow registration
47+
w.RegisterWorkflowWithOptions(DynamicWorkflow, workflow.RegisterOptions{Name: "cadence_samples.DynamicWorkflow"})
48+
w.RegisterActivityWithOptions(DynamicGreetingActivity, activity.RegisterOptions{Name: "cadence_samples.DynamicGreetingActivity"})
49+
50+
err := w.Start()
51+
if err != nil {
52+
panic("Failed to start worker: " + err.Error())
53+
}
54+
logger.Info("Started Worker.", zap.String("worker", TaskListName))
55+
56+
}
57+
58+
func BuildCadenceClient(dialOptions ...grpc.DialOption) workflowserviceclient.Interface {
59+
grpcTransport := grpc.NewTransport()
60+
// Create a single peer chooser that identifies the host/port and configures
61+
// a gRPC dialer with TLS credentials
62+
myChooser := peer.NewSingle(
63+
yarpchostport.Identify(HostPort),
64+
grpcTransport.NewDialer(dialOptions...),
65+
)
66+
outbound := grpcTransport.NewOutbound(myChooser)
67+
68+
dispatcher := yarpc.NewDispatcher(yarpc.Config{
69+
Name: ClientName,
70+
Outbounds: yarpc.Outbounds{
71+
CadenceService: {Unary: outbound},
72+
},
73+
})
74+
if err := dispatcher.Start(); err != nil {
75+
panic("Failed to start dispatcher: " + err.Error())
76+
}
77+
78+
clientConfig := dispatcher.ClientConfig(CadenceService)
79+
80+
// Create a compatibility adapter that wraps proto-based YARPC clients
81+
// to provide a unified interface for domain, workflow, worker, and visibility APIs
82+
return compatibility.NewThrift2ProtoAdapter(
83+
apiv1.NewDomainAPIYARPCClient(clientConfig),
84+
apiv1.NewWorkflowAPIYARPCClient(clientConfig),
85+
apiv1.NewWorkerAPIYARPCClient(clientConfig),
86+
apiv1.NewVisibilityAPIYARPCClient(clientConfig),
87+
)
88+
}
89+
90+
func BuildLogger() *zap.Logger {
91+
config := zap.NewDevelopmentConfig()
92+
config.Level.SetLevel(zapcore.InfoLevel)
93+
94+
var err error
95+
logger, err := config.Build()
96+
if err != nil {
97+
panic("Failed to setup logger: " + err.Error())
98+
}
99+
100+
return logger
101+
}

0 commit comments

Comments
 (0)