Skip to content

Commit 09ba7f3

Browse files
feat: Adds example use of blocks workflow for interactive / human-in-the-loop workflows (#108)
* Adds blocks example * Adds block example Co-authored-by: Vishwa Patil <[email protected]>
1 parent d7ee6d1 commit 09ba7f3

File tree

7 files changed

+398
-3
lines changed

7 files changed

+398
-3
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 \
@@ -53,6 +54,7 @@ TEST_DIRS=./cmd/samples/cron \
5354
./cmd/samples/dsl \
5455
./cmd/samples/expense \
5556
./cmd/samples/fileprocessing \
57+
./cmd/samples/blocks \
5658
./cmd/samples/recipes/branch \
5759
./cmd/samples/recipes/choice \
5860
./cmd/samples/recipes/greetings \
@@ -83,6 +85,9 @@ cancelactivity:
8385
helloworld:
8486
go build -o bin/helloworld cmd/samples/recipes/helloworld/*.go
8587

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

@@ -204,6 +209,7 @@ clean:
204209
rm -Rf $(BUILD)
205210

206211
bins: helloworld \
212+
blocks \
207213
versioning \
208214
delaystart \
209215
branch \

cmd/samples/blocks/blocks

32.4 MB
Binary file not shown.
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package main
2+
3+
import (
4+
"time"
5+
6+
"go.uber.org/cadence/workflow"
7+
"go.uber.org/cadence/x/blocks"
8+
"go.uber.org/zap"
9+
)
10+
11+
/**
12+
* This is the blocks workflow sample that demonstrates JSON blob queries.
13+
*/
14+
15+
// ApplicationName is the task list for this sample
16+
const ApplicationName = "blocksGroup"
17+
18+
const blocksWorkflowName = "blocksWorkflow"
19+
20+
// This is an example of using the 'blocks' query response in a cadence query, in this example,
21+
// to select the lunch option.
22+
func blocksWorkflow(ctx workflow.Context, name string) error {
23+
ao := workflow.ActivityOptions{
24+
ScheduleToStartTimeout: time.Minute,
25+
StartToCloseTimeout: time.Minute,
26+
HeartbeatTimeout: time.Second * 20,
27+
}
28+
ctx = workflow.WithActivityOptions(ctx, ao)
29+
30+
logger := workflow.GetLogger(ctx)
31+
32+
votes := []map[string]string{}
33+
34+
workflow.SetQueryHandler(ctx, "options", func() (blocks.QueryResponse, error) {
35+
logger := workflow.GetLogger(ctx)
36+
logger.Info("Responding to 'options' query")
37+
38+
return makeResponse(votes), nil
39+
})
40+
41+
votesChan := workflow.GetSignalChannel(ctx, "lunch_order")
42+
workflow.Go(ctx, func(ctx workflow.Context) {
43+
for {
44+
var vote map[string]string
45+
votesChan.Receive(ctx, &vote)
46+
votes = append(votes, vote)
47+
}
48+
})
49+
defer func() {
50+
votesChan.Close()
51+
}()
52+
53+
err := workflow.Sleep(ctx, 30*time.Minute)
54+
if err != nil {
55+
logger.Error("Sleep failed", zap.Error(err))
56+
return err
57+
}
58+
59+
logger.Info("Workflow completed.", zap.Any("Result", votes))
60+
return nil
61+
}
62+
63+
// makeResponse creates the lunch query response payload based on the current votes
64+
func makeResponse(votes []map[string]string) blocks.QueryResponse {
65+
return blocks.New(
66+
blocks.NewMarkdownSection("## Lunch options\nWe're voting on where to order lunch today. Select the option you want to vote for."),
67+
blocks.NewDivider(),
68+
blocks.NewMarkdownSection(makeVoteTable(votes)),
69+
blocks.NewMarkdownSection(makeMenu()),
70+
blocks.NewSignalActions(
71+
blocks.NewSignalButton("Farmhouse", "lunch_order", map[string]string{"location": "farmhouse - red thai curry", "requests": "spicy"}),
72+
blocks.NewSignalButtonWithExternalWorkflow("Ethiopian", "no_lunch_order_walk_in_person", nil, "in-person-order-workflow", ""),
73+
blocks.NewSignalButton("Ler Ros", "lunch_order", map[string]string{"location": "Ler Ros", "meal": "tofo Bahn Mi"}),
74+
),
75+
)
76+
}
77+
78+
func makeMenu() string {
79+
80+
options := []struct {
81+
image string
82+
desc string
83+
}{
84+
{
85+
image: "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e2/Red_roast_duck_curry.jpg/200px-Red_roast_duck_curry.jpg",
86+
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.",
87+
},
88+
{
89+
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",
90+
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.",
91+
},
92+
{
93+
image: "https://upload.wikimedia.org/wikipedia/commons/thumb/5/54/Ethiopian_wat.jpg/960px-Ethiopian_wat.jpg",
94+
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.",
95+
},
96+
}
97+
98+
table := "| Picture | Description |\n|---|----|\n"
99+
for _, option := range options {
100+
table += "| ![food](" + option.image + ") | " + option.desc + " |\n"
101+
}
102+
103+
table += "\n\n\n(source wikipedia)"
104+
105+
return table
106+
}
107+
108+
func makeVoteTable(votes []map[string]string) string {
109+
if len(votes) == 0 {
110+
return "| lunch order vote | meal | requests |\n|-------|-------|-------|\n| No votes yet |\n"
111+
}
112+
table := "| lunch order vote | meal | requests |\n|-------|-------|-------|\n"
113+
for _, vote := range votes {
114+
115+
loc := vote["location"]
116+
meal := vote["meal"]
117+
requests := vote["requests"]
118+
119+
table += "| " + loc + " | " + meal + " | " + requests + " |\n"
120+
}
121+
122+
return table
123+
}
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestMakeResponse(t *testing.T) {
11+
// Test with empty votes
12+
t.Run("EmptyVotes", func(t *testing.T) {
13+
votes := []map[string]string{}
14+
result := makeResponse(votes)
15+
16+
// Verify the structure
17+
assert.Equal(t, "formattedData", result.CadenceResponseType)
18+
assert.Equal(t, "blocks", result.Format)
19+
assert.Len(t, result.Blocks, 5)
20+
21+
// Verify JSON serialization matches expected output
22+
resultJSON, err := json.Marshal(result)
23+
assert.NoError(t, err)
24+
25+
expectedJSON := `{
26+
"cadenceResponseType": "formattedData",
27+
"format": "blocks",
28+
"blocks": [
29+
{
30+
"type": "section",
31+
"format": "text/markdown",
32+
"componentOptions": {
33+
"text": "## Lunch options\nWe're voting on where to order lunch today. Select the option you want to vote for."
34+
}
35+
},
36+
{
37+
"type": "divider"
38+
},
39+
{
40+
"type": "section",
41+
"format": "text/markdown",
42+
"componentOptions": {
43+
"text": "| lunch order vote | meal | requests |\n|-------|-------|-------|\n| No votes yet |\n"
44+
}
45+
},
46+
{
47+
"type": "section",
48+
"format": "text/markdown",
49+
"componentOptions": {
50+
"text": "| Picture | Description |\n|---|----|\n| ![food](https://upload.wikimedia.org/wikipedia/commons/thumb/e/e2/Red_roast_duck_curry.jpg/200px-Red_roast_duck_curry.jpg) | 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. |\n| ![food](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) | 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. |\n| ![food](https://upload.wikimedia.org/wikipedia/commons/thumb/5/54/Ethiopian_wat.jpg/960px-Ethiopian_wat.jpg) | 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. |\n\n\n\n(source wikipedia)"
51+
}
52+
},
53+
{
54+
"type": "actions",
55+
"elements": [
56+
{
57+
"type": "button",
58+
"componentOptions": {
59+
"type": "plain_text",
60+
"text": "Farmhouse"
61+
},
62+
"action": {
63+
"type": "signal",
64+
"signal_name": "lunch_order",
65+
"signal_value": {
66+
"location": "farmhouse - red thai curry",
67+
"requests": "spicy"
68+
}
69+
}
70+
},
71+
{
72+
"type": "button",
73+
"componentOptions": {
74+
"type": "plain_text",
75+
"text": "Ethiopian"
76+
},
77+
"action": {
78+
"type": "signal",
79+
"signal_name": "no_lunch_order_walk_in_person",
80+
"workflow_id": "in-person-order-workflow"
81+
}
82+
},
83+
{
84+
"type": "button",
85+
"componentOptions": {
86+
"type": "plain_text",
87+
"text": "Ler Ros"
88+
},
89+
"action": {
90+
"type": "signal",
91+
"signal_name": "lunch_order",
92+
"signal_value": {
93+
"location": "Ler Ros",
94+
"meal": "tofo Bahn Mi"
95+
}
96+
}
97+
}
98+
]
99+
}
100+
]
101+
}`
102+
103+
assert.JSONEq(t, expectedJSON, string(resultJSON))
104+
})
105+
106+
// Test with some votes
107+
t.Run("WithVotes", func(t *testing.T) {
108+
votes := []map[string]string{
109+
{"location": "farmhouse - red thai curry", "requests": "spicy"},
110+
{"location": "Ler Ros", "meal": "tofo Bahn Mi"},
111+
}
112+
result := makeResponse(votes)
113+
114+
// Verify the structure
115+
assert.Equal(t, "formattedData", result.CadenceResponseType)
116+
assert.Equal(t, "blocks", result.Format)
117+
assert.Len(t, result.Blocks, 5)
118+
119+
// Verify JSON serialization matches expected output
120+
resultJSON, err := json.Marshal(result)
121+
assert.NoError(t, err)
122+
123+
expectedJSON := `{
124+
"cadenceResponseType": "formattedData",
125+
"format": "blocks",
126+
"blocks": [
127+
{
128+
"type": "section",
129+
"format": "text/markdown",
130+
"componentOptions": {
131+
"text": "## Lunch options\nWe're voting on where to order lunch today. Select the option you want to vote for."
132+
}
133+
},
134+
{
135+
"type": "divider"
136+
},
137+
{
138+
"type": "section",
139+
"format": "text/markdown",
140+
"componentOptions": {
141+
"text": "| lunch order vote | meal | requests |\n|-------|-------|-------|\n| farmhouse - red thai curry | | spicy |\n| Ler Ros | tofo Bahn Mi | |\n"
142+
}
143+
},
144+
{
145+
"type": "section",
146+
"format": "text/markdown",
147+
"componentOptions": {
148+
"text": "| Picture | Description |\n|---|----|\n| ![food](https://upload.wikimedia.org/wikipedia/commons/thumb/e/e2/Red_roast_duck_curry.jpg/200px-Red_roast_duck_curry.jpg) | 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. |\n| ![food](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) | 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. |\n| ![food](https://upload.wikimedia.org/wikipedia/commons/thumb/5/54/Ethiopian_wat.jpg/960px-Ethiopian_wat.jpg) | 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. |\n\n\n\n(source wikipedia)"
149+
}
150+
},
151+
{
152+
"type": "actions",
153+
"elements": [
154+
{
155+
"type": "button",
156+
"componentOptions": {
157+
"type": "plain_text",
158+
"text": "Farmhouse"
159+
},
160+
"action": {
161+
"type": "signal",
162+
"signal_name": "lunch_order",
163+
"signal_value": {
164+
"location": "farmhouse - red thai curry",
165+
"requests": "spicy"
166+
}
167+
}
168+
},
169+
{
170+
"type": "button",
171+
"componentOptions": {
172+
"type": "plain_text",
173+
"text": "Ethiopian"
174+
},
175+
"action": {
176+
"type": "signal",
177+
"signal_name": "no_lunch_order_walk_in_person",
178+
"workflow_id": "in-person-order-workflow"
179+
}
180+
},
181+
{
182+
"type": "button",
183+
"componentOptions": {
184+
"type": "plain_text",
185+
"text": "Ler Ros"
186+
},
187+
"action": {
188+
"type": "signal",
189+
"signal_name": "lunch_order",
190+
"signal_value": {
191+
"location": "Ler Ros",
192+
"meal": "tofo Bahn Mi"
193+
}
194+
}
195+
}
196+
]
197+
}
198+
]
199+
}`
200+
201+
assert.JSONEq(t, expectedJSON, string(resultJSON))
202+
})
203+
}

0 commit comments

Comments
 (0)