Skip to content

Commit 8389419

Browse files
committed
chore: fix and add unit test
1 parent a23c1cd commit 8389419

File tree

5 files changed

+280
-42
lines changed

5 files changed

+280
-42
lines changed

pkg/config/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"github.com/spf13/viper"
1111
)
1212

13+
// NewConfig return a viper.Viper instance from given configuration details
14+
// Configuration details consist of the configuration file path, configuration file name, also the env prefix if available
1315
func NewConfig(configPath, configName, envPrefix string) *viper.Viper {
1416
fang := viper.New()
1517

pkg/errs/errs.go renamed to pkg/errs/error.go

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -65,28 +65,34 @@ func (e *Error) Cause() error {
6565
}
6666

6767
func (e Error) Unwrap() error {
68-
return e.Err
68+
return errs.Unwrap(e.Err)
6969
}
7070

7171
func (e *Error) Error() string {
7272
return e.Err.Error()
7373
}
7474

75-
// StackTrace satisfy errors.StackTrace interface
76-
func (e Error) StackTrace() errors.StackTrace {
77-
type stackTracer interface {
78-
StackTrace() errors.StackTrace
79-
}
80-
81-
if st, ok := e.Err.(stackTracer); ok {
82-
return st.StackTrace()
75+
func (e *Error) Format(s fmt.State, verb rune) {
76+
switch verb {
77+
case 'v':
78+
if s.Flag('+') {
79+
if st, ok := e.Err.(interface {
80+
StackTrace() errors.StackTrace
81+
}); ok {
82+
for _, fr := range st.StackTrace()[1:] {
83+
fmt.Fprintf(s, "\n%+v", fr)
84+
}
85+
return
86+
}
87+
}
88+
fallthrough
89+
case 's':
90+
fmt.Fprintf(s, "%s", e.Error())
8391
}
84-
85-
return nil
8692
}
8793

8894
func (e *Error) isZero() bool {
89-
return e.Err == nil &&
95+
return e.Is(ErrUndefined) &&
9096
e.User == "" &&
9197
e.Param == "" &&
9298
e.Code == ""
@@ -145,6 +151,7 @@ func (k Kind) String() string {
145151
}
146152

147153
var DefaultRealm Realm = "restricted"
154+
var ErrUndefined = errors.New("undefined error")
148155

149156
// E builds an error value from its arguments.
150157
// There must be at least one argument or E panics.
@@ -153,28 +160,30 @@ var DefaultRealm Realm = "restricted"
153160
// only the last one is recorded.
154161
//
155162
// The types are:
156-
// errs.Kind
157-
// The class of error, such as permission failure.
158163
// errs.UserName
159164
// The username of the user attempting the operation.
165+
// errs.Kind
166+
// The class of error, such as permission failure.
167+
// errs.Code
168+
// The code for a human-readable purpose about the error.
169+
// errs.Parameter
170+
// The parameter represent the parameter related with the error.
160171
// string
161172
// Treated as an error message and assigned to the
162173
// Err field after a call to errors.New.
163174
// error
164-
// The underlying error that triggered this one.
175+
// The underlying error that triggered this one, if the error not contains stack,
176+
// we will wrap it
165177
//
166178
// If the error is printed, only those items that have been
167179
// set to non-zero values will appear in the result.
168180
//
169181
// If Kind is not specified or Other, we set it to the Kind of
170182
// the underlying error.
171183
func E(args ...interface{}) error {
172-
type stackTracer interface {
173-
StackTrace() errors.StackTrace
174-
}
175184

176185
if len(args) == 0 {
177-
panic("call to errors.E with no arguments")
186+
panic("call to errs.E with no arguments")
178187
}
179188

180189
e := &Error{}
@@ -204,7 +213,9 @@ func E(args ...interface{}) error {
204213
// if the error implements stackTracer, then it is
205214
// a pkg/errors error type and does not need to have
206215
// the stack added
207-
_, ok := arg.(stackTracer)
216+
_, ok := arg.(interface {
217+
StackTrace() errors.StackTrace
218+
})
208219
if ok {
209220
e.Err = arg
210221
} else {
@@ -220,7 +231,11 @@ func E(args ...interface{}) error {
220231
// If this error and the inner still has Realm == "", while error Kind is Unauthenticated
221232
// then the realm set to default "restricted" method
222233
if e.Realm == "" && e.Kind == Unauthenticated {
223-
e.Realm = "restricted"
234+
e.Realm = DefaultRealm
235+
}
236+
237+
if e.Err == nil {
238+
e.Err = ErrUndefined
224239
}
225240

226241
prev, ok := e.Err.(*Error)
@@ -255,8 +270,8 @@ func E(args ...interface{}) error {
255270
prev.Realm = ""
256271
}
257272

258-
// If this inner error hasn't Default Realm, then pull up the inner Realm
259-
if prev.Realm != DefaultRealm && e.Realm == DefaultRealm {
273+
// If this inner error has Realm, pull up the inner one
274+
if e.Realm == "" {
260275
e.Realm = prev.Realm
261276
prev.Realm = ""
262277
}

pkg/errs/errs_test.go renamed to pkg/errs/error_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package errs_test
22

33
import (
4+
"errors"
45
"fmt"
56
"testing"
67

@@ -29,6 +30,18 @@ func TestErrs(t *testing.T) {
2930
assert.Equal(t, "other_error", errs.Other.String())
3031
assert.Equal(t, "I/O_error", errs.IO.String())
3132
})
33+
34+
t.Run("Unwrap the error should return the unwrapped error", func(t *testing.T) {
35+
var errX = fmt.Errorf("error X")
36+
37+
err := errs.E(
38+
errs.Code("internal"),
39+
errs.Internal,
40+
fmt.Errorf("internal error from X: %w", errX),
41+
)
42+
assert.True(t, errors.Is(errors.Unwrap(err), errX))
43+
assert.True(t, errs.KindIs(errs.Internal, err))
44+
})
3245
}
3346

3447
func TestKindIs(t *testing.T) {

pkg/errs/http_error.go renamed to pkg/errs/httperror.go

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -81,16 +81,19 @@ func HTTPErrorHandler(w http.ResponseWriter, lgr zerolog.Logger, err error) {
8181

8282
func commonErrHandler(w http.ResponseWriter, lgr zerolog.Logger, e *Error) {
8383
if e.isZero() {
84-
lgr.Error().Stack().
85-
Str("kind", string(e.Kind)).
86-
Msg("empty error")
84+
lgr.Error().
85+
Stack().
86+
Str("kind", e.Kind.String()).
87+
Msg(e.Error())
8788

8889
w.WriteHeader(http.StatusInternalServerError)
8990
return
9091
}
9192

92-
lgr.Error().Stack().Err(e.Err).
93-
Str("kind", string(e.Kind)).
93+
lgr.Error().
94+
Stack().
95+
Err(e.Err).
96+
Str("kind", e.Kind.String()).
9497
Str("username", string(e.User)).
9598
Str("parameter", string(e.Param)).
9699
Str("code", string(e.Code)).
@@ -101,14 +104,14 @@ func commonErrHandler(w http.ResponseWriter, lgr zerolog.Logger, e *Error) {
101104
case Internal, Database, IO:
102105
errResponse = HTTPErrResponse{
103106
Error: &ServiceError{
104-
Kind: string(e.Kind),
107+
Kind: e.Kind.String(),
105108
Message: "internal server error",
106109
},
107110
}
108111
default:
109112
errResponse = HTTPErrResponse{
110113
Error: &ServiceError{
111-
Kind: string(e.Kind),
114+
Kind: e.Kind.String(),
112115
Code: string(e.Code),
113116
Param: string(e.Param),
114117
Message: e.Error(),
@@ -119,8 +122,8 @@ func commonErrHandler(w http.ResponseWriter, lgr zerolog.Logger, e *Error) {
119122
errJSON, _ := json.Marshal(errResponse)
120123
w.Header().Set("Content-Type", "application/json")
121124
w.Header().Set("X-Content-Type-Options", "nosniff")
122-
w.Write(errJSON)
123125
w.WriteHeader(HTTPStatusCodeFromError(e))
126+
w.Write(errJSON)
124127
}
125128

126129
func validationErrHandler(w http.ResponseWriter, lgr zerolog.Logger, e *Error) {
@@ -137,29 +140,31 @@ func validationErrHandler(w http.ResponseWriter, lgr zerolog.Logger, e *Error) {
137140
Int("fields", len(verr)).
138141
Msg("input validation error")
139142

140-
var errFields []ServiceError
143+
var errs []ServiceError
141144
for _, err := range verr {
142145
ie, ok := err.(*Error)
143146
if !ok {
147+
lgr.Error().
148+
Stack().
149+
Err(err).
150+
Msg("input validation error - unexpected error")
144151
continue
145152
}
146153

147-
errFields = append(errFields, ServiceError{
154+
errs = append(errs, ServiceError{
148155
Code: string(ie.Code),
149156
Param: string(ie.Param),
150157
Message: ie.Error(),
151158
})
152159
}
153160

154-
var errResponse = HTTPErrResponse{
155-
Errors: errFields,
156-
}
157-
158-
errJSON, _ := json.Marshal(errResponse)
161+
errJSON, _ := json.Marshal(HTTPErrResponse{
162+
Errors: errs,
163+
})
159164
w.Header().Set("Content-Type", "application/json")
160165
w.Header().Set("X-Content-Type-Options", "nosniff")
161-
w.Write(errJSON)
162166
w.WriteHeader(HTTPStatusCodeFromError(e))
167+
w.Write(errJSON)
163168
}
164169

165170
func unauthenticatedErrHandler(w http.ResponseWriter, lgr zerolog.Logger, e *Error) {
@@ -193,21 +198,21 @@ func unknownErrHandler(w http.ResponseWriter, lgr zerolog.Logger, err error) {
193198
},
194199
}
195200

196-
lgr.Error().Stack().Err(err).Msg("unknown error")
201+
lgr.Error().Stack().Err(err).Int("code", HTTPStatusCodeFromError(err)).Msg("unknown error")
197202

198203
errJSON, _ := json.Marshal(errResponse)
199204
w.Header().Set("Content-Type", "application/json")
200205
w.Header().Set("X-Content-Type-Options", "nosniff")
206+
w.WriteHeader(http.StatusNotImplemented)
201207
w.Write(errJSON)
202-
w.WriteHeader(http.StatusInternalServerError)
203208
}
204209

205210
// HTTPStatusCodeFromError translate error to an http status code
206211
func HTTPStatusCodeFromError(err error) int {
207212

208213
var e *Error
209214
if !errors.As(err, &e) {
210-
return http.StatusInternalServerError
215+
return http.StatusNotImplemented
211216
}
212217

213218
switch e.Kind {

0 commit comments

Comments
 (0)