Organize backend into product executable and shared Go modules
This commit is contained in:
1 parent
981e84a4b4
commit
f0720ec66b
382 files changed
+1944
-6989
No files matched your search
@@ -0,0 +1,225 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"git.sendico.io/tech/sxchnge/shared/errors"
|
||||
api "git.sendico.io/tech/sxchnge/shared/http"
|
||||
"git.sendico.io/tech/sxchnge/shared/logging"
|
||||
"git.sendico.io/tech/sxchnge/shared/service"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// BaseResponse is a general structure for all API responses.
|
||||
type BaseResponse struct {
|
||||
Status string `json:"status"` // "success" or "error"
|
||||
Data any `json:"data"` // The actual data payload or the error details
|
||||
}
|
||||
|
||||
// ErrorResponse provides more details about an error.
|
||||
type ErrorResponse struct {
|
||||
Code int `json:"code"` // A unique identifier for the error type, useful for client handling
|
||||
Error string `json:"error"`
|
||||
Source string `json:"source"`
|
||||
Details any `json:"details"` // Additional details or hints about the error, if necessary
|
||||
}
|
||||
|
||||
func errMessage(err error) string {
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func logRequest(logger mlogger.Logger, r *http.Request, message string) {
|
||||
logger.Debug(
|
||||
message,
|
||||
zap.String("host", r.Host),
|
||||
zap.String("address", r.RemoteAddr),
|
||||
zap.String("method", r.Method),
|
||||
zap.String("request_uri", r.RequestURI),
|
||||
zap.String("proto", r.Proto),
|
||||
zap.String("user_agent", r.UserAgent()),
|
||||
)
|
||||
}
|
||||
|
||||
func writeJSON(logger mlogger.Logger, w http.ResponseWriter, r *http.Request, code int, payload any) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
w.Header().Set("Pragma", "no-cache")
|
||||
w.WriteHeader(code)
|
||||
if err := json.NewEncoder(w).Encode(&payload); err != nil {
|
||||
logger.Warn("Failed to encode JSON response",
|
||||
zap.Error(err),
|
||||
zap.String("host", r.Host),
|
||||
zap.String("address", r.RemoteAddr),
|
||||
zap.String("method", r.Method),
|
||||
zap.String("request_uri", r.RequestURI),
|
||||
zap.String("proto", r.Proto),
|
||||
zap.String("user_agent", r.UserAgent()))
|
||||
}
|
||||
}
|
||||
|
||||
func errorf(
|
||||
logger mlogger.Logger,
|
||||
w http.ResponseWriter, r *http.Request,
|
||||
source mservice.Type, code int, message string, details any,
|
||||
) {
|
||||
logRequest(logger, r, message)
|
||||
|
||||
errorMessage := BaseResponse{
|
||||
Status: api.MSError,
|
||||
Data: ErrorResponse{
|
||||
Code: code,
|
||||
Details: details,
|
||||
Source: source,
|
||||
Error: message,
|
||||
},
|
||||
}
|
||||
|
||||
writeJSON(logger, w, r, code, errorMessage)
|
||||
}
|
||||
|
||||
func Accepted(logger mlogger.Logger, data any) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := BaseResponse{
|
||||
Status: api.MSSuccess,
|
||||
Data: data,
|
||||
}
|
||||
writeJSON(logger, w, r, http.StatusAccepted, resp)
|
||||
}
|
||||
}
|
||||
|
||||
func Ok(logger mlogger.Logger, data any) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := BaseResponse{
|
||||
Status: api.MSSuccess,
|
||||
Data: data,
|
||||
}
|
||||
writeJSON(logger, w, r, http.StatusOK, resp)
|
||||
}
|
||||
}
|
||||
|
||||
func Created(logger mlogger.Logger, data any) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := BaseResponse{
|
||||
Status: api.MSSuccess,
|
||||
Data: data,
|
||||
}
|
||||
writeJSON(logger, w, r, http.StatusCreated, resp)
|
||||
}
|
||||
}
|
||||
|
||||
func Auto(logger mlogger.Logger, source mservice.Type, err error) http.HandlerFunc {
|
||||
if err == nil {
|
||||
return Success(logger)
|
||||
}
|
||||
if errors.Is(err, merrors.ErrAccessDenied) {
|
||||
return AccessDenied(logger, source, errMessage(err))
|
||||
}
|
||||
if errors.Is(err, merrors.ErrDataConflict) {
|
||||
return DataConflict(logger, source, errMessage(err))
|
||||
}
|
||||
if errors.Is(err, merrors.ErrInvalidArg) {
|
||||
return BadRequest(logger, source, "invalid_argument", errMessage(err))
|
||||
}
|
||||
if errors.Is(err, merrors.ErrNoData) {
|
||||
return NotFound(logger, source, errMessage(err))
|
||||
}
|
||||
if errors.Is(err, merrors.ErrUnauthorized) {
|
||||
return Unauthorized(logger, source, errMessage(err))
|
||||
}
|
||||
if errors.Is(err, merrors.ErrServiceUnavailable) {
|
||||
return Error(logger, source, http.StatusServiceUnavailable, "service_unavailable", errMessage(err))
|
||||
}
|
||||
return Internal(logger, source, err)
|
||||
}
|
||||
|
||||
func Internal(logger mlogger.Logger, source mservice.Type, err error) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
errorf(logger, w, r, source, http.StatusInternalServerError, "internal_error", errMessage(err))
|
||||
}
|
||||
}
|
||||
|
||||
func NotImplemented(logger mlogger.Logger, source mservice.Type, hint string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
errorf(logger, w, r, source, http.StatusNotImplemented, "not_implemented", hint)
|
||||
}
|
||||
}
|
||||
|
||||
func BadRequest(logger mlogger.Logger, source mservice.Type, err, hint string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
errorf(logger, w, r, source, http.StatusBadRequest, err, hint)
|
||||
}
|
||||
}
|
||||
|
||||
func Gone(logger mlogger.Logger, source mservice.Type, err, hint string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
errorf(logger, w, r, source, http.StatusGone, err, hint)
|
||||
}
|
||||
}
|
||||
|
||||
func BadQueryParam(logger mlogger.Logger, source mservice.Type, param string, err error) http.HandlerFunc {
|
||||
return BadRequest(logger, source, "invalid_query_parameter", fmt.Sprintf("Failed to parse '%s': %v", param, err))
|
||||
}
|
||||
|
||||
func BadReference(logger mlogger.Logger, source mservice.Type, refName, refVal string, err error) http.HandlerFunc {
|
||||
return BadRequest(logger, source, "broken_reference",
|
||||
fmt.Sprintf("broken object reference: %s = %s, error: %v", refName, refVal, err))
|
||||
}
|
||||
|
||||
func BadPayload(logger mlogger.Logger, source mservice.Type, err error) http.HandlerFunc {
|
||||
return BadRequest(logger, source, "broken_payload",
|
||||
fmt.Sprintf("broken '%s' object payload, error: %v", source, err))
|
||||
}
|
||||
|
||||
func DataConflict(logger mlogger.Logger, source mservice.Type, hint string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
errorf(logger, w, r, source, http.StatusConflict, "data_conflict", hint)
|
||||
}
|
||||
}
|
||||
|
||||
func Error(logger mlogger.Logger, source mservice.Type, code int, errType, hint string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
errorf(logger, w, r, source, code, errType, hint)
|
||||
}
|
||||
}
|
||||
|
||||
func ErrorWithDetails(logger mlogger.Logger, source mservice.Type, code int, errType string, details any) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
errorf(logger, w, r, source, code, errType, details)
|
||||
}
|
||||
}
|
||||
|
||||
func AccessDenied(logger mlogger.Logger, source mservice.Type, hint string) http.HandlerFunc {
|
||||
return Error(logger, source, http.StatusForbidden, "access_denied", hint)
|
||||
}
|
||||
|
||||
func Forbidden(logger mlogger.Logger, source mservice.Type, errType, hint string) http.HandlerFunc {
|
||||
return Error(logger, source, http.StatusForbidden, errType, hint)
|
||||
}
|
||||
|
||||
func TooManyRequests(logger mlogger.Logger, source mservice.Type, hint string) http.HandlerFunc {
|
||||
return Error(logger, source, http.StatusTooManyRequests, "too_many_requests", hint)
|
||||
}
|
||||
|
||||
func LicenseRequired(logger mlogger.Logger, source mservice.Type, hint string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
errorf(logger, w, r, source, http.StatusPaymentRequired, "license_required", hint)
|
||||
}
|
||||
}
|
||||
|
||||
func Unauthorized(logger mlogger.Logger, source mservice.Type, hint string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
errorf(logger, w, r, source, http.StatusUnauthorized, "unauthorized", hint)
|
||||
}
|
||||
}
|
||||
|
||||
func NotFound(logger mlogger.Logger, source mservice.Type, hint string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
errorf(logger, w, r, source, http.StatusNotFound, "not_found", hint)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user