Skip to content

Commit

Permalink
adding golangci config, adding linting to github actions, fixing lint… (
Browse files Browse the repository at this point in the history
actatum#5)

* adding golangci config, adding linting to github actions, fixing lint errors

* removing some print statements
  • Loading branch information
actatum authored Jun 21, 2022
1 parent da203bb commit e5c95cd
Show file tree
Hide file tree
Showing 11 changed files with 77 additions and 17 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/actions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ jobs:
with:
go-version: ${{ matrix.go-version }}
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
- name: Run Tests
run: |
go test -v -race ./...
23 changes: 23 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
run:
timeout: 5m
modules-download-mode: readonly

linters:
enable:
- errcheck
- goimports
- revive
- govet
- staticcheck
- lll

linters-settings:
errcheck:
check-type-assertions: true
govet:
check-shadowing: true

issues:
exclude-use-default: false
max-issues-per-linter: 0
max-same-issues: 0
5 changes: 5 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import (
"github.com/nats-io/nats.go"
)

// Client represents a stormRPC client. It contains all functionality for making RPC requests
// to stormRPC servers.
type Client struct {
nc *nats.Conn
}

// NewClient returns a new instance of a Client.
func NewClient(natsURL string, opts ...ClientOption) (*Client, error) {
nc, err := nats.Connect(natsURL)
if err != nil {
Expand All @@ -24,6 +27,7 @@ func NewClient(natsURL string, opts ...ClientOption) (*Client, error) {

type clientOptions struct{}

// ClientOption represents functional options for configuring a stormRPC Client.
type ClientOption interface {
apply(*clientOptions)
}
Expand All @@ -33,6 +37,7 @@ func (c *Client) Close() {
c.nc.Close()
}

// Do completes a request to a stormRPC Server.
func (c *Client) Do(ctx context.Context, r Request) Response {
msg, err := c.nc.RequestMsgWithContext(ctx, r.Msg)
if errors.Is(err, nats.ErrNoResponders) {
Expand Down
18 changes: 13 additions & 5 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ func TestNewClient(t *testing.T) {
return
}

_, err = NewClient(ns.ClientURL())
c, err := NewClient(ns.ClientURL())
if err != nil {
t.Fatal(err)
}
c.Close()
})
}

Expand Down Expand Up @@ -80,7 +81,9 @@ func TestClient_Do(t *testing.T) {
time.Sleep(timeout + 10*time.Millisecond)
return Response{Msg: &nats.Msg{Subject: r.Reply}}
})
go srv.Run()
go func() {
_ = srv.Run()
}()
t.Cleanup(func() {
_ = srv.Shutdown(context.Background())
})
Expand Down Expand Up @@ -119,7 +122,9 @@ func TestClient_Do(t *testing.T) {
srv.Handle(subject, func(ctx context.Context, r Request) Response {
return NewErrorResponse(r.Reply, Errorf(ErrorCodeNotFound, "thingy not found"))
})
go srv.Run()
go func() {
_ = srv.Run()
}()
t.Cleanup(func() {
_ = srv.Shutdown(context.Background())
})
Expand Down Expand Up @@ -197,13 +202,16 @@ func TestClient_Do(t *testing.T) {
t.Fatal(err)
}
srv.Handle(subject, func(ctx context.Context, r Request) Response {
resp, err := NewResponse(r.Reply, map[string]string{"hello": "world"})
var resp Response
resp, err = NewResponse(r.Reply, map[string]string{"hello": "world"})
if err != nil {
return NewErrorResponse(r.Reply, err)
}
return resp
})
go srv.Run()
go func() {
_ = srv.Run()
}()
t.Cleanup(func() {
_ = srv.Shutdown(context.Background())
})
Expand Down
9 changes: 9 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import (
"fmt"
)

// ErrorCode represents an enum type for stormRPC error codes.
type ErrorCode int

// RPC ErrorCodes.
const (
ErrorCodeUnknown ErrorCode = 0
ErrorCodeInternal ErrorCode = 1
Expand All @@ -24,22 +26,27 @@ func (c ErrorCode) String() string {
}
}

// Error represents an RPC error.
type Error struct {
Code ErrorCode
Message string
}

// Error allows for the Error type to conform to the built-in error interface.
func (e Error) Error() string {
return fmt.Sprintf("%s: %s", e.Code.String(), e.Message)
}

// Errorf constructs a new RPC Error.
func Errorf(code ErrorCode, format string, args ...any) *Error {
return &Error{
Code: code,
Message: fmt.Sprintf(format, args...),
}
}

// CodeFromErr retrieves the ErrorCode from a given error.
// If the error is not of type Error, ErrorCodeUnknown is returned.
func CodeFromErr(err error) ErrorCode {
var e *Error
if errors.As(err, &e) {
Expand All @@ -48,6 +55,8 @@ func CodeFromErr(err error) ErrorCode {
return ErrorCodeUnknown
}

// MessageFromErr retrieves the message from a given error.
// If the error is not of type Error, "unknown error" is returned.
func MessageFromErr(err error) string {
var e *Error
if errors.As(err, &e) {
Expand Down
2 changes: 2 additions & 0 deletions middleware/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"go.uber.org/zap"
)

// Logger logs request scoped information such as request id, trace information, and request duration.
// This middleware should be applied after RequestID, and Tracing.
func Logger(l *zap.Logger) func(next stormrpc.HandlerFunc) stormrpc.HandlerFunc {
return func(next stormrpc.HandlerFunc) stormrpc.HandlerFunc {
return func(ctx context.Context, r stormrpc.Request) stormrpc.Response {
Expand Down
1 change: 0 additions & 1 deletion middleware/recoverer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ func TestRecoverer(t *testing.T) {
req, _ := stormrpc.NewRequest("test", map[string]string{"hi": "there"})
handler := stormrpc.HandlerFunc(func(ctx context.Context, r stormrpc.Request) stormrpc.Response {
panic(fmt.Errorf("panic"))
return stormrpc.NewErrorResponse("test", fmt.Errorf("new"))
})
h := Recoverer(handler)
resp := h(context.Background(), req)
Expand Down
8 changes: 8 additions & 0 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import (
"google.golang.org/protobuf/proto"
)

// Request is stormRPC's wrapper around a nats.Msg and is used by both clients and servers.
type Request struct {
*nats.Msg
}

// NewRequest constructs a new request with the given parameters. It also handles encoding the request body.
func NewRequest(subject string, body any, opts ...RequestOption) (Request, error) {
options := requestOptions{
encodeProto: false,
Expand Down Expand Up @@ -65,6 +67,7 @@ type requestOptions struct {
encodeMsgpack bool
}

// RequestOption represents functional options for configuring a request.
type RequestOption interface {
apply(options *requestOptions)
}
Expand All @@ -75,6 +78,7 @@ func (p encodeProtoOption) apply(opts *requestOptions) {
opts.encodeProto = bool(p)
}

// WithEncodeProto is a RequestOption to encode the request body using the proto.Marshal method.
func WithEncodeProto() RequestOption {
return encodeProtoOption(true)
}
Expand All @@ -85,10 +89,13 @@ func (p encodeMsgpackOption) apply(opts *requestOptions) {
opts.encodeMsgpack = bool(p)
}

// WithEncodeMsgpack is a RequestOption to encode the request body using the msgpack.Marshal method.
func WithEncodeMsgpack() RequestOption {
return encodeMsgpackOption(true)
}

// Decode de-serializes the body into the passed in object. The de-serialization method is based on
// the request's Content-Type header.
func (r *Request) Decode(v any) error {
var err error

Expand All @@ -113,6 +120,7 @@ func (r *Request) Decode(v any) error {
return nil
}

// Subject returns the underlying nats.Msg subject.
func (r *Request) Subject() string {
return r.Msg.Subject
}
8 changes: 6 additions & 2 deletions response.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import (
"google.golang.org/protobuf/proto"
)

// TODO: Create constructor like request that handles encoding etc.

// Response is stormRPC's wrapper around a nats.Msg and is used by both clients and servers.
type Response struct {
*nats.Msg
Err error
}

// NewResponse constructs a new response with the given parameters. It also handles encoding the response body.
func NewResponse(reply string, body any, opts ...ResponseOption) (Response, error) {
options := requestOptions{
encodeProto: false,
Expand Down Expand Up @@ -65,6 +65,7 @@ func NewResponse(reply string, body any, opts ...ResponseOption) (Response, erro
}, nil
}

// NewErrorResponse constructs a new error response with the given parameters.
func NewErrorResponse(reply string, err error) Response {
return Response{
Msg: &nats.Msg{
Expand All @@ -74,8 +75,11 @@ func NewErrorResponse(reply string, err error) Response {
}
}

// ResponseOption represents functional options for configuring a response.
type ResponseOption RequestOption

// Decode de-serializes the body into the passed in object. The de-serialization method is based on
// the response's Content-Type header.
func (r *Response) Decode(v any) error {
var err error

Expand Down
16 changes: 8 additions & 8 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package stormrpc

import (
"context"
"fmt"
"time"

"github.com/nats-io/nats.go"
)

var defaultServerTimeout = 5 * time.Second

// Server represents a stormRPC server. It contains all functionality for handling RPC requests.
type Server struct {
nc *nats.Conn
name string
Expand All @@ -20,6 +20,7 @@ type Server struct {
mw []Middleware
}

// NewServer returns a new instance of a Server.
func NewServer(name, natsURL string, opts ...ServerOption) (*Server, error) {
options := serverOptions{
errorHandler: func(ctx context.Context, err error) {},
Expand Down Expand Up @@ -48,6 +49,7 @@ type serverOptions struct {
errorHandler ErrorHandler
}

// ServerOption represents functional options for configuring a stormRPC Server.
type ServerOption interface {
apply(*serverOptions)
}
Expand All @@ -58,16 +60,21 @@ func (h errorHandlerOption) apply(opts *serverOptions) {
opts.errorHandler = ErrorHandler(h)
}

// WithErrorHandler is a ServerOption that allows for registering a function for handling server errors.
func WithErrorHandler(fn ErrorHandler) ServerOption {
return errorHandlerOption(fn)
}

// HandlerFunc is the function signature for handling of a single request to a stormRPC server.
type HandlerFunc func(ctx context.Context, r Request) Response

// Middleware is the function signature for wrapping HandlerFunc's to extend their functionality.
type Middleware func(next HandlerFunc) HandlerFunc

// ErrorHandler is the function signature for handling server errors.
type ErrorHandler func(context.Context, error)

// Handle registers a new HandlerFunc on the server.
func (s *Server) Handle(subject string, fn HandlerFunc) {
s.handlerFuncs[subject] = fn
}
Expand Down Expand Up @@ -126,9 +133,6 @@ func (s *Server) applyMiddlewares() {
// wildcard subjects are not supported as you'll need to register a handler func for each
// rpc the server supports.
func (s *Server) handler(msg *nats.Msg) {
// TODO: remove this Printf
fmt.Printf("received msg on subject: %s = %s\n", msg.Subject, string(msg.Data))

fn := s.handlerFuncs[msg.Subject]

ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
Expand All @@ -154,15 +158,11 @@ func (s *Server) handler(msg *nats.Msg) {
err := msg.RespondMsg(resp.Msg)
if err != nil {
s.errorHandler(ctx, err)
// TODO: remove the Printf
fmt.Printf("msg.RespondMsg: %v\n", err)
}
}

err := msg.RespondMsg(resp.Msg)
if err != nil {
s.errorHandler(ctx, err)
// TODO: remove the Printf
fmt.Printf("msg.RespondMsg: %v\n", err)
}
}
2 changes: 1 addition & 1 deletion server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ func sameStringSlice(x, y []string) bool {
if _, ok := diff[_y]; !ok {
return false
}
diff[_y] -= 1
diff[_y]--
if diff[_y] == 0 {
delete(diff, _y)
}
Expand Down

0 comments on commit e5c95cd

Please sign in to comment.