github.com/shoshinnikita/budget-manager@v0.7.1-0.20220131195411-8c46ff1c6778/internal/web/utils/encode.go (about) 1 package utils 2 3 import ( 4 "context" 5 "encoding/json" 6 "net/http" 7 8 "github.com/ShoshinNikita/budget-manager/internal/logger" 9 "github.com/ShoshinNikita/budget-manager/internal/pkg/reqid" 10 "github.com/ShoshinNikita/budget-manager/internal/web/api/models" 11 ) 12 13 type responseEncoder struct { 14 resp models.Response 15 statusCode int 16 success bool 17 respErrorMsg string 18 } 19 20 type EncodeOption func(*responseEncoder) 21 22 // EncodeResponse can be used to encode a custom response 23 func EncodeResponse(resp models.Response) EncodeOption { 24 return func(enc *responseEncoder) { 25 enc.resp = resp 26 } 27 } 28 29 // EncodeStatusCode can be used to write a custom status code 30 func EncodeStatusCode(statusCode int) EncodeOption { 31 return func(enc *responseEncoder) { 32 enc.statusCode = statusCode 33 } 34 } 35 36 // Encode is a helper function to encode API responses. It writes http.StatusOK and 37 // encodes a base response by default. The fields of the base response are automatically filled 38 // with values for a "successful" response. Use encode options or other Encode... functions 39 // to change this behavior 40 func Encode(ctx context.Context, w http.ResponseWriter, 41 log logger.Logger, options ...EncodeOption) (ok bool) { 42 43 enc := &responseEncoder{ 44 resp: &models.BaseResponse{}, 45 statusCode: http.StatusOK, 46 success: true, 47 } 48 for _, opt := range options { 49 opt(enc) 50 } 51 52 enc.resp.SetBaseResponse(models.BaseResponse{ 53 RequestID: reqid.FromContext(ctx).ToString(), 54 Success: enc.success, 55 Error: enc.respErrorMsg, 56 }) 57 58 w.Header().Set("Content-Type", "application/json") 59 w.WriteHeader(enc.statusCode) 60 if err := json.NewEncoder(w).Encode(enc.resp); err != nil { 61 LogInternalError(log, "couldn't encode response", err) 62 return false 63 } 64 return true 65 } 66 67 // EncodeError is a wrapper for Encode that encodes error response 68 func EncodeError(ctx context.Context, w http.ResponseWriter, log logger.Logger, 69 err error, statusCode int, options ...EncodeOption) bool { 70 71 options = append(options, func(enc *responseEncoder) { 72 enc.statusCode = statusCode 73 enc.success = false 74 enc.respErrorMsg = err.Error() 75 }) 76 return Encode(ctx, w, log, options...) 77 } 78 79 // EncodeInternalError is a wrapper for Encode that logs and encodes an internal error 80 func EncodeInternalError(ctx context.Context, w http.ResponseWriter, log logger.Logger, 81 respMsg string, err error, options ...EncodeOption) bool { 82 83 LogInternalError(log, respMsg, err) 84 85 options = append(options, func(enc *responseEncoder) { 86 enc.statusCode = http.StatusInternalServerError 87 enc.success = false 88 enc.respErrorMsg = respMsg 89 }) 90 return Encode(ctx, w, log, options...) 91 }