Files
tm_back/pkg/response/response.go
T

222 lines
5.7 KiB
Go

package response
import (
"errors"
"net/http"
"github.com/asaskevich/govalidator"
"github.com/labstack/echo/v4"
orm "tm/pkg/mongo"
)
// APIResponse represents a standard API response structure
type APIResponse struct {
Success bool `json:"success"`
Message string `json:"message,omitempty"`
Data interface{} `json:"data,omitempty"`
Error *APIError `json:"error,omitempty"`
Meta *Meta `json:"meta,omitempty"`
}
// APIError represents error details in API response
type APIError struct {
Code string `json:"code"`
Message string `json:"message"`
Details string `json:"details,omitempty"`
}
// Meta represents metadata for paginated responses
type Meta struct {
Total int `json:"total"`
Limit int `json:"limit"`
Offset int `json:"offset"`
Page int `json:"page"`
Pages int `json:"pages"`
HasMore bool `json:"has_more"`
NextCursor string `json:"next_cursor,omitempty"`
}
type Pagination struct {
Limit int `query:"limit" valid:"optional,range(1|100)" default:"10"`
Offset int `query:"offset" valid:"optional,range(0|1000000)" default:"0"`
Cursor string `query:"cursor" valid:"optional"`
SortBy string `query:"sort_by" valid:"optional"`
SortOrder string `query:"sort_order" valid:"optional,in(asc|desc)"`
}
// Success returns a successful response
func Success(c echo.Context, data interface{}, message string) error {
return c.JSON(http.StatusOK, APIResponse{
Success: true,
Message: message,
Data: data,
})
}
// SuccessWithMeta returns a successful response with metadata
func SuccessWithMeta(c echo.Context, data interface{}, meta *Meta, message string) error {
return c.JSON(http.StatusOK, APIResponse{
Success: true,
Message: message,
Data: data,
Meta: meta,
})
}
// Created returns a 201 Created response
func Created(c echo.Context, data interface{}, message string) error {
return c.JSON(http.StatusCreated, APIResponse{
Success: true,
Message: message,
Data: data,
})
}
// Accepted returns a 202 Accepted response (for async operations)
func Accepted(c echo.Context, data interface{}, message string) error {
return c.JSON(http.StatusAccepted, APIResponse{
Success: true,
Message: message,
Data: data,
})
}
// BadRequest returns a 400 Bad Request response
func BadRequest(c echo.Context, message, details string) error {
return c.JSON(http.StatusBadRequest, APIResponse{
Success: false,
Message: "Bad Request",
Error: &APIError{
Code: "BAD_REQUEST",
Message: message,
Details: details,
},
})
}
// Unauthorized returns a 401 Unauthorized response
func Unauthorized(c echo.Context, message string) error {
return c.JSON(http.StatusUnauthorized, APIResponse{
Success: false,
Message: "Unauthorized",
Error: &APIError{
Code: "UNAUTHORIZED",
Message: message,
},
})
}
// Forbidden returns a 403 Forbidden response
func Forbidden(c echo.Context, message string) error {
return c.JSON(http.StatusForbidden, APIResponse{
Success: false,
Message: "Forbidden",
Error: &APIError{
Code: "FORBIDDEN",
Message: message,
},
})
}
// NotFound returns a 404 Not Found response
func NotFound(c echo.Context, message string) error {
return c.JSON(http.StatusNotFound, APIResponse{
Success: false,
Message: "Not Found",
Error: &APIError{
Code: "NOT_FOUND",
Message: message,
},
})
}
// Conflict returns a 409 Conflict response
func Conflict(c echo.Context, message string) error {
return c.JSON(http.StatusConflict, APIResponse{
Success: false,
Message: "Conflict",
Error: &APIError{
Code: "CONFLICT",
Message: message,
},
})
}
// TooManyRequests returns a 429 Too Many Requests response
func TooManyRequests(c echo.Context, message string) error {
return c.JSON(http.StatusTooManyRequests, APIResponse{
Success: false,
Message: "Too Many Requests",
Error: &APIError{
Code: "TOO_MANY_REQUESTS",
Message: message,
},
})
}
// ValidationError returns a 422 Unprocessable Entity response
func ValidationError(c echo.Context, message, details string) error {
return c.JSON(http.StatusUnprocessableEntity, APIResponse{
Success: false,
Message: "Validation Error",
Error: &APIError{
Code: "VALIDATION_ERROR",
Message: message,
Details: details,
},
})
}
// InternalServerError returns a 500 Internal Server Error response
func InternalServerError(c echo.Context, message string) error {
return c.JSON(http.StatusInternalServerError, APIResponse{
Success: false,
Message: "Internal Server Error",
Error: &APIError{
Code: "INTERNAL_SERVER_ERROR",
Message: message,
},
})
}
// ServiceUnavailable returns a 503 Service Unavailable response
func ServiceUnavailable(c echo.Context, message string) error {
return c.JSON(http.StatusServiceUnavailable, APIResponse{
Success: false,
Message: "Service Unavailable",
Error: &APIError{
Code: "SERVICE_UNAVAILABLE",
Message: message,
},
})
}
// Parse parses the form and validates it
func Parse[T any](c echo.Context) (*T, error) {
var form T
if err := c.Bind(&form); err != nil {
return nil, err
}
if _, err := govalidator.ValidateStruct(form); err != nil {
return nil, err
}
return &form, nil
}
// PaginationBadRequest writes a 400 response for invalid list pagination parameters.
func PaginationBadRequest(c echo.Context, err error) error {
msg := "Invalid pagination parameters"
if errors.Is(err, orm.ErrInvalidCursor) {
msg = "Invalid pagination cursor"
}
return BadRequest(c, msg, err.Error())
}
// IsListPaginationError reports client-facing pagination errors from list queries.
func IsListPaginationError(err error) bool {
return IsPaginationError(err) || errors.Is(err, orm.ErrInvalidCursor) || errors.Is(err, orm.ErrInvalidPagination)
}