improve shutdown and app service boilerplate
This commit is contained in:
@ -1,13 +1,14 @@
|
||||
// This serves as a reference sample configuration
|
||||
// to show how to merge custom config fields into
|
||||
// go-app configuration
|
||||
// Package config contains ServiceConfig, which is intended
|
||||
// to extend go-app/pkg/config.AppConfig. Add any custom fields
|
||||
// here, and it will automatically be handled by go-app, including
|
||||
// overrides by environment, and json schema generation
|
||||
package config
|
||||
|
||||
import (
|
||||
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/config"
|
||||
)
|
||||
|
||||
type DemoConfig struct {
|
||||
type ServiceConfig struct {
|
||||
Timezone string `yaml:"timezone" json:"timezone,omitempty" default:"UTC"`
|
||||
Opts *DemoOpts `yaml:"opts" json:"opts,omitempty"`
|
||||
|
||||
|
54
pkg/demo/demo.go
Normal file
54
pkg/demo/demo.go
Normal file
@ -0,0 +1,54 @@
|
||||
// Package demo provides a reference implementation
|
||||
// of service.AppService. It packages out the GRPC and HTTP
|
||||
// functionality, for the sake of structure.
|
||||
package demo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
optsgrpc "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/grpc/opts"
|
||||
optshttp "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/http/opts"
|
||||
|
||||
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/config"
|
||||
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/demo/demogrpc"
|
||||
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/demo/demohttp"
|
||||
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/service"
|
||||
)
|
||||
|
||||
type DemoService struct {
|
||||
ctx context.Context
|
||||
config *config.ServiceConfig
|
||||
http *demohttp.DemoHTTPServer
|
||||
grpc *demogrpc.DemoGRPCServer
|
||||
}
|
||||
|
||||
func (d *DemoService) Init(ctx context.Context, config *config.ServiceConfig,
|
||||
) (service.ShutdownFunc, error) {
|
||||
d.config = config
|
||||
d.ctx = ctx
|
||||
|
||||
// These don't HAVE to be split into separate packages
|
||||
// This is, after all, a demo app. Just implement the interface.
|
||||
d.http = demohttp.NewDemoHTTPServer(ctx, config)
|
||||
d.grpc = demogrpc.NewDemoGRPCServer(ctx, config)
|
||||
|
||||
// TODO: This should actually do shutdown stuff
|
||||
return func(_ context.Context) (string, error) {
|
||||
return "DemoService", nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DemoService) GetGRPC() *optsgrpc.AppGRPC {
|
||||
return &optsgrpc.AppGRPC{
|
||||
Services: d.grpc.GetServices(),
|
||||
GRPCDialOpts: d.grpc.GetDialOpts(),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DemoService) GetHTTP() *optshttp.AppHTTP {
|
||||
return &optshttp.AppHTTP{
|
||||
Ctx: d.ctx,
|
||||
Funcs: d.http.GetHandleFuncs(),
|
||||
HealthChecks: d.http.GetHealthCheckFuncs(),
|
||||
}
|
||||
}
|
@ -27,12 +27,12 @@ const (
|
||||
type DemoGRPCServer struct {
|
||||
tracer trace.Tracer
|
||||
ctx context.Context
|
||||
cfg *config.DemoConfig
|
||||
cfg *config.ServiceConfig
|
||||
client *resty.Client
|
||||
pb.UnimplementedDemoAppServiceServer
|
||||
}
|
||||
|
||||
func NewDemoGRPCServer(ctx context.Context, cfg *config.DemoConfig) *DemoGRPCServer {
|
||||
func NewDemoGRPCServer(ctx context.Context, cfg *config.ServiceConfig) *DemoGRPCServer {
|
||||
if cfg.Opts == nil {
|
||||
cfg.Opts = &config.DemoOpts{}
|
||||
}
|
@ -11,10 +11,10 @@ import (
|
||||
|
||||
type DemoHTTPServer struct {
|
||||
ctx context.Context
|
||||
cfg *config.DemoConfig
|
||||
cfg *config.ServiceConfig
|
||||
}
|
||||
|
||||
func NewDemoHTTPServer(ctx context.Context, cfg *config.DemoConfig) *DemoHTTPServer {
|
||||
func NewDemoHTTPServer(ctx context.Context, cfg *config.ServiceConfig) *DemoHTTPServer {
|
||||
return &DemoHTTPServer{
|
||||
ctx: ctx,
|
||||
cfg: cfg,
|
63
pkg/service/service.go
Normal file
63
pkg/service/service.go
Normal file
@ -0,0 +1,63 @@
|
||||
// Package service defines generics around implementation of
|
||||
// a new service that provides configuration for go-app
|
||||
//
|
||||
// The interface is meant to make the template more clear to implement
|
||||
// without unnecessary changes in main.go
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/config"
|
||||
|
||||
optsgrpc "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/grpc/opts"
|
||||
optshttp "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/http/opts"
|
||||
)
|
||||
|
||||
type ShutdownFunc func(ctx context.Context) (string, error)
|
||||
|
||||
type AppService interface {
|
||||
Init(ctx context.Context, config *config.ServiceConfig) (ShutdownFunc, error)
|
||||
GetGRPC() *optsgrpc.AppGRPC
|
||||
GetHTTP() *optshttp.AppHTTP
|
||||
}
|
||||
|
||||
// MergeServices is used if multiple services are served by this server
|
||||
// with their own distinct packages and configuration
|
||||
//
|
||||
// You should be overriding the done chan, listener, or any other
|
||||
// configuration used by the SERVER rather than the individual services.
|
||||
func MergeServices(ctx context.Context, appServices ...AppService) (*optsgrpc.AppGRPC, *optshttp.AppHTTP) {
|
||||
mergedGRPC := &optsgrpc.AppGRPC{}
|
||||
for _, svc := range appServices {
|
||||
mergedGRPC = mergeGRPC(mergedGRPC, svc.GetGRPC())
|
||||
}
|
||||
|
||||
mergedHTTP := &optshttp.AppHTTP{}
|
||||
for _, svc := range appServices {
|
||||
mergedHTTP = mergeHTTP(mergedHTTP, svc.GetHTTP())
|
||||
}
|
||||
mergedHTTP.Ctx = ctx
|
||||
|
||||
return mergedGRPC, mergedHTTP
|
||||
}
|
||||
|
||||
func mergeHTTP(a *optshttp.AppHTTP, b *optshttp.AppHTTP) *optshttp.AppHTTP {
|
||||
return &optshttp.AppHTTP{
|
||||
Funcs: append(a.Funcs, b.Funcs...),
|
||||
Middleware: append(a.Middleware, b.Middleware...),
|
||||
Handlers: append(a.Handlers, b.Handlers...),
|
||||
HealthChecks: append(a.HealthChecks, b.HealthChecks...),
|
||||
}
|
||||
}
|
||||
|
||||
func mergeGRPC(a *optsgrpc.AppGRPC, b *optsgrpc.AppGRPC) *optsgrpc.AppGRPC {
|
||||
return &optsgrpc.AppGRPC{
|
||||
Services: append(a.Services, b.Services...),
|
||||
GRPCOpts: append(a.GRPCOpts, b.GRPCOpts...),
|
||||
UnaryInterceptors: append(a.UnaryInterceptors, b.UnaryInterceptors...),
|
||||
StreamInterceptors: append(a.StreamInterceptors, b.StreamInterceptors...),
|
||||
GRPCDialOpts: append(a.GRPCDialOpts, b.GRPCDialOpts...),
|
||||
GRPCGatewayOpts: append(a.GRPCGatewayOpts, b.GRPCGatewayOpts...),
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user