Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify restate.Service and restate.Object into restate.Reflect #27

Merged
merged 1 commit into from
Aug 14, 2024
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
8 changes: 4 additions & 4 deletions examples/codegen/proto/helloworld_restate.pb.go

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

6 changes: 3 additions & 3 deletions examples/ticketreservation/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import (
func main() {
server := server.NewRestate().
// Handlers can be inferred from object methods
Bind(restate.Object(&userSession{})).
Bind(restate.Object(&ticketService{})).
Bind(restate.Service(&checkout{}))
Bind(restate.Reflect(&userSession{})).
Bind(restate.Reflect(&ticketService{})).
Bind(restate.Reflect(&checkout{}))

if err := server.Start(context.Background(), ":9080"); err != nil {
slog.Error("application exited unexpectedly", "err", err.Error())
Expand Down
34 changes: 14 additions & 20 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,18 @@ type Void = encoding.Void
// ObjectHandler is the required set of methods for a Virtual Object handler.
type ObjectHandler interface {
Call(ctx ObjectContext, request []byte) (output []byte, err error)
getOptions() *options.ObjectHandlerOptions
Handler
}

// ServiceHandler is the required set of methods for a Service handler.
type ServiceHandler interface {
Call(ctx Context, request []byte) (output []byte, err error)
getOptions() *options.ServiceHandlerOptions
Handler
}

// Handler is implemented by all Restate handlers
type Handler interface {
sealed()
getOptions() *options.HandlerOptions
InputPayload() *encoding.InputPayload
OutputPayload() *encoding.OutputPayload
HandlerType() *internal.ServiceHandlerType
Expand All @@ -51,16 +49,16 @@ type ObjectSharedHandlerFn[I any, O any] func(ctx ObjectSharedContext, input I)

type serviceHandler[I any, O any] struct {
fn ServiceHandlerFn[I, O]
options options.ServiceHandlerOptions
options options.HandlerOptions
}

var _ ServiceHandler = (*serviceHandler[struct{}, struct{}])(nil)

// NewServiceHandler converts a function of signature [ServiceHandlerFn] into a handler on a Restate service.
func NewServiceHandler[I any, O any](fn ServiceHandlerFn[I, O], opts ...options.ServiceHandlerOption) *serviceHandler[I, O] {
o := options.ServiceHandlerOptions{}
func NewServiceHandler[I any, O any](fn ServiceHandlerFn[I, O], opts ...options.HandlerOption) *serviceHandler[I, O] {
o := options.HandlerOptions{}
for _, opt := range opts {
opt.BeforeServiceHandler(&o)
opt.BeforeHandler(&o)
}
return &serviceHandler[I, O]{
fn: fn,
Expand Down Expand Up @@ -104,28 +102,26 @@ func (h *serviceHandler[I, O]) HandlerType() *internal.ServiceHandlerType {
return nil
}

func (h *serviceHandler[I, O]) getOptions() *options.ServiceHandlerOptions {
func (h *serviceHandler[I, O]) getOptions() *options.HandlerOptions {
return &h.options
}

func (h *serviceHandler[I, O]) sealed() {}

type objectHandler[I any, O any] struct {
// only one of exclusiveFn or sharedFn should be set, as indicated by handlerType
exclusiveFn ObjectHandlerFn[I, O]
sharedFn ObjectSharedHandlerFn[I, O]
options options.ObjectHandlerOptions
options options.HandlerOptions
handlerType internal.ServiceHandlerType
}

var _ ObjectHandler = (*objectHandler[struct{}, struct{}])(nil)

// NewObjectHandler converts a function of signature [ObjectHandlerFn] into an exclusive-mode handler on a Virtual Object.
// The handler will have access to a full [ObjectContext] which may mutate state.
func NewObjectHandler[I any, O any](fn ObjectHandlerFn[I, O], opts ...options.ObjectHandlerOption) *objectHandler[I, O] {
o := options.ObjectHandlerOptions{}
func NewObjectHandler[I any, O any](fn ObjectHandlerFn[I, O], opts ...options.HandlerOption) *objectHandler[I, O] {
o := options.HandlerOptions{}
for _, opt := range opts {
opt.BeforeObjectHandler(&o)
opt.BeforeHandler(&o)
}
return &objectHandler[I, O]{
exclusiveFn: fn,
Expand All @@ -136,10 +132,10 @@ func NewObjectHandler[I any, O any](fn ObjectHandlerFn[I, O], opts ...options.Ob

// NewObjectSharedHandler converts a function of signature [ObjectSharedHandlerFn] into a shared-mode handler on a Virtual Object.
// The handler will only have access to a [ObjectSharedContext] which can only read a snapshot of state.
func NewObjectSharedHandler[I any, O any](fn ObjectSharedHandlerFn[I, O], opts ...options.ObjectHandlerOption) *objectHandler[I, O] {
o := options.ObjectHandlerOptions{}
func NewObjectSharedHandler[I any, O any](fn ObjectSharedHandlerFn[I, O], opts ...options.HandlerOption) *objectHandler[I, O] {
o := options.HandlerOptions{}
for _, opt := range opts {
opt.BeforeObjectHandler(&o)
opt.BeforeHandler(&o)
}
return &objectHandler[I, O]{
sharedFn: fn,
Expand Down Expand Up @@ -190,12 +186,10 @@ func (h *objectHandler[I, O]) OutputPayload() *encoding.OutputPayload {
return encoding.OutputPayloadFor(h.options.Codec, o)
}

func (h *objectHandler[I, O]) getOptions() *options.ObjectHandlerOptions {
func (h *objectHandler[I, O]) getOptions() *options.HandlerOptions {
return &h.options
}

func (h *objectHandler[I, O]) HandlerType() *internal.ServiceHandlerType {
return &h.handlerType
}

func (h *objectHandler[I, O]) sealed() {}
28 changes: 6 additions & 22 deletions internal/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,34 +51,18 @@ type RunOption interface {
BeforeRun(*RunOptions)
}

type ServiceHandlerOptions struct {
type HandlerOptions struct {
Codec encoding.PayloadCodec
}

type ServiceHandlerOption interface {
BeforeServiceHandler(*ServiceHandlerOptions)
type HandlerOption interface {
BeforeHandler(*HandlerOptions)
}

type ObjectHandlerOptions struct {
Codec encoding.PayloadCodec
}

type ObjectHandlerOption interface {
BeforeObjectHandler(*ObjectHandlerOptions)
}

type ServiceOptions struct {
DefaultCodec encoding.PayloadCodec
}

type ServiceOption interface {
BeforeService(*ServiceOptions)
}

type ObjectOptions struct {
type ServiceDefinitionOptions struct {
DefaultCodec encoding.PayloadCodec
}

type ObjectOption interface {
BeforeObject(*ObjectOptions)
type ServiceDefinitionOption interface {
BeforeServiceDefinition(*ServiceDefinitionOptions)
}
19 changes: 5 additions & 14 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import (

// re-export for use in generated code
type CallOption = options.CallOption
type ServiceOption = options.ServiceOption
type ObjectOption = options.ObjectOption
type ServiceDefinitionOption = options.ServiceDefinitionOption

type withCodec struct {
codec encoding.Codec
Expand Down Expand Up @@ -43,21 +42,13 @@ type withPayloadCodec struct {
codec encoding.PayloadCodec
}

var _ options.ServiceHandlerOption = withPayloadCodec{}
var _ options.ServiceOption = withPayloadCodec{}
var _ options.ObjectHandlerOption = withPayloadCodec{}
var _ options.ObjectOption = withPayloadCodec{}
var _ options.HandlerOption = withPayloadCodec{}
var _ options.ServiceDefinitionOption = withPayloadCodec{}

func (w withPayloadCodec) BeforeServiceHandler(opts *options.ServiceHandlerOptions) {
func (w withPayloadCodec) BeforeHandler(opts *options.HandlerOptions) {
opts.Codec = w.codec
}
func (w withPayloadCodec) BeforeObjectHandler(opts *options.ObjectHandlerOptions) {
opts.Codec = w.codec
}
func (w withPayloadCodec) BeforeService(opts *options.ServiceOptions) {
opts.DefaultCodec = w.codec
}
func (w withPayloadCodec) BeforeObject(opts *options.ObjectOptions) {
func (w withPayloadCodec) BeforeServiceDefinition(opts *options.ServiceDefinitionOptions) {
opts.DefaultCodec = w.codec
}

Expand Down
17 changes: 2 additions & 15 deletions protoc-gen-go-restate/restate.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,15 +241,15 @@ func genService(gen *protogen.Plugin, g *protogen.GeneratedFile, service *protog
g.P(deprecationComment)
}

g.P("func New", service.GoName, "Server(srv ", serverType, ", opts ...", routerOptionType(gen, g, service), ") ", sdkPackage.Ident("ServiceDefinition"), " {")
g.P("func New", service.GoName, "Server(srv ", serverType, ", opts ...", sdkPackage.Ident("ServiceDefinitionOption"), ") ", sdkPackage.Ident("ServiceDefinition"), " {")
g.P("// If the following call panics, it indicates Unimplemented", serverType, " was")
g.P("// embedded by pointer and is nil. This will cause panics if an")
g.P("// unimplemented method is ever invoked, so we test this at initialization")
g.P("// time to prevent it from happening at runtime later due to I/O.")
g.P("if t, ok := srv.(interface { testEmbeddedByValue() }); ok {")
g.P("t.testEmbeddedByValue()")
g.P("}")
g.P("sOpts := append([]", routerOptionType(gen, g, service), "{", sdkPackage.Ident("WithProtoJSON"), "}, opts...)")
g.P("sOpts := append([]", sdkPackage.Ident("ServiceDefinitionOption"), "{", sdkPackage.Ident("WithProtoJSON"), "}, opts...)")
g.P("router := ", newRouterType(gen, g, service), `("`, service.GoName, `", sOpts...)`)
for _, method := range service.Methods {
g.P(`router = router.Handler("`, method.GoName, `",`, newHandlerType(gen, g, method), "(srv.", method.GoName, "))")
Expand Down Expand Up @@ -347,19 +347,6 @@ func newRouterType(gen *protogen.Plugin, g *protogen.GeneratedFile, service *pro
}
}

func routerOptionType(gen *protogen.Plugin, g *protogen.GeneratedFile, service *protogen.Service) string {
serviceType := proto.GetExtension(service.Desc.Options().(*descriptorpb.ServiceOptions), sdk.E_ServiceType).(sdk.ServiceType)
switch serviceType {
case sdk.ServiceType_SERVICE:
return g.QualifiedGoIdent(sdkPackage.Ident("ServiceOption"))
case sdk.ServiceType_VIRTUAL_OBJECT:
return g.QualifiedGoIdent(sdkPackage.Ident("ObjectOption"))
default:
gen.Error(fmt.Errorf("Unexpected service type: %s", serviceType.String()))
return ""
}
}

func newHandlerType(gen *protogen.Plugin, g *protogen.GeneratedFile, method *protogen.Method) string {
serviceType := proto.GetExtension(method.Parent.Desc.Options().(*descriptorpb.ServiceOptions), sdk.E_ServiceType).(sdk.ServiceType)
handlerType := proto.GetExtension(method.Desc.Options().(*descriptorpb.MethodOptions), sdk.E_HandlerType).(sdk.HandlerType)
Expand Down
Loading
Loading