Skip to content

Commit ae6b06c

Browse files
authored
Revert "Enhance errors with multiple and custom errors (gofr-dev#797)" (gofr-dev#838)
This reverts commit 1fe0a57.
1 parent 2a90407 commit ae6b06c

File tree

7 files changed

+44
-173
lines changed

7 files changed

+44
-173
lines changed

docs/advanced-guide/gofr-errors/page.md

-14
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,3 @@ func (c customError) StatusCode() int {
5858
return http.StatusMethodNotAllowed
5959
}
6060
```
61-
62-
GoFr also provides a `CustomError` struct to create a custom error type with specific status code, reason, and details (optional)
63-
64-
#### Usage:
65-
```go
66-
customErr := gofrHTTP.CustomError{Code: http.StatusUnauthorized, Reason: "request unauthorized", Details: []string{"invalid credentials"}}
67-
```
68-
69-
GoFr also provides a `MultipleErrors` struct to pass multiple errors with a same status code.
70-
71-
#### Usage:
72-
```go
73-
multipleErr := gofrHTTP.MultipleErrors{Code: http.StatusBadRequest, Errors: []error{gofrHTTP.ErrorInvalidParam{}, gofrHTTP.ErrorMissingParam{}}}
74-
```

examples/http-server/main_test.go

+9-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package main
33
import (
44
"context"
55
"encoding/json"
6-
"fmt"
76
"io"
87
"net/http"
98
"testing"
@@ -16,7 +15,6 @@ import (
1615
"gofr.dev/pkg/gofr/config"
1716
"gofr.dev/pkg/gofr/container"
1817
"gofr.dev/pkg/gofr/datasource/redis"
19-
gofrHTTP "gofr.dev/pkg/gofr/http"
2018
"gofr.dev/pkg/gofr/logging"
2119
"gofr.dev/pkg/gofr/testutil"
2220
)
@@ -58,6 +56,8 @@ func TestIntegration_SimpleAPIServer(t *testing.T) {
5856

5957
assert.Equal(t, tc.body, data.Data, "TEST[%d], Failed.\n%s", i, tc.desc)
6058

59+
assert.NoError(t, err, "TEST[%d], Failed.\n%s", i, tc.desc)
60+
6161
assert.Equal(t, http.StatusOK, resp.StatusCode, "TEST[%d], Failed.\n%s", i, tc.desc)
6262

6363
resp.Body.Close()
@@ -75,19 +75,19 @@ func TestIntegration_SimpleAPIServer_Errors(t *testing.T) {
7575
desc: "error handler called",
7676
path: "/error",
7777
statusCode: http.StatusInternalServerError,
78-
body: "some error occurred",
78+
body: map[string]interface{}{"message": "some error occurred"},
7979
},
8080
{
8181
desc: "empty route",
8282
path: "/",
8383
statusCode: http.StatusNotFound,
84-
body: gofrHTTP.ErrorInvalidRoute{}.Error(),
84+
body: map[string]interface{}{"message": "route not registered"},
8585
},
8686
{
8787
desc: "route not registered with the server",
8888
path: "/route",
8989
statusCode: http.StatusNotFound,
90-
body: gofrHTTP.ErrorInvalidRoute{}.Error(),
90+
body: map[string]interface{}{"message": "route not registered"},
9191
},
9292
}
9393

@@ -99,7 +99,7 @@ func TestIntegration_SimpleAPIServer_Errors(t *testing.T) {
9999
resp, err := c.Do(req)
100100

101101
var data = struct {
102-
Errors interface{} `json:"errors"`
102+
Error interface{} `json:"error"`
103103
}{}
104104

105105
b, err := io.ReadAll(resp.Body)
@@ -108,7 +108,9 @@ func TestIntegration_SimpleAPIServer_Errors(t *testing.T) {
108108

109109
_ = json.Unmarshal(b, &data)
110110

111-
assert.Contains(t, fmt.Sprintf("%v", data.Errors), tc.body, "TEST[%d], Failed.\n%s", i, tc.desc)
111+
assert.Equal(t, tc.body, data.Error, "TEST[%d], Failed.\n%s", i, tc.desc)
112+
113+
assert.NoError(t, err, "TEST[%d], Failed.\n%s", i, tc.desc)
112114

113115
assert.Equal(t, tc.statusCode, resp.StatusCode, "TEST[%d], Failed.\n%s", i, tc.desc)
114116

pkg/gofr/handler_test.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func TestHandler_ServeHTTP(t *testing.T) {
3434
{"method is get, data is nil and error is nil", http.MethodGet, nil, nil, http.StatusOK,
3535
`{}`},
3636
{"method is get, data is mil, error is not nil", http.MethodGet, nil, errTest, http.StatusInternalServerError,
37-
`{"errors":[{"reason":"some error"`},
37+
`{"error":{"message":"some error"}}`},
3838
{"method is post, data is nil and error is nil", http.MethodPost, "Created", nil, http.StatusCreated,
3939
`{"data":"Created"}`},
4040
{"method is delete, data is nil and error is nil", http.MethodDelete, nil, nil, http.StatusNoContent,
@@ -56,7 +56,6 @@ func TestHandler_ServeHTTP(t *testing.T) {
5656
}.ServeHTTP(w, r)
5757

5858
assert.Containsf(t, w.Body.String(), tc.body, "TEST[%d], Failed.\n%s", i, tc.desc)
59-
6059
assert.Equal(t, tc.statusCode, w.Code, "TEST[%d], Failed.\n%s", i, tc.desc)
6160
}
6261
}

pkg/gofr/http/errors.go

-33
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,6 @@ import (
77
"strings"
88
)
99

10-
type MultipleErrors struct {
11-
Code int
12-
Errors []error
13-
}
14-
15-
func (m MultipleErrors) Error() string {
16-
var result string
17-
18-
for _, v := range m.Errors {
19-
result += fmt.Sprintf("%s\n", v)
20-
}
21-
22-
return strings.TrimSuffix(result, "\n")
23-
}
24-
25-
func (m MultipleErrors) StatusCode() int {
26-
return m.Code
27-
}
28-
29-
type CustomError struct {
30-
Code int
31-
Reason string
32-
Details interface{}
33-
}
34-
35-
func (c CustomError) Error() string {
36-
return c.Reason
37-
}
38-
39-
func (c CustomError) StatusCode() int {
40-
return c.Code
41-
}
42-
4310
const alreadyExistsMessage = "entity already exists"
4411

4512
// ErrorEntityNotFound represents an error for when an entity is not found in the system.

pkg/gofr/http/errors_test.go

-16
Original file line numberDiff line numberDiff line change
@@ -111,19 +111,3 @@ func Test_ErrorErrorPanicRecovery(t *testing.T) {
111111

112112
assert.Equal(t, http.StatusInternalServerError, err.StatusCode(), "TEST Failed.\n")
113113
}
114-
115-
func Test_MultipleErrors(t *testing.T) {
116-
err := MultipleErrors{Code: http.StatusNotFound, Errors: []error{ErrorInvalidRoute{}}}
117-
118-
assert.Equal(t, "route not registered", err.Error(), "TEST Failed.\n")
119-
120-
assert.Equal(t, http.StatusNotFound, err.StatusCode(), "TEST Failed.\n")
121-
}
122-
123-
func Test_CustomError(t *testing.T) {
124-
err := CustomError{Code: http.StatusUnauthorized, Reason: "request unauthorized"}
125-
126-
assert.Equal(t, "request unauthorized", err.Error(), "TEST Failed.\n")
127-
128-
assert.Equal(t, http.StatusUnauthorized, err.StatusCode(), "TEST Failed.\n")
129-
}

pkg/gofr/http/responder.go

+23-70
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ package http
22

33
import (
44
"encoding/json"
5-
"errors"
65
"net/http"
7-
"time"
86

97
resTypes "gofr.dev/pkg/gofr/http/response"
108
)
@@ -14,34 +12,16 @@ func NewResponder(w http.ResponseWriter, method string) *Responder {
1412
return &Responder{w: w, method: method}
1513
}
1614

17-
// Responder encapsulates a http.ResponseWriter and is responsible for crafting structured responses.
15+
// Responder encapsulates an http.ResponseWriter and is responsible for crafting structured responses.
1816
type Responder struct {
1917
w http.ResponseWriter
2018
method string
2119
}
2220

23-
// response represents an HTTP response.
24-
type response struct {
25-
Data interface{} `json:"data,omitempty"`
26-
Errors []errResponse `json:"errors,omitempty"`
27-
}
28-
29-
type errResponse struct {
30-
Reason string `json:"reason"`
31-
Details interface{} `json:"details,omitempty"`
32-
DateTime time.Time `json:"datetime"`
33-
}
34-
35-
type statusCodeResponder interface {
36-
StatusCode() int
37-
Error() string
38-
}
39-
4021
// Respond sends a response with the given data and handles potential errors, setting appropriate
4122
// status codes and formatting responses as JSON or raw data as needed.
4223
func (r Responder) Respond(data interface{}, err error) {
43-
statusCode := getStatusCode(r.method, data, err)
44-
errObj := getErrResponse(err)
24+
statusCode, errorObj := getStatusCode(r.method, data, err)
4525

4626
var resp interface{}
4727
switch v := data.(type) {
@@ -56,8 +36,8 @@ func (r Responder) Respond(data interface{}, err error) {
5636
return
5737
default:
5838
resp = response{
59-
Data: v,
60-
Errors: errObj,
39+
Data: v,
40+
Error: errorObj,
6141
}
6242
}
6343

@@ -69,67 +49,40 @@ func (r Responder) Respond(data interface{}, err error) {
6949
}
7050

7151
// getStatusCode returns corresponding HTTP status codes.
72-
func getStatusCode(method string, data interface{}, err error) (status int) {
52+
func getStatusCode(method string, data interface{}, err error) (status int, errObj interface{}) {
7353
if err == nil {
7454
switch method {
7555
case http.MethodPost:
7656
if data != nil {
77-
return http.StatusCreated
57+
return http.StatusCreated, nil
7858
}
7959

80-
return http.StatusAccepted
60+
return http.StatusAccepted, nil
8161
case http.MethodDelete:
82-
return http.StatusNoContent
62+
return http.StatusNoContent, nil
8363
default:
84-
return http.StatusOK
64+
return http.StatusOK, nil
8565
}
8666
}
8767

88-
var e statusCodeResponder
89-
if errors.As(err, &e) {
90-
if data != nil {
91-
return http.StatusPartialContent
68+
e, ok := err.(statusCodeResponder)
69+
if ok {
70+
return e.StatusCode(), map[string]interface{}{
71+
"message": err.Error(),
9272
}
93-
94-
status = e.StatusCode()
95-
96-
if e.StatusCode() == 0 {
97-
return http.StatusInternalServerError
98-
}
99-
100-
return status
10173
}
10274

103-
return http.StatusInternalServerError
75+
return http.StatusInternalServerError, map[string]interface{}{
76+
"message": err.Error(),
77+
}
10478
}
10579

106-
func getErrResponse(err error) []errResponse {
107-
var (
108-
errResp []errResponse
109-
m MultipleErrors
110-
c CustomError
111-
)
112-
113-
if err != nil {
114-
switch {
115-
case errors.As(err, &m):
116-
for _, v := range m.Errors {
117-
resp := errResponse{Reason: v.Error(), DateTime: time.Now()}
118-
119-
if errors.As(v, &c) {
120-
resp.Details = c.Details
121-
}
122-
123-
errResp = append(errResp, resp)
124-
}
125-
case errors.As(err, &c):
126-
errResp = append(errResp, errResponse{Reason: c.Reason, Details: c.Details, DateTime: time.Now()})
127-
default:
128-
errResp = append(errResp, errResponse{Reason: err.Error(), DateTime: time.Now()})
129-
}
130-
131-
return errResp
132-
}
80+
// response represents an HTTP response.
81+
type response struct {
82+
Error interface{} `json:"error,omitempty"`
83+
Data interface{} `json:"data,omitempty"`
84+
}
13385

134-
return nil
86+
type statusCodeResponder interface {
87+
StatusCode() int
13588
}

pkg/gofr/http/responder_test.go

+11-31
Original file line numberDiff line numberDiff line change
@@ -38,43 +38,23 @@ func TestResponder_getStatusCode(t *testing.T) {
3838
data interface{}
3939
err error
4040
statusCode int
41+
errObj interface{}
4142
}{
42-
{"success case", http.MethodGet, "success response", nil, http.StatusOK},
43-
{"post with response body", http.MethodPost, "entity created", nil, http.StatusCreated},
44-
{"post with nil response", http.MethodPost, nil, nil, http.StatusAccepted},
45-
{"success delete", http.MethodDelete, nil, nil, http.StatusNoContent},
46-
{"invalid route error", http.MethodGet, nil, ErrorInvalidRoute{}, http.StatusNotFound},
47-
{"internal server error", http.MethodGet, nil, http.ErrHandlerTimeout, http.StatusInternalServerError},
43+
{"success case", http.MethodGet, "success response", nil, http.StatusOK, nil},
44+
{"post with response body", http.MethodPost, "entity created", nil, http.StatusCreated, nil},
45+
{"post with nil response", http.MethodPost, nil, nil, http.StatusAccepted, nil},
46+
{"success delete", http.MethodDelete, nil, nil, http.StatusNoContent, nil},
47+
{"invalid route error", http.MethodGet, nil, ErrorInvalidRoute{}, http.StatusNotFound,
48+
map[string]interface{}{"message": ErrorInvalidRoute{}.Error()}},
49+
{"internal server error", http.MethodGet, nil, http.ErrHandlerTimeout, http.StatusInternalServerError,
50+
map[string]interface{}{"message": http.ErrHandlerTimeout.Error()}},
4851
}
4952

5053
for i, tc := range tests {
51-
statusCode := getStatusCode(tc.method, tc.data, tc.err)
54+
statusCode, errObj := getStatusCode(tc.method, tc.data, tc.err)
5255

5356
assert.Equal(t, tc.statusCode, statusCode, "TEST[%d], Failed.\n%s", i, tc.desc)
54-
}
55-
}
56-
57-
func TestResponder_getErrResponse(t *testing.T) {
58-
tests := []struct {
59-
desc string
60-
err error
61-
reason []string
62-
details interface{}
63-
}{
64-
{"success case", nil, nil, nil},
65-
{"invalid param error", ErrorInvalidParam{}, []string{ErrorInvalidParam{}.Error()}, nil},
66-
{"multiple errors", MultipleErrors{Errors: []error{ErrorMissingParam{}, CustomError{Reason: ErrorEntityAlreadyExist{}.Error()}}},
67-
[]string{ErrorMissingParam{}.Error(), CustomError{Reason: alreadyExistsMessage}.Error()}, nil},
68-
{"custom error", CustomError{Reason: ErrorEntityNotFound{}.Error()}, []string{ErrorEntityNotFound{}.Error()}, nil},
69-
}
70-
71-
for i, tc := range tests {
72-
errObj := getErrResponse(tc.err)
73-
74-
for j, err := range errObj {
75-
assert.Equal(t, tc.reason[j], err.Reason, "TEST[%d], Failed.\n%s", i, tc.desc)
7657

77-
assert.Equal(t, tc.details, err.Details, "TEST[%d], Failed.\n%s", i, tc.desc)
78-
}
58+
assert.Equal(t, tc.errObj, errObj, "TEST[%d], Failed.\n%s", i, tc.desc)
7959
}
8060
}

0 commit comments

Comments
 (0)