Skip to content

Commit

Permalink
Merge pull request #1 from Golerplate/feat/grpc
Browse files Browse the repository at this point in the history
feat: add grpc and errors
  • Loading branch information
Teyz authored Feb 15, 2024
2 parents abab115 + 3ce24fc commit fcca09e
Show file tree
Hide file tree
Showing 10 changed files with 437 additions and 0 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Create new release

on:
push:
branches:
- main

jobs:
create_release:
name: Create new release
runs-on: ubuntu-latest
steps:
- name: Code checkout
uses: actions/checkout@v2

- name: Read last release
id: get_last_release
run: echo ::set-output name=version::$(git describe --tags $(git rev-list --tags --max-count=1))

- name: Calculate new version
id: calculate_new_version
run: echo ::set-output name=version::$(echo "${{ steps.get_last_release.outputs.version }}" | awk -F. -v OFS=. '{$NF = $NF + 1;} 1')

- name: Get commit messages
id: get_commit_messages
run: |
git log --pretty=format:"- %s" $(git describe --tags $(git rev-list --tags --max-count=1))..HEAD > commit_messages.txt
cat commit_messages.txt
echo "::set-output name=commit_messages::$(cat commit_messages.txt)"
- name: Create new release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ steps.calculate_new_version.outputs.version }}
release_name: v${{ steps.calculate_new_version.outputs.version }}
body: |
${{ steps.get_commit_messages.outputs.commit_messages }}
draft: false
prerelease: false
158 changes: 158 additions & 0 deletions errors/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package errors

import (
"github.com/pkg/errors"
)

type ErrorWithKey struct {
Key string
}

func (e *ErrorWithKey) Error() string {
return e.Key
}

// NewNotFoundError return a new NotFoundError
func NewNotFoundError(key string) error {
return errors.WithStack(&NotFoundError{
ErrorWithKey: ErrorWithKey{
Key: key,
},
})
}

// NotFoundError is used when we cannot find a specified resource
type NotFoundError struct {
ErrorWithKey
}

// IsNotFoundError verify if an error is a NotFoundError
func IsNotFoundError(err error) bool {
_, ok := errors.Cause(err).(*NotFoundError)

return ok
}

// NewBadRequestError return a new BadRequestError
func NewBadRequestError(key string) error {
return errors.WithStack(&BadRequestError{
ErrorWithKey: ErrorWithKey{
Key: key,
},
})
}

// BadRequestError is used when the given parameters does not match requirements
type BadRequestError struct {
ErrorWithKey
}

// IsBadRequestError verify if an error is a BadRequestError
func IsBadRequestError(err error) bool {
_, ok := errors.Cause(err).(*BadRequestError)

return ok
}

// NewExpiredResourceError return a new ExpiredResourceError
func NewExpiredResourceError(key string) error {
return errors.WithStack(&ExpiredResourceError{
ErrorWithKey: ErrorWithKey{
Key: key,
},
})
}

// ExpiresResourceError is used when the given resource has expired
type ExpiredResourceError struct {
ErrorWithKey
}

// IsExpiresResourceError verify if an error is a ExpiredResourceError
func IsExpiredResourceError(err error) bool {
_, ok := errors.Cause(err).(*ExpiredResourceError)

return ok
}

// NewInternalServerError return a new InternalServerError
func NewInternalServerError(key string) error {
return errors.WithStack(&InternalServerError{
ErrorWithKey: ErrorWithKey{
Key: key,
},
})
}

// InternalServerError is used when an error unexpected appears
type InternalServerError struct {
ErrorWithKey
}

// IsInternalServerError verify if an error is a InternalServerError
func IsInternalServerError(err error) bool {
_, ok := errors.Cause(err).(*InternalServerError)

return ok
}

// NewUnauthorizedError return a new UnauthorizedError
func NewUnauthorizedError(key string, subjectAndMessage ...string) error {
return errors.WithStack(&UnauthorizedError{
ErrorWithKey: ErrorWithKey{key},
})
}

// UnauthorizedError is used when action is not authorized
type UnauthorizedError struct {
ErrorWithKey
}

// IsUnauthorizedError verify if an error is a UnauthorizedError
func IsUnauthorizedError(err error) bool {
_, ok := errors.Cause(err).(*UnauthorizedError)

return ok
}

// NewResourceAlreadyExist return a new ResourceAlreadyExist
func NewResourceAlreadyCreatedError(key string) error {
return errors.WithStack(&ResourceAlreadyCreatedError{
ErrorWithKey: ErrorWithKey{
Key: key,
},
})
}

// ResourceAlreadyCreatedError is used when a resource already exist and could not be created another time
type ResourceAlreadyCreatedError struct {
ErrorWithKey
}

// IsResourceAlreadyCreatedError verify if an error is a ResourceAlreadyCreatedError
func IsResourceAlreadyCreatedError(err error) bool {
_, ok := errors.Cause(err).(*ResourceAlreadyCreatedError)

return ok
}

// NewOutdatedResourceError return a new OutdatedResourceError
func NewOutdatedResourceError(key string) error {
return errors.WithStack(&OutdatedResourceError{
ErrorWithKey: ErrorWithKey{
Key: key,
},
})
}

// ResourceAlreadyCreatedError is used when a resource already exist and could not be created another time
type OutdatedResourceError struct {
ErrorWithKey
}

// IsResourceAlreadyCreatedError verify if an error is a ResourceAlreadyCreatedError
func IsOutdatedResourceError(err error) bool {
_, ok := errors.Cause(err).(*OutdatedResourceError)

return ok
}
16 changes: 16 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module github.com/Golerplate/pkg

go 1.21.5

require (
github.com/bufbuild/connect-go v1.10.0
github.com/pkg/errors v0.9.1
github.com/rs/zerolog v1.32.0
)

require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
golang.org/x/sys v0.14.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
)
28 changes: 28 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
github.com/bufbuild/connect-go v1.10.0 h1:QAJ3G9A1OYQW2Jbk3DeoJbkCxuKArrvZgDt47mjdTbg=
github.com/bufbuild/connect-go v1.10.0/go.mod h1:CAIePUgkDR5pAFaylSMtNK45ANQjp9JvpluG20rhpV8=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
5 changes: 5 additions & 0 deletions grpc/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package grpc

type GRPCServerConfig struct {
Port uint16 `env:"GRPC_SERVER_PORT" envDefault:"50051"`
}
51 changes: 51 additions & 0 deletions grpc/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package grpc

import (
"context"

"github.com/Golerplate/pkg/errors"
connectgo "github.com/bufbuild/connect-go"
)

// TranslateFromGRPCError translates an error from a gRPC service to a errors.
// If no error is passed, it returns nil.
func TranslateFromGRPCError(_ context.Context, err error) error {
// check if error is nil
if err == nil {
return nil
}

switch connectgo.CodeOf(err) {
case connectgo.CodeInternal:
return errors.NewInternalServerError(err.Error())
case connectgo.CodeNotFound:
return errors.NewNotFoundError(err.Error())
case connectgo.CodeAlreadyExists:
return errors.NewResourceAlreadyCreatedError(err.Error())
case connectgo.CodeInvalidArgument:
return errors.NewBadRequestError(err.Error())
default:
return errors.NewInternalServerError(err.Error())
}
}

// TranslateToGRPCError translates an error from errors to a gRPC service.
// If no error is passed, it returns nil.
func TranslateToGRPCError(_ context.Context, err error) error {
if err == nil {
return nil
}

switch {
case errors.IsNotFoundError(err):
return connectgo.NewError(connectgo.CodeNotFound, err)
case errors.IsResourceAlreadyCreatedError(err):
return connectgo.NewError(connectgo.CodeAlreadyExists, err)
case errors.IsBadRequestError(err):
return connectgo.NewError(connectgo.CodeInvalidArgument, err)
case errors.IsInternalServerError(err):
return connectgo.NewError(connectgo.CodeInternal, err)
default:
return connectgo.NewError(connectgo.CodeInternal, err)
}
}
32 changes: 32 additions & 0 deletions grpc/interceptors/chain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package grpc

import (
"time"

"github.com/Golerplate/pkg/grpc/interceptors/logger"
"github.com/Golerplate/pkg/grpc/interceptors/recover"
"github.com/Golerplate/pkg/grpc/interceptors/timeout"
"github.com/bufbuild/connect-go"
)

type InterceptorsChain []connect.Interceptor

func ServerDefaultChain() InterceptorsChain {
return []connect.Interceptor{
recover.RecoverInterceptor(),
}
}

func ClientDefaultChain() InterceptorsChain {
return []connect.Interceptor{
timeout.TimeoutInterceptor(5 * time.Second),
logger.ClientLoggerInterceptor(),
}
}

func ClientConfigurableChain(t time.Duration) InterceptorsChain {
return []connect.Interceptor{
timeout.TimeoutInterceptor(t),
logger.ClientLoggerInterceptor(),
}
}
43 changes: 43 additions & 0 deletions grpc/interceptors/logger/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package logger

import (
"context"
"time"

"github.com/bufbuild/connect-go"
"github.com/rs/zerolog/log"
)

func ClientLoggerInterceptor() connect.UnaryInterceptorFunc {
iceptor := func(next connect.UnaryFunc) connect.UnaryFunc {
return connect.UnaryFunc(func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
startTime := time.Now().UTC()

resp, err := next(ctx, req)
requestDuration := time.Since(startTime)

logger := log.Debug()
if err != nil {
logger = log.Error().Err(err)
}

status := "OK"
if err != nil {
status = "ERROR"
}

if connect.CodeOf(err) == connect.CodeNotFound {
logger = log.Debug()
}

logger.Str("protocol", "grpc").
Str("method", req.Spec().Procedure).
Str("status", status).
Dur("duration", requestDuration).
Msg("sent a grpc call")

return resp, err
})
}
return connect.UnaryInterceptorFunc(iceptor)
}
Loading

0 comments on commit fcca09e

Please sign in to comment.