budget-go/backend/controller/crud/crud.go
2025-02-08 20:19:26 +01:00

263 lines
4.9 KiB
Go

package crud
import (
"math"
"net/url"
"strconv"
"strings"
"github.com/labstack/echo/v4"
"gitnet.fr/deblan/budget/database/manager"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type UpdateCallback func(*gorm.DB, interface{}, interface{}) (interface{}, error)
type CreateCallback func(*gorm.DB, interface{}) (interface{}, error)
type Error struct {
Code int `json:"code"`
Message string `json:"message"`
}
type Controller struct {
Config Configuration
}
func New() *Controller {
c := Controller{}
return &c
}
func (ctrl *Controller) With(config Configuration) *Controller {
ctrl.Config = config
return ctrl
}
func (ctrl *Controller) List(c echo.Context) error {
db := manager.Get().Db
db = db.Model(ctrl.Config.Model)
order := c.QueryParam("order")
sort := c.QueryParam("sort")
if !ctrl.Config.IsValidOrder(order) {
order = ctrl.Config.GetDefaultOrder()
sort = ctrl.Config.GetDefaultSort()
}
db.Order(clause.OrderByColumn{
Column: clause.Column{Name: order},
Desc: sort == "desc",
})
if ctrl.Config.ListQuery != nil {
ctrl.Config.ListQuery(db)
}
data := ListData{
Limit: ctrl.Config.DefaultLimit,
Order: order,
Sort: sort,
}
limit, err := strconv.Atoi(c.QueryParam("limit"))
if err == nil && ctrl.Config.IsValidLimit(limit) {
data.Limit = limit
}
page, err := strconv.Atoi(c.QueryParam("page"))
if err == nil && page > 0 {
data.Page = page
}
ctrl.ApplyFilters(c.QueryParams(), db)
ctrl.Paginate(&data, db)
return c.JSON(200, data)
}
func (ctrl *Controller) Show(c echo.Context) error {
db := manager.Get().Db
value, err := strconv.Atoi(c.Param("id"))
if err != nil {
return c.JSON(400, Error{
Code: 400,
Message: err.Error(),
})
}
var count int64
db.Model(ctrl.Config.Model).Where("id = ?", value).Count(&count)
if count == 0 {
return c.JSON(404, Error{
Code: 404,
Message: "Not found",
})
}
item := ctrl.Config.CreateModel()
db.Model(ctrl.Config.Model).Where("id = ?", value)
if ctrl.Config.ItemQuery != nil {
ctrl.Config.ItemQuery(db)
}
db.First(&item)
return c.JSON(200, item)
}
func (ctrl *Controller) Delete(c echo.Context) error {
db := manager.Get().Db
value, err := strconv.Atoi(c.Param("id"))
if err != nil {
return c.JSON(400, Error{
Code: 400,
Message: err.Error(),
})
}
var count int64
db.Model(ctrl.Config.Model).Where("id = ?", value).Count(&count)
if count == 0 {
return c.JSON(404, Error{
Code: 404,
Message: "Not found",
})
}
item := ctrl.Config.CreateModel()
db.Model(ctrl.Config.Model).Where("id = ?", value).Delete(&item)
return c.JSON(200, nil)
}
func (ctrl *Controller) Create(c echo.Context, body interface{}, createCallback CreateCallback) error {
db := manager.Get().Db
if err := c.Bind(body); err != nil {
return c.JSON(400, Error{
Code: 400,
Message: "Bad request",
})
}
if err := c.Validate(body); err != nil {
return c.JSON(400, Error{
Code: 400,
Message: err.Error(),
})
}
result, err := createCallback(db, body)
if err != nil {
return c.JSON(400, Error{
Code: 400,
Message: err.Error(),
})
}
return c.JSON(200, result)
}
func (ctrl *Controller) Update(c echo.Context, body interface{}, updateCallback UpdateCallback) error {
db := manager.Get().Db
value, err := strconv.Atoi(c.Param("id"))
if err != nil {
return c.JSON(400, Error{
Code: 400,
Message: err.Error(),
})
}
var count int64
db.Model(ctrl.Config.Model).Where("id = ?", value).Count(&count)
if count == 0 {
return c.JSON(404, Error{
Code: 404,
Message: "Not found",
})
}
item := ctrl.Config.CreateModel()
db = db.Model(ctrl.Config.Model)
if ctrl.Config.ItemQuery != nil {
ctrl.Config.ItemQuery(db)
}
db.First(&item, value)
if err := c.Bind(body); err != nil {
return c.JSON(400, Error{
Code: 400,
Message: err.Error(),
})
}
if err := c.Validate(body); err != nil {
return c.JSON(400, Error{
Code: 400,
Message: err.Error(),
})
}
result, err := updateCallback(db, item, body)
if err != nil {
return c.JSON(400, Error{
Code: 400,
Message: err.Error(),
})
}
return c.JSON(200, result)
}
func (ctrl *Controller) Paginate(data *ListData, db *gorm.DB) {
var totalRows int64
db.Model(ctrl.Config.Model).Count(&totalRows)
data.TotalRows = totalRows
totalPages := int(math.Ceil(float64(totalRows) / float64(data.Limit)))
data.TotalPages = totalPages
if data.Limit > 0 {
db.Offset(data.GetOffset())
db.Limit(data.GetLimit())
}
db.Find(&ctrl.Config.Models)
data.Rows = ctrl.Config.Models
}
func (ctrl *Controller) ApplyFilters(value url.Values, db *gorm.DB) {
for name, value := range value {
elements := strings.Split(name, "__")
if len(elements) != 2 {
continue
}
field := elements[0]
comparator := elements[1]
if !ctrl.Config.IsValidOrder(field) {
continue
}
manager.DbComparator(db, ctrl.Config.Table, field, comparator, value[0])
}
}