Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/unreleased/Added-20251117-095639.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Added
body: Add typed_search_attributes workflow option
time: 2025-11-17T09:56:39.580517-07:00
custom:
Author: cludden
PullRequest: "131"
8 changes: 4 additions & 4 deletions buf.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ deps:
commit: 76b304caf5984a3a978e9565ea71a2ac
digest: b5:72c441888d19ff57f824b75f59aba0346a16bfa3ef5da83a5da21c614f0763764e1d37511312dc82f54e1dae5937a5829e8c2c1b7f806c983046bbf624b04ac2
- name: buf.build/cludden/protoc-gen-go-temporal
commit: cb7ac4e77f4e475aa01c09ea6863b8bd
digest: b5:cc480cc2d12ad1f4a5a7cfee696f61aadca27496b36790541f63f1a5e12d9178ae03bb79fd615322951425d298fce2f69ea0d7971270cf4fdb519df7bdfc6daa
commit: 706631d1caec4f1ab4f4421f5d9dc0cb
digest: b5:2b39f97ce9904f30deb02a9105c11ff9a0b9ce119aef8a5a3d2d1138f3c3c5202f7917ab78dcc6bae10f17875399e07b408efd105084438c5f235c333b268bbd
- name: buf.build/envoyproxy/protoc-gen-validate
commit: daf171c6cdb54629b5f51e345a79e4dd
digest: b5:c745e1521879f43740230b1df673d0729f55704efefdcfc489d4a0a2d40c92a26cacfeab62813403040a8b180142d53b398c7ca784a065e43823605ee49681de
- name: buf.build/googleapis/googleapis
commit: 28151c0d0a1641bf938a7672c500e01d
digest: b5:93b70089baa4fc05a92d3e52db91a4b7812db3b57b9664f6cb301733938cb630e377a938e8a56779388171c749c1d42a2e9a6c6230f2ff45f127a8102a6a27d0
- name: buf.build/temporalio/api
commit: b3c9abfdf37c449593a3c0d2774a3618
digest: b5:c123e858dba36871e9e604ff5fe6e3e1a8188238929fda76dc5d4297630c1a3bea35b4dd9ef27c665c4c77d34b327de6971dbe9ef57845a6727166cc61494167
commit: 145e888ec59442288b19a82a03d6397e
digest: b5:0c0f925b43db1f56b0b5f9a480cd0d7b8f318d7a0369c6b310002bed1d5b4ea922b542d6cf43027810e36b98e4ef3685df8ed8f91cc3d11fcfa2c16bfb8b00d9
23 changes: 23 additions & 0 deletions docs/docs/configuration/workflow.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,29 @@ service Example {
}
```

### typed_search_attributes

`string`

Specifies the default [Workflow typed search attributes](https://docs.temporal.io/visibility#search-attribute) as a [Bloblang mapping](https://www.benthos.dev/docs/guides/bloblang/about/) that returns a map of search attribute types to a map of search attribute keys to values. See the [Search Attributes example](/docs/examples/searchattributes/) for more details.

```protobuf
service Example {
rpc Hello(HelloInput) returns (HelloOutput) {
option (temporal.v1.workflow) = {
typed_search_attributes:
'keyword.CustomKeywordField = customKeywordField \n'
'string.CustomTextField = customTextField \n'
'int64.CustomIntField = customIntField.int64() \n'
'float64.CustomDoubleField = customDoubleField \n'
'bool.CustomBoolField = customBoolField \n'
'time.CustomDatetimeField = customDatetimeField.ts_parse("2006-01-02T15:04:05Z") \n'
'keyword_list.CustomKeywordListField = customKeywordListField \n'
};
}
}
```

### update

[temporal.v1.WorkflowOptions.Update](https://buf.build/cludden/protoc-gen-go-temporal/docs/main:temporal.v1#temporal.v1.WorkflowOptions.Update)
Expand Down
83 changes: 70 additions & 13 deletions examples/searchattributes/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,49 @@ import (
"go.temporal.io/sdk/client"
"go.temporal.io/sdk/converter"
tlog "go.temporal.io/sdk/log"
"go.temporal.io/sdk/temporal"
"go.temporal.io/sdk/worker"
"go.temporal.io/sdk/workflow"
"google.golang.org/protobuf/types/known/timestamppb"
)

var (
customKeywordField = temporal.NewSearchAttributeKeyKeyword("CustomKeywordField")
customKeywordListField = temporal.NewSearchAttributeKeyKeywordList("CustomKeywordListField")
customTextField = temporal.NewSearchAttributeKeyString("CustomTextField")
customIntField = temporal.NewSearchAttributeKeyInt64("CustomIntField")
customDoubleField = temporal.NewSearchAttributeKeyFloat64("CustomDoubleField")
customBoolField = temporal.NewSearchAttributeKeyBool("CustomBoolField")
customDatetimeField = temporal.NewSearchAttributeKeyTime("CustomDatetimeField")
)

func main() {
app, err := searchattributesv1.NewExampleCli(
searchattributesv1.NewExampleCliOptions().WithWorker(func(cmd *cli.Context, c client.Client) (worker.Worker, error) {
w := worker.New(c, searchattributesv1.ExampleTaskQueue, worker.Options{})
searchattributesv1.RegisterExampleWorkflows(w, &Workflows{})
return w, nil
}),
)
if err != nil {
log.Fatal(err)
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}

type Workflows struct{}

type SearchAttributesWorkflow struct {
*searchattributesv1.SearchAttributesWorkflowInput
log tlog.Logger
}

func NewSearchAttributesWorkflow(ctx workflow.Context, input *searchattributesv1.SearchAttributesWorkflowInput) (searchattributesv1.SearchAttributesWorkflow, error) {
func (w *Workflows) SearchAttributes(
ctx workflow.Context,
input *searchattributesv1.SearchAttributesWorkflowInput,
) (searchattributesv1.SearchAttributesWorkflow, error) {
return &SearchAttributesWorkflow{input, workflow.GetLogger(ctx)}, nil
}

Expand Down Expand Up @@ -60,18 +93,42 @@ func (w *SearchAttributesWorkflow) Execute(ctx workflow.Context) (err error) {
return nil
}

func main() {
app, err := searchattributesv1.NewExampleCli(
searchattributesv1.NewExampleCliOptions().WithWorker(func(cmd *cli.Context, c client.Client) (worker.Worker, error) {
w := worker.New(c, searchattributesv1.ExampleTaskQueue, worker.Options{})
searchattributesv1.RegisterSearchAttributesWorkflow(w, NewSearchAttributesWorkflow)
return w, nil
}),
)
if err != nil {
log.Fatal(err)
type TypedSearchAttributesWorkflow struct {
*searchattributesv1.TypedSearchAttributesWorkflowInput
log tlog.Logger
}

func (w *Workflows) TypedSearchAttributes(
ctx workflow.Context, input *searchattributesv1.TypedSearchAttributesWorkflowInput,
) (searchattributesv1.TypedSearchAttributesWorkflow, error) {
return &TypedSearchAttributesWorkflow{input, workflow.GetLogger(ctx)}, nil
}

func (w *TypedSearchAttributesWorkflow) Execute(
ctx workflow.Context,
) (*searchattributesv1.TypedSearchAttributesOutput, error) {
out := &searchattributesv1.TypedSearchAttributesOutput{}
sa := workflow.GetTypedSearchAttributes(ctx)
if v, ok := sa.GetKeyword(customKeywordField); ok {
out.CustomKeywordField = v
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
if v, ok := sa.GetFloat64(customDoubleField); ok {
out.CustomDoubleField = v
}
if v, ok := sa.GetBool(customBoolField); ok {
out.CustomBoolField = v
}
if v, ok := sa.GetTime(customDatetimeField); ok {
out.CustomDatetimeField = timestamppb.New(v)
}
if v, ok := sa.GetKeywordList(customKeywordListField); ok {
out.CustomKeywordListField = v
}
if v, ok := sa.GetString(customTextField); ok {
out.CustomTextField = v
}
if v, ok := sa.GetInt64(customIntField); ok {
out.CustomIntField = v
}
return out, nil
}
66 changes: 66 additions & 0 deletions examples/searchattributes/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package main

import (
"context"
"testing"
"time"

searchattributesv1 "github.com/cludden/protoc-gen-go-temporal/gen/example/searchattributes/v1"
"github.com/hairyhenderson/go-which"
"github.com/stretchr/testify/require"
"go.temporal.io/api/enums/v1"
"go.temporal.io/api/operatorservice/v1"
"go.temporal.io/sdk/testsuite"
"go.temporal.io/sdk/worker"
"google.golang.org/protobuf/types/known/timestamppb"
)

func TestTypedSearchAttributes(t *testing.T) {
existingPath := which.Which("temporal")
if existingPath == "" {
t.Skip("temporal binary not found in PATH, skipping test")
}
srv, err := testsuite.StartDevServer(context.Background(), testsuite.DevServerOptions{})
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, srv.Stop()) })

c := srv.Client()
_, err = c.OperatorService().AddSearchAttributes(context.Background(), &operatorservice.AddSearchAttributesRequest{
Namespace: "default",
SearchAttributes: map[string]enums.IndexedValueType{
"CustomKeywordField": enums.INDEXED_VALUE_TYPE_KEYWORD,
"CustomKeywordListField": enums.INDEXED_VALUE_TYPE_KEYWORD_LIST,
"CustomTextField": enums.INDEXED_VALUE_TYPE_TEXT,
"CustomIntField": enums.INDEXED_VALUE_TYPE_INT,
"CustomDoubleField": enums.INDEXED_VALUE_TYPE_DOUBLE,
"CustomBoolField": enums.INDEXED_VALUE_TYPE_BOOL,
"CustomDatetimeField": enums.INDEXED_VALUE_TYPE_DATETIME,
},
})
require.NoError(t, err)

client := searchattributesv1.NewExampleClient(c)
w := worker.New(c, searchattributesv1.ExampleTaskQueue, worker.Options{})
searchattributesv1.RegisterExampleWorkflows(w, &Workflows{})
require.NoError(t, w.Start())
t.Cleanup(w.Stop)

now := time.Now().UTC()
out, err := client.TypedSearchAttributes(context.Background(), &searchattributesv1.TypedSearchAttributesInput{
CustomKeywordField: "test",
CustomTextField: "test",
CustomIntField: 123,
CustomDoubleField: 123.456,
CustomBoolField: true,
CustomDatetimeField: timestamppb.New(now),
CustomKeywordListField: []string{"test"},
})
require.NoError(t, err)
require.Equal(t, "test", out.CustomKeywordField)
require.Equal(t, "test", out.CustomTextField)
require.Equal(t, int64(123), out.CustomIntField)
require.Equal(t, 123.456, out.CustomDoubleField)
require.Equal(t, true, out.CustomBoolField)
require.Equal(t, now, out.CustomDatetimeField.AsTime())
require.Equal(t, []string{"test"}, out.CustomKeywordListField)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ service Example {
'CustomDatetimeField = customDatetimeField.ts_parse("2006-01-02T15:04:05Z") \n'
};
}

rpc TypedSearchAttributes(TypedSearchAttributesInput) returns (TypedSearchAttributesOutput) {
option (temporal.v1.workflow) = {
id: 'searchattributes.v1.TypedSearchAttributes/${! uuid_v4() }'
typed_search_attributes:
'keyword.CustomKeywordField = customKeywordField \n'
'string.CustomTextField = customTextField \n'
'int64.CustomIntField = customIntField.int64() \n'
'float64.CustomDoubleField = customDoubleField \n'
'bool.CustomBoolField = customBoolField \n'
'time.CustomDatetimeField = customDatetimeField.ts_parse("2006-01-02T15:04:05Z") \n'
'keyword_list.CustomKeywordListField = customKeywordListField \n'
};
}
}

message SearchAttributesInput {
Expand All @@ -31,3 +45,23 @@ message SearchAttributesInput {
bool custom_bool_field = 5;
google.protobuf.Timestamp custom_datetime_field = 6;
}

message TypedSearchAttributesInput {
string custom_keyword_field = 1;
string custom_text_field = 2;
int64 custom_int_field = 3;
double custom_double_field = 4;
bool custom_bool_field = 5;
google.protobuf.Timestamp custom_datetime_field = 6;
repeated string custom_keyword_list_field = 7;
}

message TypedSearchAttributesOutput {
string custom_keyword_field = 1;
string custom_text_field = 2;
int64 custom_int_field = 3;
double custom_double_field = 4;
bool custom_bool_field = 5;
google.protobuf.Timestamp custom_datetime_field = 6;
repeated string custom_keyword_list_field = 7;
}
20 changes: 20 additions & 0 deletions gen/example/helloworld/v1/example_temporal.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading