Skip to content

Commit

Permalink
Return errors on handler.
Browse files Browse the repository at this point in the history
  • Loading branch information
cscatolini committed Apr 11, 2018
1 parent 027f024 commit ccb3af9
Show file tree
Hide file tree
Showing 21 changed files with 342 additions and 1,434 deletions.
13 changes: 13 additions & 0 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,3 +385,16 @@ func (a *Agent) write() {
func (a *Agent) SendRequest(serverID, route string, v interface{}) (*protos.Response, error) {
return nil, fmt.Errorf("not implemented")
}

// AnswerWithError answers with an error
func (a *Agent) AnswerWithError(mid uint, err error) {
p, e := util.GetErrorPayload(a.serializer, err)
if e != nil {
log.Error("error answering the player with an error: ", e.Error())
return
}
e = a.Session.ResponseMID(mid, p)
if e != nil {
log.Error("error answering the player with an error: ", e.Error())
}
}
6 changes: 6 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/topfreegames/pitaya/component"
"github.com/topfreegames/pitaya/config"
"github.com/topfreegames/pitaya/constants"
"github.com/topfreegames/pitaya/errors"
"github.com/topfreegames/pitaya/internal/codec"
"github.com/topfreegames/pitaya/internal/message"
"github.com/topfreegames/pitaya/logger"
Expand Down Expand Up @@ -388,3 +389,8 @@ func AddRoute(
func Shutdown() {
close(app.dieChan)
}

// Error creates a new error with a code, message and metadata
func Error(err error, code string, metadata ...map[string]string) *errors.Error {
return errors.NewError(err, code, metadata...)
}
32 changes: 32 additions & 0 deletions app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package pitaya

import (
"errors"
"fmt"
"net"
"os"
Expand All @@ -34,6 +35,7 @@ import (
"github.com/topfreegames/pitaya/acceptor"
"github.com/topfreegames/pitaya/cluster"
"github.com/topfreegames/pitaya/constants"
e "github.com/topfreegames/pitaya/errors"
"github.com/topfreegames/pitaya/helpers"
"github.com/topfreegames/pitaya/internal/codec"
"github.com/topfreegames/pitaya/internal/message"
Expand Down Expand Up @@ -339,3 +341,33 @@ func TestStartAndListenCluster(t *testing.T) {
return err
}, nil, 10*time.Millisecond, 100*time.Millisecond)
}

func TestError(t *testing.T) {
t.Parallel()

tables := []struct {
name string
err error
code string
metadata map[string]string
}{
{"nil_metadata", errors.New(uuid.New().String()), uuid.New().String(), nil},
{"empty_metadata", errors.New(uuid.New().String()), uuid.New().String(), map[string]string{}},
{"non_empty_metadata", errors.New(uuid.New().String()), uuid.New().String(), map[string]string{"key": uuid.New().String()}},
}

for _, table := range tables {
t.Run(table.name, func(t *testing.T) {
var err *e.Error
if table.metadata != nil {
err = Error(table.err, table.code, table.metadata)
} else {
err = Error(table.err, table.code)
}
assert.NotNil(t, err)
assert.Equal(t, table.code, err.Code)
assert.Equal(t, table.err.Error(), err.Message)
assert.Equal(t, table.metadata, err.Metadata)
})
}
}
4 changes: 2 additions & 2 deletions cluster/nats_rpc_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ func TestNatsRPCClientCall(t *testing.T) {
{"test_error", &protos.Response{Data: []byte("nok"), Error: "nok"}, nil, errors.New("nok")},
{"test_ok", &protos.Response{Data: []byte("ok")}, &protos.Response{Data: []byte("ok")}, nil},
{"test_bad_response", []byte("invalid"), nil, errors.New("unexpected EOF")},
{"test_bad_proto", &protos.ErrorPayload{Code: 400, Reason: "snap"}, nil, errors.New("proto: wrong wireType = 0 for field Data")},
{"test_bad_proto", &protos.Session{ID: 1, Uid: "snap"}, nil, errors.New("proto: bad wiretype for field protos.Response.Data: got wiretype 0, want 2")},
{"test_no_response", nil, nil, errors.New("nats: timeout")},
}

Expand All @@ -321,7 +321,7 @@ func TestNatsRPCClientCall(t *testing.T) {
if val, ok := table.response.(*protos.Response); ok {
b, _ := proto.Marshal(val)
conn.Publish(m.Reply, b)
} else if val, ok := table.response.(*protos.ErrorPayload); ok {
} else if val, ok := table.response.(*protos.Session); ok {
b, _ := proto.Marshal(val)
conn.Publish(m.Reply, b)
} else {
Expand Down
11 changes: 8 additions & 3 deletions component/method.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ func isRemoteMethod(method reflect.Method) bool {
return false
}

// Validate it is not a handler method
if isHandlerMethod(method) {
return false
}

return true
}

Expand All @@ -82,12 +87,12 @@ func isHandlerMethod(method reflect.Method) bool {
return false
}

// Method needs either no out or one out: interface{}(or []byte)
if mt.NumOut() != 0 && mt.NumOut() != 1 {
// Method needs either no out or two outs: interface{}(or []byte), error
if mt.NumOut() != 0 && mt.NumOut() != 2 {
return false
}

if mt.NumOut() == 1 && mt.Out(0) != typeOfBytes && mt.Out(0).Kind() != reflect.Ptr {
if mt.NumOut() == 2 && (mt.Out(1) != typeOfError || mt.Out(0) != typeOfBytes && mt.Out(0).Kind() != reflect.Ptr) {
return false
}

Expand Down
8 changes: 4 additions & 4 deletions component/method_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ type TestType struct {
func (t *TestType) ExportedNoHandlerNorRemote() {}
func (t *TestType) ExportedHandlerWithOnlySession(*session.Session) {}
func (t *TestType) ExportedHandlerWithSessionAndRawWithNoOuts(s *session.Session, msg []byte) {}
func (t *TestType) ExportedHandlerWithSessionAndPointerWithRawOut(s *session.Session, tt *TestType) []byte {
return nil
func (t *TestType) ExportedHandlerWithSessionAndPointerWithRawOut(s *session.Session, tt *TestType) ([]byte, error) {
return nil, nil
}
func (t *TestType) ExportedHandlerWithSessionAndPointerWithPointerOut(s *session.Session, tt *TestType) *TestType {
return nil
func (t *TestType) ExportedHandlerWithSessionAndPointerWithPointerOut(s *session.Session, tt *TestType) (*TestType, error) {
return nil, nil
}
func (t *TestType) ExportedRemoteRawOut() ([]byte, error) {
return nil, nil
Expand Down
49 changes: 49 additions & 0 deletions errors/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) nano Author and TFG Co. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package errors

// ErrUnknowCode is a string code representing an unknown error
// This will be used when no error code is sent by the handler
const ErrUnknowCode = "PIT-000"

// Error is an error with a code, message and metadata
type Error struct {
Code string
Message string
Metadata map[string]string
}

//NewError ctor
func NewError(err error, code string, metadata ...map[string]string) *Error {
e := &Error{
Code: code,
Message: err.Error(),
}
if len(metadata) > 0 {
e.Metadata = metadata[0]
}
return e

}

func (e *Error) Error() string {
return e.Message
}
69 changes: 69 additions & 0 deletions errors/errors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) TFG Co. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package errors

import (
"errors"
"testing"

"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)

func TestNewError(t *testing.T) {
t.Parallel()

tables := []struct {
name string
err error
code string
metadata map[string]string
}{
{"nil_metadata", errors.New(uuid.New().String()), uuid.New().String(), nil},
{"empty_metadata", errors.New(uuid.New().String()), uuid.New().String(), map[string]string{}},
{"non_empty_metadata", errors.New(uuid.New().String()), uuid.New().String(), map[string]string{"key": uuid.New().String()}},
}

for _, table := range tables {
t.Run(table.name, func(t *testing.T) {
var err *Error
if table.metadata != nil {
err = NewError(table.err, table.code, table.metadata)
} else {
err = NewError(table.err, table.code)
}
assert.NotNil(t, err)
assert.Equal(t, table.code, err.Code)
assert.Equal(t, table.err.Error(), err.Message)
assert.Equal(t, table.metadata, err.Metadata)
})
}
}

func TestErrorError(t *testing.T) {
t.Parallel()

sourceErr := errors.New(uuid.New().String())
err := NewError(sourceErr, uuid.New().String())

errStr := err.Error()
assert.Equal(t, sourceErr.Error(), errStr)
}
9 changes: 4 additions & 5 deletions examples/demo/chat/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,13 @@ func (r *Room) AfterInit() {
}

// Join room
func (r *Room) Join(s *session.Session, msg []byte) *protos.JoinResponse {
func (r *Room) Join(s *session.Session, msg []byte) (*protos.JoinResponse, error) {
res := &protos.JoinResponse{}
fakeUID := s.ID() //just use s.ID as uid !!!
fakeUID := s.ID() // just use s.ID as uid !!!
err := s.Bind(strconv.Itoa(int(fakeUID))) // binding session uid

if err != nil {
res.Result = err.Error()
return res
return nil, pitaya.Error(err, "RH-000", map[string]string{"failed": "bind"})
}

s.Push("onMembers", &protos.AllMembers{Members: r.group.Members()})
Expand All @@ -86,7 +85,7 @@ func (r *Room) Join(s *session.Session, msg []byte) *protos.JoinResponse {
})

res.Result = "success"
return res
return res, nil
}

// Message sync last message to all members
Expand Down
12 changes: 6 additions & 6 deletions examples/demo/cluster/services/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,27 +28,27 @@ type Response struct {
Msg string
}

func reply(code int32, msg string) *Response {
func reply(code int32, msg string) (*Response, error) {
res := &Response{
Code: code,
Msg: msg,
}
return res
return res, nil
}

// GetSessionData gets the session data
func (c *Connector) GetSessionData(s *session.Session) *SessionData {
func (c *Connector) GetSessionData(s *session.Session) (*SessionData, error) {
res := &SessionData{
Data: s.GetData(),
}
return res
return res, nil
}

// SetSessionData sets the session data
func (c *Connector) SetSessionData(s *session.Session, data *SessionData) *Response {
func (c *Connector) SetSessionData(s *session.Session, data *SessionData) (*Response, error) {
err := s.SetData(data.Data)
if err != nil {
return reply(500, err.Error())
return nil, err
}
return reply(200, "success")
}
Expand Down
32 changes: 13 additions & 19 deletions examples/demo/cluster/services/room.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,56 +100,50 @@ func (r *Room) AfterInit() {
}

// Entry is the entrypoint
func (r *Room) Entry(s *session.Session, msg []byte) *JoinResponse {
func (r *Room) Entry(s *session.Session, msg []byte) (*JoinResponse, error) {
fakeUID := uuid.New().String() // just use s.ID as uid !!!
err := s.Bind(fakeUID) // binding session uid
if err != nil {
return &JoinResponse{Result: err.Error()}
return nil, err
}
return &JoinResponse{Result: "ok"}
return &JoinResponse{Result: "ok"}, nil
}

// GetSessionData gets the session data
func (r *Room) GetSessionData(s *session.Session) *SessionData {
func (r *Room) GetSessionData(s *session.Session) (*SessionData, error) {
return &SessionData{
Data: s.GetData(),
}
}, nil
}

// SetSessionData sets the session data
func (r *Room) SetSessionData(s *session.Session, data *SessionData) []byte {
func (r *Room) SetSessionData(s *session.Session, data *SessionData) ([]byte, error) {
err := s.SetData(data.Data)
if err != nil {
return []byte(err.Error())
return nil, err
}
err = s.PushToFront()
if err != nil {
return []byte(err.Error())
return nil, err
}
return []byte("success")
return []byte("success"), nil
}

// Join room
func (r *Room) Join(s *session.Session) *JoinResponse {
func (r *Room) Join(s *session.Session) (*JoinResponse, error) {
err := r.group.Add(s)
if err != nil {
return &JoinResponse{
Code: 500,
Result: err.Error(),
}
return nil, err
}
s.Push("onMembers", &AllMembers{Members: r.group.Members()})
r.group.Broadcast("onNewUser", &NewUser{Content: fmt.Sprintf("New user: %d", s.ID())})
err = s.OnClose(func() {
r.group.Leave(s)
})
if err != nil {
return &JoinResponse{
Code: 500,
Result: err.Error(),
}
return nil, err
}
return &JoinResponse{Result: "success"}
return &JoinResponse{Result: "success"}, nil
}

// Message sync last message to all members
Expand Down
Loading

0 comments on commit ccb3af9

Please sign in to comment.