Skip to content

Commit 640d317

Browse files
committed
feat: adds experimental codec server support
1 parent 3a8f11b commit 640d317

File tree

14 files changed

+1081
-248
lines changed

14 files changed

+1081
-248
lines changed

README.md

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ A protoc plugin for generating typed Temporal clients and workers in Go from pro
1818
- [CLI](#cli)
1919
- [Test Client](#test-client)
2020
- [Cross-Namespace (XNS)](#cross-namespace-xns)
21+
- [Codec](#codec)
2122
- [Documentation](#documentation)
2223
- [License](#license)
2324

@@ -36,7 +37,6 @@ Generated **Client** with:
3637
- default `client.StartWorkflowOptions` and `client.UpdateWorkflowWithOptionsRequest`
3738
- dynamic workflow ids, update ids, and search attributes via [Bloblang expressions](#bloblang-expressions)
3839
- default timeouts, id reuse policies, retry policies, wait policies
39-
- experimental [cross-namespace (xns)](#cross-namespace-xns) support
4040

4141

4242
Generated **Worker** resources with:
@@ -54,6 +54,12 @@ Optional **CLI** with:
5454
- typed flags for conventiently specifying workflow, query, and signal inputs
5555

5656

57+
Generated [Cross-Namespace (XNS)](#cross-namespace-xns) helpers: **[Experimental]**
58+
- with support for invoking a service's workflows, queries, signals, and updates from workflows in a different temporal namespace
59+
60+
Generated [Remote Codec Server](#codec) helpers **[Experimental]**
61+
62+
5763
## Getting Started
5864
1. Install [buf](https://docs.buf.build/installation)
5965

@@ -472,6 +478,7 @@ via:
472478
| cli-categories | `bool` | enables cli categories | `true` |
473479
| cli-enabled | `bool` | enables cli generation | `false` |
474480
| disable-workflow-input-rename | `bool` | disables renamed workflow input suffix | `false` |
481+
| enable-codec | `bool` | enables [experimental codec-server support](#codec) | `false` |
475482
| enable-patch-support | `bool` | enables experimental support for [protoc-gen-go-patch](https://github.com/alta/protopatch) | `false` |
476483
| enable-xns | `bool` | enables [experimental cross-namespace support](#cross-namespace-xns) | `false` |
477484
| workflow-update-enabled | `bool` | enables experimental workflow update | `false` |
@@ -648,6 +655,98 @@ The generated code includes resources that are compatible with the Temporal Go S
648655
649656
This plugin provides experimental support for cross-namespace and/or cross-cluster integration by enabling the `enable-xns` plugin option. When enabled, the plugin will generate an additional `path/to/generated/code/<package>xns` go package containing types, methods, and helpers for calling workflows, queries, signals, and updates from other Temporal workflows via activities. The activities use [heartbeating](https://docs.temporal.io/activities#activity-heartbeat) to maintain liveness for long-running workflows or updates, and their associated timeouts can be configured using the generated options helpers. For an example of xns integration, see the [example/external](./example/external/external.go) package.
650657
658+
## Codec
659+
660+
*__Experimental__*
661+
662+
Temporal's [default data converter](https://pkg.go.dev/go.temporal.io/sdk/converter#GetDefaultDataConverter) will serialize protobuf types using the `json/protobuf` encoding provided by the [ProtoJSONPayloadConverter](https://pkg.go.dev/go.temporal.io/sdk/converter#ProtoJSONPayloadConverter), which allows the Temporal UI to automatically decode the underlying payload and render it as JSON. If you'd prefer to take advantage of protobuf's binary format for smaller payloads, you can provide an alternative data converter to the Temporal client at initialization that prioritizes the [ProtoPayloadConverter](https://pkg.go.dev/go.temporal.io/sdk/converter#ProtoPayloadConverter) ahead of the `ProtoJSONPayloadConverter`. See below for an example.
663+
664+
If you choose to use `binary/protobuf` encoding, you'll lose the ability to view decoded payloads in the Temporal UI unless you configure the [Remote Codec Server](https://docs.temporal.io/dataconversion#codec-server) integration. This plugin can generate helpers that simplify the process of implementing a remote codec server for use with the Temporal UI to support conversion between `binary/protobuf` and `json/protobuf` or `json/plain` payload encodings. See below for a simple example. For a more advanced example that supports different codecs per namespace, cors, and authentication, see the [codec-server](https://github.com/temporalio/samples-go/blob/main/codec-server/codec-server/main.go) go sample.
665+
666+
**Example:** *custom data converter that uses `binary/protobuf` for encoding, but supports all of `binary/protobuf`, `json/protobuf`, and `json/plain` for decoding.*
667+
668+
```go
669+
package main
670+
671+
import (
672+
"go.temporal.io/sdk/client"
673+
"go.temporal.io/sdk/converter"
674+
)
675+
func main() {
676+
client, _ := client.Dial(client.Options{
677+
DataConverter: converter.NewCompositeDataConverter(
678+
// Order is important here, as the first match (ProtoPayload in this case) will always
679+
// be used for serialization, and both ProtoJsonPayload and ProtoPayload converters
680+
// check for the same proto.Message interface.
681+
// Deserialization is controlled by metadata, therefore both converters can deserialize
682+
// corresponding data format (JSON or binary proto).
683+
converter.NewNilPayloadConverter(),
684+
converter.NewByteSlicePayloadConverter(),
685+
converter.NewProtoPayloadConverter(),
686+
converter.NewProtoJSONPayloadConverterWithOptions(converter.ProtoJSONPayloadConverterOptions{
687+
AllowUnknownFields: true, // Prevent errors when the underlying protobuf payload has added fields
688+
}),
689+
converter.NewJSONPayloadConverter(),
690+
),
691+
})
692+
}
693+
```
694+
695+
**Example:** *basic Remote Codec Server implementation*
696+
697+
```go
698+
package main
699+
700+
import (
701+
"context"
702+
"errors"
703+
"log"
704+
"net/http"
705+
"os"
706+
"os/signal"
707+
"syscall"
708+
709+
examplev1 "github.com/cludden/protoc-gen-go-temporal/gen/example/v1"
710+
"github.com/cludden/protoc-gen-go-temporal/pkg/codec"
711+
"github.com/cludden/protoc-gen-go-temporal/pkg/scheme"
712+
"go.temporal.io/sdk/converter"
713+
)
714+
715+
func main() {
716+
// initialize codec handler using this plugin's `pkg/codec` and `pkg/scheme` packages
717+
// along with the generated scheme helpers
718+
handler := converter.NewPayloadCodecHTTPHandler(
719+
codec.NewProtoJSONCodec(
720+
scheme.New(
721+
examplev1.WithExampleSchemeTypes(),
722+
),
723+
),
724+
)
725+
726+
// initialize http server with codec handler
727+
srv := &http.Server{
728+
Addr: "0.0.0.0:8080",
729+
Handler: handler,
730+
}
731+
732+
// handle graceful shutdown
733+
go func() {
734+
shutdownCh := make(chan os.Signal, 1)
735+
signal.Notify(shutdownCh, syscall.SIGINT, syscall.SIGTERM)
736+
<-shutdownCh
737+
738+
if err := srv.Shutdown(context.Background()); err != nil {
739+
log.Fatalf("error shutting down server: %v", err)
740+
}
741+
}()
742+
743+
// start remote codec server
744+
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
745+
log.Fatalf("server error: %v", err)
746+
}
747+
}
748+
```
749+
651750
## Documentation
652751
653752
- [Generated code reference](./docs/generated.md)

buf.gen.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ plugins:
99
opt: plugin=go,paths=source_relative
1010
- plugin: go_temporal
1111
out: gen
12-
opt: paths=source_relative,cli-enabled=true,cli-categories=true,workflow-update-enabled=true,enable-patch-support=true,enable-xns=true
12+
opt: paths=source_relative,cli-enabled=true,cli-categories=true,workflow-update-enabled=true,enable-patch-support=true,enable-xns=true,enable-codec=true
1313
strategy: all
1414
- plugin: doc
1515
out: docs/api

example/main.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,23 @@ package main
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"log"
8+
"net/http"
79
"os"
10+
"os/signal"
11+
"syscall"
812

913
"github.com/cludden/protoc-gen-go-temporal/example/external"
1014
examplev1 "github.com/cludden/protoc-gen-go-temporal/gen/example/v1"
1115
"github.com/cludden/protoc-gen-go-temporal/gen/example/v1/examplev1xns"
16+
"github.com/cludden/protoc-gen-go-temporal/pkg/codec"
17+
"github.com/cludden/protoc-gen-go-temporal/pkg/scheme"
1218
"github.com/urfave/cli/v2"
1319
"go.temporal.io/sdk/activity"
1420
"go.temporal.io/sdk/client"
21+
"go.temporal.io/sdk/converter"
1522
"go.temporal.io/sdk/worker"
1623
"go.temporal.io/sdk/workflow"
1724
logger "go.temporal.io/server/common/log"
@@ -157,6 +164,41 @@ func main() {
157164
}
158165
app.Commands = append(app.Commands, external)
159166

167+
app.Commands = append(app.Commands, &cli.Command{
168+
Name: "codec",
169+
Usage: "run remote codec server",
170+
Action: func(cmd *cli.Context) error {
171+
handler := converter.NewPayloadCodecHTTPHandler(
172+
codec.NewProtoJSONCodec(
173+
scheme.New(
174+
examplev1.WithExampleSchemeTypes(),
175+
examplev1.WithExternalSchemeTypes(),
176+
),
177+
),
178+
)
179+
180+
srv := &http.Server{
181+
Addr: "0.0.0.0:8080",
182+
Handler: handler,
183+
}
184+
185+
go func() {
186+
sigChan := make(chan os.Signal, 1)
187+
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
188+
<-sigChan
189+
190+
if err := srv.Shutdown(context.Background()); err != nil {
191+
log.Fatalf("error shutting down server: %v", err)
192+
}
193+
}()
194+
195+
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
196+
log.Fatalf("server error: %v", err)
197+
}
198+
return nil
199+
},
200+
})
201+
160202
// run cli
161203
if err := app.Run(os.Args); err != nil {
162204
log.Fatal(err)

gen/example/v1/example_temporal.pb.go

Lines changed: 21 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/example/v1/examplev1xns/example_xns_temporal.pb.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)