service backend
This commit is contained in:
149
api/pkg/api/routers/gsresponse/response.go
Normal file
149
api/pkg/api/routers/gsresponse/response.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package gsresponse
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// Responder produces a response or a gRPC status error when executed.
|
||||
type Responder[T any] func(ctx context.Context) (*T, error)
|
||||
|
||||
func message(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
func Success[T any](resp *T) Responder[T] {
|
||||
return func(context.Context) (*T, error) {
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
func Empty[T any]() Responder[T] {
|
||||
return func(context.Context) (*T, error) {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func Error[T any](logger mlogger.Logger, service mservice.Type, code codes.Code, hint string, err error) Responder[T] {
|
||||
return func(ctx context.Context) (*T, error) {
|
||||
fields := []zap.Field{
|
||||
zap.String("service", string(service)),
|
||||
zap.String("status_code", code.String()),
|
||||
}
|
||||
if hint != "" {
|
||||
fields = append(fields, zap.String("error_hint", hint))
|
||||
}
|
||||
if err != nil {
|
||||
fields = append(fields, zap.Error(err))
|
||||
}
|
||||
logFn := logger.Warn
|
||||
switch code {
|
||||
case codes.Internal, codes.DataLoss, codes.Unavailable:
|
||||
logFn = logger.Error
|
||||
}
|
||||
logFn("gRPC request failed", fields...)
|
||||
|
||||
msg := message(err)
|
||||
switch {
|
||||
case hint == "" && msg == "":
|
||||
return nil, status.Error(code, code.String())
|
||||
case hint == "":
|
||||
return nil, status.Error(code, msg)
|
||||
case msg == "":
|
||||
return nil, status.Error(code, hint)
|
||||
default:
|
||||
return nil, status.Error(code, fmt.Sprintf("%s: %s", hint, msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Internal[T any](logger mlogger.Logger, service mservice.Type, err error) Responder[T] {
|
||||
return Error[T](logger, service, codes.Internal, "internal_error", err)
|
||||
}
|
||||
|
||||
func InvalidArgument[T any](logger mlogger.Logger, service mservice.Type, err error) Responder[T] {
|
||||
return Error[T](logger, service, codes.InvalidArgument, "invalid_argument", err)
|
||||
}
|
||||
|
||||
func NotFound[T any](logger mlogger.Logger, service mservice.Type, err error) Responder[T] {
|
||||
return Error[T](logger, service, codes.NotFound, "not_found", err)
|
||||
}
|
||||
|
||||
func Unauthorized[T any](logger mlogger.Logger, service mservice.Type, err error) Responder[T] {
|
||||
return Error[T](logger, service, codes.Unauthenticated, "unauthorized", err)
|
||||
}
|
||||
|
||||
func PermissionDenied[T any](logger mlogger.Logger, service mservice.Type, err error) Responder[T] {
|
||||
return Error[T](logger, service, codes.PermissionDenied, "access_denied", err)
|
||||
}
|
||||
|
||||
func FailedPrecondition[T any](logger mlogger.Logger, service mservice.Type, hint string, err error) Responder[T] {
|
||||
return Error[T](logger, service, codes.FailedPrecondition, hint, err)
|
||||
}
|
||||
|
||||
func Conflict[T any](logger mlogger.Logger, service mservice.Type, err error) Responder[T] {
|
||||
return Error[T](logger, service, codes.Aborted, "conflict", err)
|
||||
}
|
||||
|
||||
func DeadlineExceeded[T any](logger mlogger.Logger, service mservice.Type, err error) Responder[T] {
|
||||
return Error[T](logger, service, codes.DeadlineExceeded, "deadline_exceeded", err)
|
||||
}
|
||||
|
||||
func Unavailable[T any](logger mlogger.Logger, service mservice.Type, err error) Responder[T] {
|
||||
return Error[T](logger, service, codes.Unavailable, "service_unavailable", err)
|
||||
}
|
||||
|
||||
func Unimplemented[T any](logger mlogger.Logger, service mservice.Type, err error) Responder[T] {
|
||||
return Error[T](logger, service, codes.Unimplemented, "not_implemented", err)
|
||||
}
|
||||
|
||||
func AlreadyExists[T any](logger mlogger.Logger, service mservice.Type, err error) Responder[T] {
|
||||
return Error[T](logger, service, codes.AlreadyExists, "already_exists", err)
|
||||
}
|
||||
|
||||
func Auto[T any](logger mlogger.Logger, service mservice.Type, err error) Responder[T] {
|
||||
switch {
|
||||
case err == nil:
|
||||
return Empty[T]()
|
||||
case errors.Is(err, merrors.ErrInvalidArg):
|
||||
return InvalidArgument[T](logger, service, err)
|
||||
case errors.Is(err, merrors.ErrAccessDenied):
|
||||
return PermissionDenied[T](logger, service, err)
|
||||
case errors.Is(err, merrors.ErrNoData):
|
||||
return NotFound[T](logger, service, err)
|
||||
case errors.Is(err, merrors.ErrUnauthorized):
|
||||
return Unauthorized[T](logger, service, err)
|
||||
case errors.Is(err, merrors.ErrDataConflict):
|
||||
return Conflict[T](logger, service, err)
|
||||
default:
|
||||
return Internal[T](logger, service, err)
|
||||
}
|
||||
}
|
||||
|
||||
func Execute[T any](ctx context.Context, responder Responder[T]) (*T, error) {
|
||||
if responder == nil {
|
||||
return nil, status.Error(codes.Internal, "missing responder")
|
||||
}
|
||||
return responder(ctx)
|
||||
}
|
||||
|
||||
func Unary[TReq any, TResp any](logger mlogger.Logger, service mservice.Type, handler func(context.Context, *TReq) Responder[TResp]) func(context.Context, *TReq) (*TResp, error) {
|
||||
return func(ctx context.Context, req *TReq) (*TResp, error) {
|
||||
if handler == nil {
|
||||
return nil, status.Error(codes.Internal, "missing handler")
|
||||
}
|
||||
responder := handler(ctx, req)
|
||||
return Execute(ctx, responder)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user