From 5ac80512533e40f9335ed8a5beb590d7934179d8 Mon Sep 17 00:00:00 2001 From: alexisvisco Date: Wed, 22 Jul 2020 15:45:17 +0200 Subject: [PATCH] refactor: kcd --- pkg/kcd/binding.go => binding.go | 25 +++-- errors.go | 46 +++++++++ examples/simple/main.go | 4 +- extractors.go | 41 ++++++++ pkg/kcd/handler.go => handler.go | 10 +- hooks.go | 160 +++++++++++++++++++++++++++++++ kcd.go | 84 ++++++++++++++++ pkg/extractor/extractors.go | 9 -- pkg/extractor/header.go | 13 --- pkg/extractor/path.go | 15 --- pkg/extractor/query.go | 23 ----- pkg/hook/binding.go | 34 ------- pkg/hook/error.go | 101 ------------------- pkg/hook/render.go | 36 ------- pkg/hook/validate.go | 31 ------ pkg/kcd/kcd.go | 36 ------- pkg/kcderr/internal.go | 46 --------- 17 files changed, 349 insertions(+), 365 deletions(-) rename pkg/kcd/binding.go => binding.go (88%) create mode 100644 errors.go create mode 100644 extractors.go rename pkg/kcd/handler.go => handler.go (93%) create mode 100644 hooks.go create mode 100644 kcd.go delete mode 100644 pkg/extractor/extractors.go delete mode 100644 pkg/extractor/header.go delete mode 100644 pkg/extractor/path.go delete mode 100644 pkg/extractor/query.go delete mode 100644 pkg/hook/binding.go delete mode 100644 pkg/hook/error.go delete mode 100644 pkg/hook/render.go delete mode 100644 pkg/hook/validate.go delete mode 100644 pkg/kcd/kcd.go delete mode 100644 pkg/kcderr/internal.go diff --git a/pkg/kcd/binding.go b/binding.go similarity index 88% rename from pkg/kcd/binding.go rename to binding.go index b0dfa06..c7899ba 100644 --- a/pkg/kcd/binding.go +++ b/binding.go @@ -7,15 +7,12 @@ import ( "reflect" "strconv" "time" - - "github.com/expectedsh/kcd/pkg/extractor" - "github.com/expectedsh/kcd/pkg/kcderr" ) // bind binds the fields the fields of the input object in with // the values of the parameters extracted from the Gin context. // It reads tag to know what to extract using the extractor func. -func bind(w http.ResponseWriter, r *http.Request, v reflect.Value, tag string, extract extractor.Extractor) error { +func bind(w http.ResponseWriter, r *http.Request, v reflect.Value, tag string, extract Extractor) error { t := v.Type() if t.Kind() == reflect.Ptr { @@ -55,18 +52,18 @@ func bind(w http.ResponseWriter, r *http.Request, v reflect.Value, tag string, e continue } - bindingError := &kcderr.Input{Field: tagValue, Type: t, Extractor: tag} + bindingError := &inputError{field: tagValue, fieldType: t, extractor: tag} fieldValues, err := extract(w, r, tagValue) if err != nil { return bindingError. - WithErr(err). - WithMessage("unable to extract value from request") + withErr(err). + withMessage("unable to extract value from request") } // Extract default value and use it in place // if no values were returned. - def, ok := ft.Tag.Lookup(Config.DefaultTag) + def, ok := ft.Tag.Lookup(defaultTag) if ok && len(fieldValues) == 0 { fieldValues = append(fieldValues, def) } @@ -89,14 +86,14 @@ func bind(w http.ResponseWriter, r *http.Request, v reflect.Value, tag string, e // Multiple values can only be filled to types Slice and Array. if len(fieldValues) > 1 && (kind != reflect.Slice && kind != reflect.Array) { - return bindingError.WithMessage("multiple values not supported") + return bindingError.withMessage("multiple values not supported") } // Ensure that the number of values to fill does not exceed the length of a field of type Array. if kind == reflect.Array { if field.Len() != len(fieldValues) { msg := fmt.Sprintf("parameter expect %d values, got %d", field.Len(), len(fieldValues)) - return bindingError.WithMessage(msg) + return bindingError.withMessage(msg) } } @@ -111,8 +108,8 @@ func bind(w http.ResponseWriter, r *http.Request, v reflect.Value, tag string, e err = bindStringValue(val, v) if err != nil { return bindingError. - WithErr(err). - WithMessage(fmt.Sprintf("unable to set the value %q as type %+v", val, v.Type().Name())) + withErr(err). + withMessage(fmt.Sprintf("unable to set the value %q as type %+v", val, v.Type().Name())) } if kind == reflect.Slice { field.Set(reflect.Append(field, v)) @@ -128,8 +125,8 @@ func bind(w http.ResponseWriter, r *http.Request, v reflect.Value, tag string, e err = bindStringValue(fieldValues[0], field) if err != nil { return bindingError. - WithErr(err). - WithMessage(fmt.Sprintf("unable to set the value %q as type %+v", fieldValues[0], field.Type().Name())) + withErr(err). + withMessage(fmt.Sprintf("unable to set the value %q as type %+v", fieldValues[0], field.Type().Name())) } } diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..2c58b0e --- /dev/null +++ b/errors.go @@ -0,0 +1,46 @@ +package kcd + +import ( + "fmt" + "reflect" +) + +type inputError struct { + message string + fieldType reflect.Type + field string + extractor string + err error +} + +func (b *inputError) withMessage(msg string) *inputError { + b.message = msg + return b +} + +func (b *inputError) withErr(err error) *inputError { + b.err = err + return b +} + +// ErrorDescription implements the builtin error interface for inputError. +func (b inputError) Error() string { + if b.field != "" && b.fieldType != nil { + return fmt.Sprintf( + "binding error on field '%s' of type '%s': %s", + b.field, + b.fieldType.Name(), + b.message, + ) + } + return fmt.Sprintf("binding error: %s", b.message) +} + +type outputError struct { + Err error +} + +// ErrorDescription implements the builtin error interface for outputError. +func (o outputError) Error() string { + return "unable to marshal response into json format" +} diff --git a/examples/simple/main.go b/examples/simple/main.go index 4bb63cf..04e2bbe 100644 --- a/examples/simple/main.go +++ b/examples/simple/main.go @@ -8,14 +8,14 @@ import ( validation "github.com/go-ozzo/ozzo-validation/v4" "github.com/go-ozzo/ozzo-validation/v4/is" - "github.com/expectedsh/kcd/pkg/kcd" + "github.com/expectedsh/kcd" ) func main() { r := chi.NewRouter() r.Use(middleware.Logger) - // kcd.Config.BindingHook = ... + // kcd.Config.BindHook = ... r.Post("/{path}", kcd.Handler(CreateCustomer, http.StatusOK)) diff --git a/extractors.go b/extractors.go new file mode 100644 index 0000000..df3df98 --- /dev/null +++ b/extractors.go @@ -0,0 +1,41 @@ +package kcd + +import ( + "net/http" + "strings" + + "github.com/go-chi/chi" +) + +// defaultHeaderExtractor is an extractor that operates on the headers +// of a request. +func defaultHeaderExtractor(_ http.ResponseWriter, r *http.Request, tag string) ([]string, error) { + header := r.Header.Get(tag) + + return []string{header}, nil +} + +// defaultQueryExtractor is an extractor that operates on the path +// parameters of a request. +func defaultQueryExtractor(_ http.ResponseWriter, r *http.Request, tag string) ([]string, error) { + var params []string + query := r.URL.Query()[tag] + + splitFn := func(c rune) bool { + return c == ',' + } + + for _, q := range query { + params = append(params, strings.FieldsFunc(q, splitFn)...) + } + + return params, nil +} + +// defaultPathExtractor is an extractor that operates on the path +// parameters of a request. +func defaultPathExtractor(_ http.ResponseWriter, r *http.Request, tag string) ([]string, error) { + p := chi.URLParam(r, tag) + + return []string{p}, nil +} diff --git a/pkg/kcd/handler.go b/handler.go similarity index 93% rename from pkg/kcd/handler.go rename to handler.go index aa04ed0..f2d1e21 100644 --- a/pkg/kcd/handler.go +++ b/handler.go @@ -21,7 +21,7 @@ const ( // // func([w http.ResponseWriter], [r *http.Request], [input object ptr]) ([output object], error) // -// Input and output objects are both optional. +// inputError and output objects are both optional. // As such, the minimal accepted signature is: // // func(w http.ResponseWriter, r *http.Request) error @@ -55,25 +55,25 @@ func Handler(h interface{}, defaultSuccessStatusCode int) http.HandlerFunc { input = &i // Bind body - if err := Config.BindingHook(w, r, input.Interface()); err != nil { + if err := Config.BindHook(w, r, input.Interface()); err != nil { Config.ErrorHook(w, r, err) return } // Bind query-parameters. - if err := bind(w, r, i, Config.QueryTag, Config.QueryExtractor); err != nil { + if err := bind(w, r, i, queryTag, Config.QueryExtractor); err != nil { Config.ErrorHook(w, r, err) return } // Bind path arguments. - if err := bind(w, r, i, Config.PathTag, Config.PathExtractor); err != nil { + if err := bind(w, r, i, pathTag, Config.PathExtractor); err != nil { Config.ErrorHook(w, r, err) return } // Bind headers. - if err := bind(w, r, i, Config.HeaderTag, Config.HeaderExtractor); err != nil { + if err := bind(w, r, i, headerTag, Config.HeaderExtractor); err != nil { Config.ErrorHook(w, r, err) return } diff --git a/hooks.go b/hooks.go new file mode 100644 index 0000000..02b9fb2 --- /dev/null +++ b/hooks.go @@ -0,0 +1,160 @@ +package kcd + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "github.com/expectedsh/errors" + "github.com/go-chi/chi/middleware" + validation "github.com/go-ozzo/ozzo-validation/v4" +) + +// defaultValidateHook is the default validation hook. +// It use 'ozzo-validation' to validate structure. +// A structure must implement 'ValidatableWithContext' or 'Validatable' +func defaultValidateHook(ctx context.Context, input interface{}) error { + var err validation.Errors + + switch v := input.(type) { + case validation.ValidatableWithContext: + err = v.ValidateWithContext(ctx).(validation.Errors) + case validation.Validatable: + err = v.Validate().(validation.Errors) + } + + if len(err) == 0 { + return nil + } + + return errors. + NewWithKind(errors.KindInvalidArgument, "the request has one or multiple invalid fields"). + WithMetadata("kcd.fields", err) +} + +// defaultRenderHook is the default render hook. +// It marshals the payload to JSON, or returns an empty body if the payload is nil. +func defaultRenderHook(w http.ResponseWriter, _ *http.Request, statusCode int, response interface{}) error { + if response != nil { + marshal, err := json.Marshal(response) + if err != nil { + return outputError{Err: err} + } + + w.WriteHeader(statusCode) + if _, err := w.Write(marshal); err != nil { + return err + } + + } else { + w.WriteHeader(statusCode) + } + + return nil +} + +// errorResponse is the default response that send the default error hook +type errorResponse struct { + ErrorDescription string `json:"error_description"` + Error errors.Kind `json:"error"` + + Fields map[string]string `json:"fields,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` +} + +// defaultErrorHook is the default error hook. +// It check the error and return the corresponding response to the client. +func defaultErrorHook(w http.ResponseWriter, r *http.Request, err error) { + + response := errorResponse{ + ErrorDescription: "internal server error", + Fields: map[string]string{}, + Metadata: map[string]interface{}{}, + } + + reqID := middleware.GetReqID(r.Context()) + if reqID != "" { + response.Metadata["request_id"] = reqID + } + + switch e := err.(type) { + case *errors.Error: + w.WriteHeader(e.Kind.ToStatusCode()) + + response.ErrorDescription = e.Message + response.Error = e.Kind + + // todo: don't use string literal for kcd.* + + metadata, ok := e.GetMetadata(errorKeyFields) + if ok { + m, okMap := metadata.(validation.Errors) + if okMap { + for k, v := range m { + response.Fields[k] = v.Error() + } + } + } + + metadata, ok = e.GetMetadata(errorKeyMetadata) + if ok { + m, okMap := metadata.(map[string]interface{}) + if okMap { + for k, v := range m { + response.Metadata[k] = v + } + } + } + + case inputError: + w.WriteHeader(http.StatusBadRequest) + response.Error = errors.KindInvalidArgument + + switch e.extractor { + case queryTag, pathTag, headerTag: + response.ErrorDescription = http.StatusText(http.StatusBadRequest) + response.Fields[e.field] = fmt.Sprintf("with %s parameter: %s", e.fieldType, e.message) + case jsonTag: + response.ErrorDescription = e.message + } + case outputError: + w.WriteHeader(http.StatusInternalServerError) + + response.Error = errors.KindInternal + response.ErrorDescription = e.Error() + } + + // todo: use a log hook to log kcd real (critic) error + + marshal, err := json.Marshal(response) + if err != nil { + return + } + + _, _ = w.Write(marshal) + + return +} + +// defaultBindHook returns a Bind hook with the default logic, with configurable MaxBodyBytes. +func defaultBindHook(maxBodyBytes int64) BindHook { + return func(w http.ResponseWriter, r *http.Request, in interface{}) error { + r.Body = http.MaxBytesReader(w, r.Body, maxBodyBytes) + if r.ContentLength == 0 { + return nil + } + + bytesBody, err := ioutil.ReadAll(r.Body) + if err != nil { + return inputError{extractor: jsonTag, message: "unable to read body"} + } + + if err := json.Unmarshal(bytesBody, in); err != nil { + return inputError{extractor: jsonTag, message: "unable to unmarshal request"} + } + + return nil + } +} diff --git a/kcd.go b/kcd.go new file mode 100644 index 0000000..d437479 --- /dev/null +++ b/kcd.go @@ -0,0 +1,84 @@ +package kcd + +import ( + "context" + "net/http" +) + +const ( + queryTag = "query" + pathTag = "path" + headerTag = "header" + jsonTag = "json" + defaultTag = "default" +) + +const ( + errorKeyFields = "kcd.fields" + errorKeyMetadata = "kcd.metadata" +) + +// ErrorHook hook lets you interpret errors returned by your handlers. +// After analysis, the hook should return a suitable http status code +// and and error payload. +// This lets you deeply inspect custom error types. +type ErrorHook func(w http.ResponseWriter, r *http.Request, err error) + +// RenderHook is the last hook called by the wrapped handler before returning. +// It takes the response, request, the success HTTP status code and the response +// payload as parameters. +// +// Its role is to render the payload to the client to the proper format. +type RenderHook func(w http.ResponseWriter, r *http.Request, defaultSuccessStatusCode int, response interface{}) error + +// BindHook is the hook called by the wrapped http handler when +// binding an incoming request to the kcd handler's input object. +type BindHook func(w http.ResponseWriter, r *http.Request, in interface{}) error + +// ValidateHook is the hook called to validate the input. +type ValidateHook func(ctx context.Context, input interface{}) error + +// +type Extractor func(w http.ResponseWriter, r *http.Request, tag string) ([]string, error) + +var DefaultExtractors = struct { + Query Extractor + Path Extractor + Header Extractor +}{ + Query: defaultQueryExtractor, + Path: defaultPathExtractor, + Header: defaultHeaderExtractor, +} + +var DefaultHooks = struct { + Error ErrorHook + Render RenderHook + Binding func(maxBodySize int64) BindHook + Validate ValidateHook +}{ + Error: defaultErrorHook, + Render: defaultRenderHook, + Binding: defaultBindHook, + Validate: defaultValidateHook, +} + +var Config = struct { + QueryExtractor Extractor + PathExtractor Extractor + HeaderExtractor Extractor + ErrorHook ErrorHook + RenderHook RenderHook + BindHook BindHook + ValidateHook ValidateHook +}{ + + QueryExtractor: DefaultExtractors.Query, + PathExtractor: DefaultExtractors.Header, + HeaderExtractor: DefaultExtractors.Path, + + ErrorHook: DefaultHooks.Error, + RenderHook: DefaultHooks.Render, + BindHook: DefaultHooks.Binding(256 * 1024), + ValidateHook: DefaultHooks.Validate, +} diff --git a/pkg/extractor/extractors.go b/pkg/extractor/extractors.go deleted file mode 100644 index 5c2774e..0000000 --- a/pkg/extractor/extractors.go +++ /dev/null @@ -1,9 +0,0 @@ -package extractor - -import ( - "net/http" -) - -// An extractorFunc extracts data from a gin context according to -// parameters specified in a field tag. -type Extractor func(w http.ResponseWriter, r *http.Request, str string) ([]string, error) diff --git a/pkg/extractor/header.go b/pkg/extractor/header.go deleted file mode 100644 index f310c3d..0000000 --- a/pkg/extractor/header.go +++ /dev/null @@ -1,13 +0,0 @@ -package extractor - -import ( - "net/http" -) - -// DefaultHeaderExtractor is an extractor that operates on the headers -// of a request. -func DefaultHeaderExtractor(_ http.ResponseWriter, r *http.Request, tag string) ([]string, error) { - header := r.Header.Get(tag) - - return []string{header}, nil -} diff --git a/pkg/extractor/path.go b/pkg/extractor/path.go deleted file mode 100644 index 3c31c55..0000000 --- a/pkg/extractor/path.go +++ /dev/null @@ -1,15 +0,0 @@ -package extractor - -import ( - "net/http" - - "github.com/go-chi/chi" -) - -// DefaultPathExtractor is an extractor that operates on the path -// parameters of a request. -func DefaultPathExtractor(_ http.ResponseWriter, r *http.Request, tag string) ([]string, error) { - p := chi.URLParam(r, tag) - - return []string{p}, nil -} diff --git a/pkg/extractor/query.go b/pkg/extractor/query.go deleted file mode 100644 index 961cded..0000000 --- a/pkg/extractor/query.go +++ /dev/null @@ -1,23 +0,0 @@ -package extractor - -import ( - "net/http" - "strings" -) - -// DefaultQueryExtractor is an extractor that operates on the path -// parameters of a request. -func DefaultQueryExtractor(_ http.ResponseWriter, r *http.Request, tag string) ([]string, error) { - var params []string - query := r.URL.Query()[tag] - - splitFn := func(c rune) bool { - return c == ',' - } - - for _, q := range query { - params = append(params, strings.FieldsFunc(q, splitFn)...) - } - - return params, nil -} diff --git a/pkg/hook/binding.go b/pkg/hook/binding.go deleted file mode 100644 index 3309fb4..0000000 --- a/pkg/hook/binding.go +++ /dev/null @@ -1,34 +0,0 @@ -package hook - -import ( - "encoding/json" - "io/ioutil" - "net/http" - - "github.com/expectedsh/kcd/pkg/kcderr" -) - -// BindHook is the hook called by the wrapped http handler when -// binding an incoming request to the kcd handler's input object. -type Bind func(w http.ResponseWriter, r *http.Request, in interface{}) error - -// DefaultBindingHook returns a Bind hook with the default logic, with configurable MaxBodyBytes. -func DefaultBindingHook(maxBodyBytes int64) Bind { - return func(w http.ResponseWriter, r *http.Request, in interface{}) error { - r.Body = http.MaxBytesReader(w, r.Body, maxBodyBytes) - if r.ContentLength == 0 { - return nil - } - - bytesBody, err := ioutil.ReadAll(r.Body) - if err != nil { - return kcderr.Input{Extractor: "json", Message: "unable to read body"} - } - - if err := json.Unmarshal(bytesBody, in); err != nil { - return kcderr.Input{Extractor: "json", Message: "unable to unmarshal request"} - } - - return nil - } -} diff --git a/pkg/hook/error.go b/pkg/hook/error.go deleted file mode 100644 index 16ab61f..0000000 --- a/pkg/hook/error.go +++ /dev/null @@ -1,101 +0,0 @@ -package hook - -import ( - "encoding/json" - "fmt" - "net/http" - - "github.com/expectedsh/errors" - "github.com/go-chi/chi/middleware" - validation "github.com/go-ozzo/ozzo-validation/v4" - - "github.com/expectedsh/kcd/pkg/kcderr" -) - -// Error hook lets you interpret errors returned by your handlers. -// After analysis, the hook should return a suitable http status code -// and and error payload. -// This lets you deeply inspect custom error types. -type Error func(w http.ResponseWriter, r *http.Request, err error) - -type errorResponse struct { - ErrorDescription string `json:"error_description"` - Error errors.Kind `json:"error"` - - Fields map[string]string `json:"fields,omitempty"` - Metadata map[string]interface{} `json:"metadata,omitempty"` -} - -func DefaultErrorHook(w http.ResponseWriter, r *http.Request, err error) { - - response := errorResponse{ - ErrorDescription: "internal server error", - Fields: map[string]string{}, - Metadata: map[string]interface{}{}, - } - - reqID := middleware.GetReqID(r.Context()) - if reqID != "" { - response.Metadata["request_id"] = reqID - } - - switch e := err.(type) { - case *errors.Error: - w.WriteHeader(e.Kind.ToStatusCode()) - - response.ErrorDescription = e.Message - response.Error = e.Kind - - // todo: don't use string literal for kcd.* - - metadata, ok := e.GetMetadata("kcd.fields") - if ok { - m, okMap := metadata.(validation.Errors) - if okMap { - for k, v := range m { - response.Fields[k] = v.Error() - } - } - } - - metadata, ok = e.GetMetadata("kcd.metadata") - if ok { - m, okMap := metadata.(map[string]interface{}) - if okMap { - for k, v := range m { - response.Metadata[k] = v - } - } - } - - case kcderr.Input: - w.WriteHeader(http.StatusBadRequest) - response.Error = errors.KindInvalidArgument - - // todo: don't use string literal for query, path, header, json - - switch e.Extractor { - case "query", "path", "header": - response.ErrorDescription = http.StatusText(http.StatusBadRequest) - response.Fields[e.Field] = fmt.Sprintf("with %s parameter: %s", e.Type, e.Message) - case "json": - response.ErrorDescription = e.Message - } - case kcderr.Output: - w.WriteHeader(http.StatusInternalServerError) - - response.Error = errors.KindInternal - response.ErrorDescription = e.Error() - } - - // todo: use a log hook to log kcd real (critic) error - - marshal, err := json.Marshal(response) - if err != nil { - return - } - - _, _ = w.Write(marshal) - - return -} diff --git a/pkg/hook/render.go b/pkg/hook/render.go deleted file mode 100644 index f62ca2a..0000000 --- a/pkg/hook/render.go +++ /dev/null @@ -1,36 +0,0 @@ -package hook - -import ( - "encoding/json" - "net/http" - - "github.com/expectedsh/kcd/pkg/kcderr" -) - -// Render is the last hook called by the wrapped handler before returning. -// It takes the response, request, the success HTTP status code and the response -// payload as parameters. -// -// Its role is to render the payload to the client to the proper format. -type Render func(w http.ResponseWriter, r *http.Request, defaultSuccessStatusCode int, response interface{}) error - -// DefaultRenderHook is the default render hook. -// It marshals the payload to JSON, or returns an empty body if the payload is nil. -func DefaultRenderHook(w http.ResponseWriter, _ *http.Request, statusCode int, response interface{}) error { - if response != nil { - marshal, err := json.Marshal(response) - if err != nil { - return kcderr.Output{Err: err} - } - - w.WriteHeader(statusCode) - if _, err := w.Write(marshal); err != nil { - return err - } - - } else { - w.WriteHeader(statusCode) - } - - return nil -} diff --git a/pkg/hook/validate.go b/pkg/hook/validate.go deleted file mode 100644 index f8cfa96..0000000 --- a/pkg/hook/validate.go +++ /dev/null @@ -1,31 +0,0 @@ -package hook - -import ( - "context" - - "github.com/expectedsh/errors" - validation "github.com/go-ozzo/ozzo-validation/v4" -) - -type Validate func(ctx context.Context, input interface{}) error - -func DefaultValidateHook(ctx context.Context, input interface{}) error { - var err validation.Errors - - switch v := input.(type) { - case validation.ValidatableWithContext: - err = v.ValidateWithContext(ctx).(validation.Errors) - case validation.Validatable: - err = v.Validate().(validation.Errors) - } - - if len(err) == 0 { - return nil - } - - return errors. - NewWithKind(errors.KindInvalidArgument, "the request has one or multiple invalid fields"). - WithMetadata("kcd.fields", err) - - return nil -} diff --git a/pkg/kcd/kcd.go b/pkg/kcd/kcd.go deleted file mode 100644 index 345212e..0000000 --- a/pkg/kcd/kcd.go +++ /dev/null @@ -1,36 +0,0 @@ -package kcd - -import ( - "github.com/expectedsh/kcd/pkg/extractor" - "github.com/expectedsh/kcd/pkg/hook" -) - -type H map[string]interface{} - -var Config = &struct { - QueryTag string - PathTag string - HeaderTag string - DefaultTag string - QueryExtractor extractor.Extractor - PathExtractor extractor.Extractor - HeaderExtractor extractor.Extractor - ErrorHook hook.Error - RenderHook hook.Render - BindingHook hook.Bind - ValidateHook hook.Validate -}{ - QueryTag: "query", - PathTag: "path", - HeaderTag: "header", - DefaultTag: "default", - - QueryExtractor: extractor.DefaultQueryExtractor, - PathExtractor: extractor.DefaultPathExtractor, - HeaderExtractor: extractor.DefaultHeaderExtractor, - - ErrorHook: hook.DefaultErrorHook, - RenderHook: hook.DefaultRenderHook, - BindingHook: hook.DefaultBindingHook(256 * 1024), - ValidateHook: hook.DefaultValidateHook, -} diff --git a/pkg/kcderr/internal.go b/pkg/kcderr/internal.go deleted file mode 100644 index 1974af0..0000000 --- a/pkg/kcderr/internal.go +++ /dev/null @@ -1,46 +0,0 @@ -package kcderr - -import ( - "fmt" - "reflect" -) - -type Input struct { - Message string - Type reflect.Type - Field string - Extractor string - Err error -} - -func (b *Input) WithMessage(msg string) *Input { - b.Message = msg - return b -} - -func (b *Input) WithErr(err error) *Input { - b.Err = err - return b -} - -// ErrorDescription implements the builtin error interface for Input. -func (b Input) Error() string { - if b.Field != "" && b.Type != nil { - return fmt.Sprintf( - "binding error on field '%s' of type '%s': %s", - b.Field, - b.Type.Name(), - b.Message, - ) - } - return fmt.Sprintf("binding error: %s", b.Message) -} - -type Output struct { - Err error -} - -// ErrorDescription implements the builtin error interface for Output. -func (o Output) Error() string { - return "unable to marshal response into json format" -}