Skip to content
This repository was archived by the owner on May 19, 2020. It is now read-only.

Commit e1e739c

Browse files
committed
* Excluding v9 validator instance because gin already have it
* Some code refactorings of validating errors processing * Code cleaning * Tests and lint * Readme update
1 parent 477b03d commit e1e739c

File tree

8 files changed

+122
-126
lines changed

8 files changed

+122
-126
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
.idea
2-
vendor
2+
vendor
3+
.golangci.yml
4+
c.out
5+
coverage.html

Makefile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
deps:
2+
go mod vendor
3+
.PHONY: deps
4+
5+
get_lint_config:
6+
@[ -f ./.golangci.yml ] && echo ".golangci.yml exists" || ( echo "getting .golangci.yml" && curl -O https://raw.githubusercontent.com/microparts/docker-golang/master/lint/.golangci.yml )
7+
.PHONY: get_lint_config
8+
9+
lint: get_lint_config
10+
golangci-lint run
11+
.PHONY: lint
12+
13+
test-unit:
14+
go test $$(go list ./...) --race --cover -count=1 -timeout 1s -coverprofile=c.out -v
15+
.PHONY: test-unit
16+
17+
coverage-html:
18+
go tool cover -html=c.out -o coverage.html
19+
.PHONE: coverage-html
20+
21+
test: deps test-unit coverage-html
22+
.PHONY: test

README.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
errors-go-gin
2-
-------------
1+
# ginerrors
32

4-
errors for gin
3+
Smart generating error code and response for gin mux based on passed error.
4+
5+
## Usage
56

67
```go
78
package main
@@ -16,10 +17,23 @@ import (
1617
)
1718

1819
func main() {
19-
ginerrors.InitValidator()
2020
r := gin.New()
21+
2122
r.GET("/", func(c*gin.Context) {c.JSON(http.StatusOK,`{"status":"ok"}`)})
2223
r.GET("/err", func(c *gin.Context) { ginerrors.Response(c, errors.New("error")) })
2324
_ = r.Run(":8080")
2425
}
25-
```
26+
```
27+
28+
## Linter
29+
30+
Lint code with [golangci-lint](https://github.com/golangci/golangci-lint) and
31+
[custom config](https://github.com/microparts/docker-golang/blob/master/lint/.golangci.yml) for it:
32+
33+
make lint
34+
35+
## Testing
36+
37+
Test code with race checking and generation coverage profile:
38+
39+
make test

gin.go

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,32 @@ package ginerrors
22

33
import (
44
"database/sql"
5+
"errors"
6+
"fmt"
57
"net/http"
68
"strings"
79

810
"github.com/gin-gonic/gin"
9-
"github.com/pkg/errors"
1011
"gopkg.in/go-playground/validator.v9"
1112
)
1213

14+
type langName string
15+
type validationRule string
16+
type errorPattern string
17+
type validationError map[validationRule]errorPattern
18+
19+
func (ve errorPattern) string() string {
20+
return string(ve)
21+
}
22+
23+
var CommonValidationErrors = map[langName]validationError{
24+
"ru": {
25+
"ek": "Ошибка валидации для свойства `%s` с правилом `%s`",
26+
"required": "Свойство `%s` обязательно для заполнения",
27+
"gt": "Свойство `%s` должно содержать более `%s` элементов",
28+
},
29+
}
30+
1331
type ResponseObject struct {
1432
Error ErrorObject `json:"error,omitempty"`
1533
}
@@ -22,6 +40,8 @@ type ErrorObject struct {
2240
}
2341

2442
var (
43+
defaultLang = "ru"
44+
2545
ErrNotFound = errors.New("route not found")
2646
ErrNoMethod = errors.New("method not allowed")
2747
ErrServerError = errors.New("internal server error")
@@ -30,13 +50,22 @@ var (
3050

3151
//Response makes common error response
3252
func Response(c *gin.Context, err interface{}) {
33-
errCode, data := MakeResponse(err)
53+
errCode, data := MakeResponse(err, getLang(c))
3454
resp := ResponseObject{Error: *data}
3555
c.AbortWithStatusJSON(errCode, resp)
3656
}
3757

58+
func getLang(c *gin.Context) langName {
59+
lang := c.GetHeader("lang")
60+
if lang == "" {
61+
lang = c.DefaultQuery("lang", defaultLang)
62+
}
63+
64+
return langName(lang)
65+
}
66+
3867
//MakeResponse makes ErrorObject based on error type
39-
func MakeResponse(err interface{}) (int, *ErrorObject) {
68+
func MakeResponse(err interface{}, lang langName) (int, *ErrorObject) {
4069
errObj := &ErrorObject{}
4170
errCode := http.StatusBadRequest
4271

@@ -53,7 +82,7 @@ func MakeResponse(err interface{}) (int, *ErrorObject) {
5382
errCode = http.StatusUnprocessableEntity
5483

5584
errObj.Message = "validation error"
56-
errObj.Validation = MakeErrorsSlice(et)
85+
errObj.Validation = MakeErrorsSlice(et, lang)
5786

5887
case error:
5988
errCode, errObj.Message = getErrCode(et)
@@ -90,3 +119,45 @@ func getErrCode(et error) (errCode int, msg string) {
90119

91120
return
92121
}
122+
123+
// validationErrors Формирование массива ошибок
124+
func MakeErrorsSlice(err error, lang langName) map[string][]string {
125+
ve := map[string][]string{}
126+
for _, e := range err.(validator.ValidationErrors) {
127+
field := getFieldName(e.Namespace(), e.Field())
128+
if _, ok := ve[field]; !ok {
129+
ve[field] = []string{}
130+
}
131+
ve[field] = append(
132+
ve[field],
133+
getErrMessage(validationRule(e.ActualTag()), field, e.Param(), lang),
134+
)
135+
}
136+
return ve
137+
}
138+
func getFieldName(namespace string, field string) string {
139+
namespace = strings.Replace(namespace, "]", "", -1)
140+
namespace = strings.Replace(namespace, "[", ".", -1)
141+
namespaceSlice := strings.Split(namespace, ".")
142+
fieldName := field
143+
144+
if len(namespaceSlice) > 2 {
145+
fieldName = strings.Join([]string{strings.Join(namespaceSlice[1:len(namespaceSlice)-1], "."), field}, ".")
146+
}
147+
148+
return fieldName
149+
}
150+
151+
func getErrMessage(errorType validationRule, field string, param string, lang langName) string {
152+
errKey := errorType
153+
_, ok := CommonValidationErrors[lang][errorType]
154+
if !ok {
155+
errKey = "ek"
156+
}
157+
158+
if param != "" && errKey == "ek" {
159+
return fmt.Sprintf(CommonValidationErrors[lang][errKey].string(), field, errorType)
160+
}
161+
162+
return fmt.Sprintf(CommonValidationErrors[lang][errKey].string(), field)
163+
}

gin_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ package ginerrors
22

33
import (
44
"database/sql"
5+
"errors"
56
"fmt"
67
"net/http"
78
"net/http/httptest"
89
"testing"
910

1011
"github.com/gin-gonic/gin"
11-
"github.com/pkg/errors"
1212
"github.com/stretchr/testify/assert"
1313
"gopkg.in/go-playground/validator.v9"
1414
)
@@ -39,15 +39,14 @@ func TestMakeResponse(t *testing.T) {
3939
}
4040
for _, testCase := range cases {
4141
t.Run(testCase.name, func(t *testing.T) {
42-
errCode, errObject := MakeResponse(testCase.err)
42+
errCode, errObject := MakeResponse(testCase.err, "ru")
4343
assert.Equal(t, testCase.errObject, errObject, testCase.name)
4444
assert.Equal(t, testCase.httpCode, errCode, testCase.name)
4545
})
4646
}
4747
}
4848

4949
func setupRouter() *gin.Engine {
50-
InitValidator()
5150
r := gin.New()
5251

5352
r.NoRoute(func(c *gin.Context) { Response(c, ErrNotFound) })

gin_validator.go

Lines changed: 0 additions & 110 deletions
This file was deleted.

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ go 1.12
44

55
require (
66
github.com/gin-gonic/gin v1.4.1-0.20191002234641-4fd3234840db
7-
github.com/pkg/errors v0.8.1
87
github.com/stretchr/testify v1.4.0
98
gopkg.in/go-playground/validator.v9 v9.30.0
109
)

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OH
2222
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
2323
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
2424
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
25-
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
26-
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
2725
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
2826
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2927
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

0 commit comments

Comments
 (0)