From 8e66616db7f23072b4613cbbb833a919d463611d Mon Sep 17 00:00:00 2001 From: nibi8 Date: Wed, 11 May 2022 21:56:28 +0300 Subject: [PATCH] Error parser --- errorparser/error_parser.go | 18 +++++ errorparser/error_parser_test.go | 23 +++++++ errorparser/json_errors.go | 48 ++++++++++++++ errorparser/json_errors_test.go | 94 +++++++++++++++++++++++++++ errorparser/parse_error.go | 28 ++++++++ errorparser/validation_errors.go | 27 ++++++++ errorparser/validation_errors_test.go | 61 +++++++++++++++++ 7 files changed, 299 insertions(+) create mode 100644 errorparser/error_parser.go create mode 100644 errorparser/error_parser_test.go create mode 100644 errorparser/json_errors.go create mode 100644 errorparser/json_errors_test.go create mode 100644 errorparser/parse_error.go create mode 100644 errorparser/validation_errors.go create mode 100644 errorparser/validation_errors_test.go diff --git a/errorparser/error_parser.go b/errorparser/error_parser.go new file mode 100644 index 00000000..33a0bc54 --- /dev/null +++ b/errorparser/error_parser.go @@ -0,0 +1,18 @@ +package errorparser + +func ParseBindError(err error) (errs []ParseError, match bool) { + + if errs, ok := parseValidatorError(err); ok { + return errs, true + } + + if errs, ok := parseJsonDecodeError(err); ok { + return errs, true + } + + // todo: protobuf + // todo: xml + // todo: yaml + + return nil, false +} diff --git a/errorparser/error_parser_test.go b/errorparser/error_parser_test.go new file mode 100644 index 00000000..a3ec8d71 --- /dev/null +++ b/errorparser/error_parser_test.go @@ -0,0 +1,23 @@ +package errorparser + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/go-playground/validator/v10" + "github.com/stretchr/testify/assert" +) + +func TestParseBindError(t *testing.T) { + + _, ok := ParseBindError(fmt.Errorf("not match")) + assert.False(t, ok) + + _, ok = ParseBindError(validator.ValidationErrors([]validator.FieldError{})) + assert.True(t, ok) + + _, ok = ParseBindError(&json.SyntaxError{}) + assert.True(t, ok) + +} diff --git a/errorparser/json_errors.go b/errorparser/json_errors.go new file mode 100644 index 00000000..0092333d --- /dev/null +++ b/errorparser/json_errors.go @@ -0,0 +1,48 @@ +package errorparser + +import ( + "encoding/json" +) + +func parseJsonDecodeError(err error) (errs []ParseError, match bool) { + + if typeErr, ok := err.(*json.UnmarshalTypeError); ok { + return parseJsonUnmarshalTypeError(typeErr), true + } + + if syntaxErr, ok := err.(*json.SyntaxError); ok { + return parseJsonSyntaxError(syntaxErr), true + } + + return nil, false +} + +func parseJsonUnmarshalTypeError(err *json.UnmarshalTypeError) (errs []ParseError) { + + errs = []ParseError{} + + item := NewParseError( + err.Field, + ParseErrorTypeMismatch, + err, + ) + + errs = append(errs, item) + + return errs +} + +func parseJsonSyntaxError(err *json.SyntaxError) (errs []ParseError) { + + errs = []ParseError{} + + item := NewParseError( + "", + ParseErrorTypeBadInput, + err, + ) + + errs = append(errs, item) + + return errs +} diff --git a/errorparser/json_errors_test.go b/errorparser/json_errors_test.go new file mode 100644 index 00000000..345f002a --- /dev/null +++ b/errorparser/json_errors_test.go @@ -0,0 +1,94 @@ +package errorparser + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/gin-gonic/gin" +) + +func TestParseJsonDecodeError(t *testing.T) { + + _, ok := parseJsonDecodeError(fmt.Errorf("not match")) + assert.False(t, ok) + + _, ok = parseJsonDecodeError(&json.UnmarshalTypeError{}) + assert.True(t, ok) + + _, ok = parseJsonDecodeError(&json.SyntaxError{}) + assert.True(t, ok) + +} + +func TestParseJsonUnmarshalTypeError(t *testing.T) { + + jsonData := `{ + "text": "text", + "count": "1" + }` + + rbody := bytes.NewReader([]byte(jsonData)) + + c, _ := gin.CreateTestContext(httptest.NewRecorder()) + c.Request, _ = http.NewRequest("POST", "/", rbody) + c.Request.Header.Add("Content-Type", gin.MIMEJSON) + + var obj struct { + Text string `json:"text"` + Count int `json:"count"` + } + + err := c.Bind(&obj) + require.Error(t, err) + + typeErr, ok := err.(*json.UnmarshalTypeError) + require.True(t, ok) + + parseErrs := parseJsonUnmarshalTypeError(typeErr) + require.Equal(t, len(parseErrs), 1) + + assert.Equal(t, parseErrs[0].ParamName, "count") + assert.Equal(t, parseErrs[0].ErrorType, ParseErrorTypeMismatch) + assert.Equal(t, parseErrs[0].InitialError, err) + +} + +func TestParseJsonSyntaxError(t *testing.T) { + + jsonData := `{ + "text": "text" + "count": 1 + }` + + rbody := bytes.NewReader([]byte(jsonData)) + + c, _ := gin.CreateTestContext(httptest.NewRecorder()) + c.Request, _ = http.NewRequest("POST", "/", rbody) + c.Request.Header.Add("Content-Type", gin.MIMEJSON) + + var obj struct { + Text string `json:"text"` + Count int `json:"count"` + } + + err := c.Bind(&obj) + require.Error(t, err) + + typeErr, ok := err.(*json.SyntaxError) + require.True(t, ok) + + parseErrs := parseJsonSyntaxError(typeErr) + require.Equal(t, len(parseErrs), 1) + + assert.Equal(t, parseErrs[0].ParamName, "") + assert.Equal(t, parseErrs[0].ErrorType, ParseErrorTypeBadInput) + assert.Equal(t, parseErrs[0].InitialError, err) + +} diff --git a/errorparser/parse_error.go b/errorparser/parse_error.go new file mode 100644 index 00000000..bf936226 --- /dev/null +++ b/errorparser/parse_error.go @@ -0,0 +1,28 @@ +package errorparser + +type ParseError struct { + ParamName string + ErrorType ParseErrorType + InitialError error +} + +func NewParseError( + paramName string, + errorType ParseErrorType, + initialError error, +) ParseError { + return ParseError{ + ParamName: paramName, + ErrorType: errorType, + InitialError: initialError, + } +} + +type ParseErrorType string + +const ( + ParseErrorTypeNone ParseErrorType = "" + ParseErrorTypeBadInput ParseErrorType = "bad_input" + ParseErrorTypeMismatch ParseErrorType = "type_mismatch" + ParseErrorTypeValidation ParseErrorType = "validation" +) diff --git a/errorparser/validation_errors.go b/errorparser/validation_errors.go new file mode 100644 index 00000000..24ab7f56 --- /dev/null +++ b/errorparser/validation_errors.go @@ -0,0 +1,27 @@ +package errorparser + +import ( + "github.com/go-playground/validator/v10" +) + +func parseValidatorError(err error) (errs []ParseError, match bool) { + if vErr, ok := err.(validator.ValidationErrors); ok { + return parseValidatorValidationErrors(vErr), true + } + return nil, false +} + +func parseValidatorValidationErrors(vErr validator.ValidationErrors) (errs []ParseError) { + fErrs := []validator.FieldError(vErr) + errs = make([]ParseError, 0, len(fErrs)) + for _, fErr := range fErrs { + item := NewParseError( + fErr.Field(), + ParseErrorTypeValidation, + fErr, + ) + + errs = append(errs, item) + } + return errs +} diff --git a/errorparser/validation_errors_test.go b/errorparser/validation_errors_test.go new file mode 100644 index 00000000..a2eac969 --- /dev/null +++ b/errorparser/validation_errors_test.go @@ -0,0 +1,61 @@ +package errorparser + +import ( + "bytes" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/go-playground/validator/v10" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/gin-gonic/gin" +) + +func TestParseValidatorError(t *testing.T) { + + _, ok := parseValidatorError(fmt.Errorf("not match")) + assert.False(t, ok) + + _, ok = parseValidatorError(validator.ValidationErrors{}) + assert.True(t, ok) + +} + +func TestParseValidatorValidationErrors(t *testing.T) { + + jsonData := `{ + "text": "", + "count": 1 + }` + + rbody := bytes.NewReader([]byte(jsonData)) + + c, _ := gin.CreateTestContext(httptest.NewRecorder()) + c.Request, _ = http.NewRequest("POST", "/", rbody) + c.Request.Header.Add("Content-Type", gin.MIMEJSON) + + var obj struct { + Text string `json:"text" binding:"required"` + Count int `json:"count"` + } + + err := c.Bind(&obj) + require.Error(t, err) + + vErr, ok := err.(validator.ValidationErrors) + require.True(t, ok) + + fErrs := []validator.FieldError(vErr) + require.Equal(t, len(fErrs), 1) + + parseErrs := parseValidatorValidationErrors(vErr) + require.Equal(t, len(parseErrs), 1) + + assert.Equal(t, parseErrs[0].ParamName, fErrs[0].Field()) + assert.Equal(t, parseErrs[0].ErrorType, ParseErrorTypeValidation) + assert.Equal(t, parseErrs[0].InitialError, fErrs[0]) + +}