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  }