Skip to content

Commit f2e2eef

Browse files
Adds blocks example
1 parent f7320eb commit f2e2eef

File tree

3 files changed

+279
-0
lines changed

3 files changed

+279
-0
lines changed

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export PATH := $(GOPATH)/bin:$(PATH)
77
default: test
88

99
PROGS = helloworld \
10+
blocks \
1011
versioning \
1112
delaystart \
1213
branch \
@@ -52,6 +53,7 @@ TEST_DIRS=./cmd/samples/cron \
5253
./cmd/samples/dsl \
5354
./cmd/samples/expense \
5455
./cmd/samples/fileprocessing \
56+
./cmd/samples/blocks \
5557
./cmd/samples/recipes/branch \
5658
./cmd/samples/recipes/choice \
5759
./cmd/samples/recipes/greetings \
@@ -82,6 +84,9 @@ cancelactivity:
8284
helloworld:
8385
go build -o bin/helloworld cmd/samples/recipes/helloworld/*.go
8486

87+
blocks:
88+
go build -o bin/blocks cmd/samples/blocks/*.go
89+
8590
delaystart:
8691
go build -o bin/delaystart cmd/samples/recipes/delaystart/*.go
8792

@@ -188,6 +193,7 @@ autoscaling-monitoring:
188193
go build -o bin/autoscaling-monitoring cmd/samples/advanced/autoscaling-monitoring/*.go
189194

190195
bins: helloworld \
196+
blocks \
191197
versioning \
192198
delaystart \
193199
branch \
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
package main
2+
3+
import (
4+
"time"
5+
6+
"go.uber.org/cadence/workflow"
7+
"go.uber.org/zap"
8+
)
9+
10+
/**
11+
* This is the blocks workflow sample that demonstrates JSON blob queries.
12+
*/
13+
14+
// ApplicationName is the task list for this sample
15+
const ApplicationName = "blocksGroup"
16+
17+
const blocksWorkflowName = "blocksWorkflow"
18+
19+
// Query response structures
20+
type LunchQueryResponse struct {
21+
CadenceResponseType string `json:"cadenceResponseType"`
22+
Format string `json:"format"`
23+
Blocks []Block `json:"blocks"`
24+
}
25+
26+
type Block struct {
27+
Type string `json:"type"`
28+
Format string `json:"format,omitempty"`
29+
ComponentOptions *ComponentOptions `json:"componentOptions,omitempty"`
30+
Elements []Element `json:"elements,omitempty"`
31+
}
32+
33+
type Element struct {
34+
Type string `json:"type"`
35+
ComponentOptions *ComponentOptions `json:"componentOptions,omitempty"`
36+
Action *Action `json:"action,omitempty"`
37+
}
38+
39+
type ComponentOptions struct {
40+
Type string `json:"type,omitempty"`
41+
Text string `json:"text,omitempty"`
42+
}
43+
44+
type Action struct {
45+
Type string `json:"type"`
46+
SignalName string `json:"signal_name,omitempty"`
47+
SignalValue interface{} `json:"signal_value,omitempty"`
48+
WorkflowID string `json:"workflow_id,omitempty"`
49+
RunID string `json:"run_id,omitempty"`
50+
}
51+
52+
// This is an example of using the 'blocks' query response in a cadence query, in this example,
53+
// to select the lunch option.
54+
func blocksWorkflow(ctx workflow.Context, name string) error {
55+
ao := workflow.ActivityOptions{
56+
ScheduleToStartTimeout: time.Minute,
57+
StartToCloseTimeout: time.Minute,
58+
HeartbeatTimeout: time.Second * 20,
59+
}
60+
ctx = workflow.WithActivityOptions(ctx, ao)
61+
62+
logger := workflow.GetLogger(ctx)
63+
64+
votes := []map[string]string{}
65+
66+
workflow.SetQueryHandler(ctx, "options", func() (LunchQueryResponse, error) {
67+
logger := workflow.GetLogger(ctx)
68+
logger.Info("Responding to 'options' query")
69+
70+
return LunchQueryResponse{
71+
CadenceResponseType: "formattedData",
72+
Format: "blocks",
73+
Blocks: []Block{
74+
{
75+
Type: "section",
76+
Format: "text/markdown",
77+
ComponentOptions: &ComponentOptions{
78+
Text: "## Lunch options\nWe're voting on where to order lunch today. Select the option you want to vote for.",
79+
},
80+
},
81+
{
82+
Type: "divider",
83+
},
84+
{
85+
Type: "section",
86+
Format: "text/markdown",
87+
ComponentOptions: &ComponentOptions{
88+
Text: makeVoteTable(votes),
89+
},
90+
},
91+
{
92+
Type: "section",
93+
Format: "text/markdown",
94+
ComponentOptions: &ComponentOptions{
95+
Text: makeMenu(),
96+
},
97+
},
98+
{
99+
Type: "actions",
100+
Elements: []Element{
101+
{
102+
Type: "button",
103+
ComponentOptions: &ComponentOptions{
104+
Type: "plain_text",
105+
Text: "Farmhouse",
106+
},
107+
Action: &Action{
108+
Type: "signal",
109+
SignalName: "lunch_order",
110+
SignalValue: map[string]string{"location": "farmhouse - red thai curry", "requests": "spicy"},
111+
},
112+
},
113+
{
114+
Type: "button",
115+
ComponentOptions: &ComponentOptions{
116+
Type: "plain_text",
117+
Text: "Ethiopian",
118+
},
119+
Action: &Action{
120+
Type: "signal",
121+
SignalName: "no_lunch_order_walk_in_person",
122+
WorkflowID: "in-person-order-workflow",
123+
},
124+
},
125+
{
126+
Type: "button",
127+
ComponentOptions: &ComponentOptions{
128+
Type: "plain_text",
129+
Text: "Ler Ros",
130+
},
131+
Action: &Action{
132+
Type: "signal",
133+
SignalName: "lunch_order",
134+
SignalValue: map[string]string{"location": "Ler Ros", "meal": "tofo Bahn Mi"},
135+
},
136+
},
137+
},
138+
},
139+
},
140+
}, nil
141+
})
142+
143+
votesChan := workflow.GetSignalChannel(ctx, "lunch_order")
144+
workflow.Go(ctx, func(ctx workflow.Context) {
145+
for {
146+
var vote map[string]string
147+
votesChan.Receive(ctx, &vote)
148+
votes = append(votes, vote)
149+
}
150+
})
151+
defer func() {
152+
votesChan.Close()
153+
}()
154+
155+
err := workflow.Sleep(ctx, 30*time.Minute)
156+
if err != nil {
157+
logger.Error("Sleep failed", zap.Error(err))
158+
return err
159+
}
160+
161+
logger.Info("Workflow completed.", zap.Any("Result", votes))
162+
return nil
163+
}
164+
165+
func makeMenu() string {
166+
167+
options := []struct {
168+
image string
169+
desc string
170+
}{
171+
{
172+
image: "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e2/Red_roast_duck_curry.jpg/200px-Red_roast_duck_curry.jpg",
173+
desc: "Farmhouse - Red Thai Curry: (Thai: แกง, romanized: kaeng, pronounced [kɛ̄ːŋ]) is a dish in Thai cuisine made from curry paste, coconut milk or water, meat, seafood, vegetables or fruit, and herbs. Curries in Thailand mainly differ from the Indian subcontinent in their use of ingredients such as fresh rhizomes, herbs, and aromatic leaves rather than a mix of dried spices.",
174+
},
175+
{
176+
image: "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/B%C3%A1nh_m%C3%AC_th%E1%BB%8Bt_n%C6%B0%E1%BB%9Bng.png/200px-B%C3%A1nh_m%C3%AC_th%E1%BB%8Bt_n%C6%B0%E1%BB%9Bng.png",
177+
desc: "Ler Ros: Lemongrass Tofu Bahn Mi: In Vietnamese cuisine, bánh mì, bánh mỳ or banh mi is a sandwich consisting of a baguette filled with various ingredients, most commonly including a protein such as pâté, chicken, or pork, and vegetables such as lettuce, cilantro, and cucumber.",
178+
},
179+
{
180+
image: "https://upload.wikimedia.org/wikipedia/commons/thumb/5/54/Ethiopian_wat.jpg/960px-Ethiopian_wat.jpg",
181+
desc: "Ethiopian Wat: Wat is a traditional Ethiopian dish made from a mixture of spices, vegetables, and legumes. It is typically served with injera, a sourdough flatbread that is used to scoop up the food.",
182+
},
183+
}
184+
185+
table := "| Picture | Description |\n|---|----|\n"
186+
for _, option := range options {
187+
table += "| ![food](" + option.image + ") | " + option.desc + " |\n"
188+
}
189+
190+
table += "\n\n\n(source wikipedia)"
191+
192+
return table
193+
}
194+
195+
func makeVoteTable(votes []map[string]string) string {
196+
if len(votes) == 0 {
197+
return "| lunch order vote | meal | requests |\n|-------|-------|-------|\n| No votes yet |\n"
198+
}
199+
table := "| lunch order vote | meal | requests |\n|-------|-------|-------|\n"
200+
for _, vote := range votes {
201+
202+
loc := vote["location"]
203+
meal := vote["meal"]
204+
requests := vote["requests"]
205+
206+
table += "| " + loc + " | " + meal + " | " + requests + " |\n"
207+
}
208+
209+
return table
210+
}

cmd/samples/blocks/main.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"time"
6+
7+
"github.com/pborman/uuid"
8+
"go.uber.org/cadence/client"
9+
"go.uber.org/cadence/worker"
10+
11+
"github.com/uber-common/cadence-samples/cmd/samples/common"
12+
)
13+
14+
// This needs to be done as part of a bootstrap step when the process starts.
15+
// The workers are supposed to be long running.
16+
func startWorkers(h *common.SampleHelper) {
17+
// Configure worker options.
18+
workerOptions := worker.Options{
19+
MetricsScope: h.WorkerMetricScope,
20+
Logger: h.Logger,
21+
FeatureFlags: client.FeatureFlags{
22+
WorkflowExecutionAlreadyCompletedErrorEnabled: true,
23+
},
24+
}
25+
h.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)
26+
}
27+
28+
func startWorkflow(h *common.SampleHelper) {
29+
workflowOptions := client.StartWorkflowOptions{
30+
ID: "lunch_order_" + uuid.New(),
31+
TaskList: ApplicationName,
32+
ExecutionStartToCloseTimeout: 10*time.Minute,
33+
DecisionTaskStartToCloseTimeout: time.Minute,
34+
}
35+
h.StartWorkflow(workflowOptions, blocksWorkflowName, "Cadence")
36+
}
37+
38+
func registerWorkflowAndActivity(
39+
h *common.SampleHelper,
40+
) {
41+
h.RegisterWorkflowWithAlias(blocksWorkflow, blocksWorkflowName)
42+
}
43+
44+
func main() {
45+
var mode string
46+
flag.StringVar(&mode, "m", "trigger", "Mode is worker, trigger.")
47+
flag.Parse()
48+
49+
var h common.SampleHelper
50+
h.SetupServiceConfig()
51+
52+
switch mode {
53+
case "worker":
54+
registerWorkflowAndActivity(&h)
55+
startWorkers(&h)
56+
57+
// The workers are supposed to be long running process that should not exit.
58+
// Use select{} to block indefinitely for samples, you can quit by CMD+C.
59+
select {}
60+
case "trigger":
61+
startWorkflow(&h)
62+
}
63+
}

0 commit comments

Comments
 (0)