From 38c7f47cdb91919f6bddaeedab5b881bf86f5d5a Mon Sep 17 00:00:00 2001 From: Leon Silcott Date: Fri, 10 Sep 2021 17:00:48 -0400 Subject: [PATCH] - Added logic for the user to overwrite the transfer object error. - Updated example to demonstrate --- examples/example_simple_api.go | 111 +++++++++++++++++++++++++++++++++ model.go | 6 ++ replier.go | 37 +++++++---- 3 files changed, 143 insertions(+), 11 deletions(-) diff --git a/examples/example_simple_api.go b/examples/example_simple_api.go index 82135ae..ae37f59 100644 --- a/examples/example_simple_api.go +++ b/examples/example_simple_api.go @@ -4,6 +4,7 @@ import ( "errors" "log" "net/http" + "strconv" "github.com/ooaklee/reply" ) @@ -75,6 +76,105 @@ func (t *fooReplyTransferObject) SetErrors(transferObjectErrors []reply.Transfer //////////////////// +///////////////////////////////////////////////// +//// Custom Transition Object Error Example ///// +// This is an example of how you can create a +// custom response structure for errrors retutned +// in the response + +type barError struct { + + // Title a short summary of the problem + Title string `json:"title,omitempty"` + + // Message a description of the error + Message string `json:"message,omitempty"` + + // About holds the link that gives further insight into the error + About string `json:"about,omitempty"` + + // More randomd top level attribute to make error + // difference + More struct { + // Status the HTTP status associated with error + Status string `json:"status,omitempty"` + + // Code internal error code used to reference error + Code string `json:"code,omitempty"` + + // Meta contains additional meta-information about the error + Meta interface{} `json:"meta,omitempty"` + } `json:"more,omitempty"` +} + +// SetTitle adds title to error +func (b *barError) SetTitle(title string) { + b.Title = title +} + +// GetTitle returns error's title +func (b *barError) GetTitle() string { + return b.Title +} + +// SetDetail adds detail to error +func (b *barError) SetDetail(detail string) { + b.Message = detail +} + +// GetDetail return error's detail +func (b *barError) GetDetail() string { + return b.Message +} + +// SetAbout adds about to error +func (b *barError) SetAbout(about string) { + b.About = about +} + +// GetAbout return error's about +func (b *barError) GetAbout() string { + return b.About +} + +// SetStatusCode converts and add http status code to error +func (b *barError) SetStatusCode(status int) { + b.More.Status = strconv.Itoa(status) +} + +// GetStatusCode returns error's HTTP status code +func (b *barError) GetStatusCode() string { + return b.More.Status +} + +// SetCode adds internal code to error +func (b *barError) SetCode(code string) { + b.More.Code = code +} + +// GetCode returns error's internal code +func (b *barError) GetCode() string { + return b.More.Code +} + +// SetMeta adds meta property to error +func (b *barError) SetMeta(meta interface{}) { + b.More.Meta = meta +} + +// GetMeta returns error's meta property +func (b *barError) GetMeta() interface{} { + return b.More.Meta +} + +// RefreshTransferObject returns an empty instance of transfer object +// error +func (b *barError) RefreshTransferObject() reply.TransferObjectError { + return &barError{} +} + +//////////////////// + type user struct { ID int `json:"id"` Name string `json:"name"` @@ -90,6 +190,8 @@ var replier *reply.Replier = reply.NewReplier(baseManifest) var replierWithCustomTransitionObj *reply.Replier = reply.NewReplier(baseManifest, reply.WithTransferObject(&fooReplyTransferObject{})) +var replierWithCustomTransitionObjError *reply.Replier = reply.NewReplier(baseManifest, reply.WithTransferObjectError(&barError{})) + func simpleUsersAPINotFoundHandler(w http.ResponseWriter, r *http.Request) { // Do something with a server @@ -177,6 +279,14 @@ func simpleUsersAPINotFoundCustomReplierHandler(w http.ResponseWriter, r *http.R ////////////////////////////// //// Handlers Using Aides //// +func simpleUsersAPIMultiErrorUsingAideWithCustomErrorHandler(w http.ResponseWriter, r *http.Request) { + + // Do something with a server + serverErrs := []error{errors.New("example-dob-validation-error"), errors.New("example-name-validation-error")} + + _ = replierWithCustomTransitionObjError.NewHTTPMultiErrorResponse(w, serverErrs) +} + func simpleUsersAPIMultiErrorUsingAideHandler(w http.ResponseWriter, r *http.Request) { // Do something with a server @@ -255,6 +365,7 @@ func handleRequest() { http.HandleFunc("/custom/users/3", simpleUsersAPINotFoundCustomReplierHandler) http.HandleFunc("/aides/errors", simpleUsersAPIMultiErrorUsingAideHandler) + http.HandleFunc("/aides/errors/custom", simpleUsersAPIMultiErrorUsingAideWithCustomErrorHandler) http.HandleFunc("/aides/users", simpleUsersAPIUsingAideHandler) http.HandleFunc("/aides/users/3", simpleUsersAPINotFoundUsingAideHandler) http.HandleFunc("/aides/users/4", simpleUsersAPINoManifestEntryUsingAideHandler) diff --git a/model.go b/model.go index 6b5dc8b..14c8a48 100644 --- a/model.go +++ b/model.go @@ -135,6 +135,12 @@ func (e *Error) GetMeta() interface{} { return e.Meta } +// RefreshTransferObject returns an empty instance of transfer object +// error +func (e *Error) RefreshTransferObject() TransferObjectError { + return &Error{} +} + // defaultReplyTransferObject handles structing response for client // consumption type defaultReplyTransferObject struct { diff --git a/replier.go b/replier.go index 3261623..00bd707 100644 --- a/replier.go +++ b/replier.go @@ -27,6 +27,7 @@ type TransferObjectError interface { GetCode() string SetMeta(meta interface{}) GetMeta() interface{} + RefreshTransferObject() TransferObjectError } // TransferObject outlines expected methods of a transfer object @@ -65,6 +66,14 @@ func WithTransferObject(replacementTransferObject TransferObject) Option { } } +// WithTransferObjectError overwrites the transfer object used to represent +// errors in response +func WithTransferObjectError(replacementTransferObjectError TransferObjectError) Option { + return func(r *Replier) { + r.transferObjectError = replacementTransferObjectError + } +} + // NewResponseRequest holds attributes for response type NewResponseRequest struct { Writer http.ResponseWriter @@ -81,18 +90,21 @@ type NewResponseRequest struct { // Replier handles managing responses type Replier struct { - errorManifest ErrorManifest - transferObject TransferObject + errorManifest ErrorManifest + transferObject TransferObject + transferObjectError TransferObjectError } // NewReplier creates a replier func NewReplier(manifests []ErrorManifest, options ...Option) *Replier { activeTransferObject := &defaultReplyTransferObject{} + activeTransferObjectError := &Error{} replier := Replier{ - errorManifest: mergeManifestCollections(manifests), - transferObject: activeTransferObject, + errorManifest: mergeManifestCollections(manifests), + transferObject: activeTransferObject, + transferObjectError: activeTransferObjectError, } // Add option add-ons on replier @@ -189,10 +201,10 @@ func (r *Replier) generateMultiErrorResponse(errs []error) error { if is5xx(manifestItem.StatusCode) { return r.sendHTTPErrorsResponse(manifestItem.StatusCode, append( []TransferObjectError{}, - ConvertErrorItemToTransferObjectError(manifestItem))) + r.convertErrorManifestItemToTransferObjectError(manifestItem))) } - transferObjectErrors = append(transferObjectErrors, ConvertErrorItemToTransferObjectError(manifestItem)) + transferObjectErrors = append(transferObjectErrors, r.convertErrorManifestItemToTransferObjectError(manifestItem)) } statusCode := getAppropiateStatusCodeOrDefault(transferObjectErrors) @@ -205,7 +217,7 @@ func (r *Replier) generateMultiErrorResponse(errs []error) error { func (r *Replier) generateErrorResponse(err error) error { manifestItem := r.getErrorManifestItem(err) - transferObjectErrors := append([]TransferObjectError{}, ConvertErrorItemToTransferObjectError(manifestItem)) + transferObjectErrors := append([]TransferObjectError{}, r.convertErrorManifestItemToTransferObjectError(manifestItem)) return r.sendHTTPErrorsResponse(manifestItem.StatusCode, transferObjectErrors) } @@ -269,10 +281,13 @@ func (r *Replier) setHeaders(h map[string]string) { } } -// ConvertErrorItemToTransferObjectError converts manifest item to valid +// convertErrorManifestItemToTransferObjectError converts manifest error item to valid // transfer object error -func ConvertErrorItemToTransferObjectError(errorItem ErrorManifestItem) TransferObjectError { - convertedError := Error{} +func (r *Replier) convertErrorManifestItemToTransferObjectError(errorItem ErrorManifestItem) TransferObjectError { + + // Use fresh transfer object error + convertedError := r.transferObjectError.RefreshTransferObject() + convertedError.SetTitle(errorItem.Title) convertedError.SetDetail(errorItem.Detail) convertedError.SetAbout(errorItem.About) @@ -280,7 +295,7 @@ func ConvertErrorItemToTransferObjectError(errorItem ErrorManifestItem) Transfer convertedError.SetStatusCode(errorItem.StatusCode) convertedError.SetMeta(errorItem.Meta) - return &convertedError + return convertedError } // getAppropiateStatusCodeOrDefault loops through collection of transfer object errors (first to last), and