Files
reloading-manager/backend/handlers/loads/handler.go
Ron Rise dd383b6fb3
All checks were successful
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 7m28s
🧪 ✨ Unit Tests Workflow / 🧪 🐹 GolangCI-Lint (push) Successful in 7m40s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 10m24s
fix tpyo (#8)
Reviewed-on: Siteworxpro/reloading-manager#8
Co-authored-by: Ron Rise <ron@siteworxpro.com>
Co-committed-by: Ron Rise <ron@siteworxpro.com>
2025-06-10 15:56:03 +00:00

512 lines
12 KiB
Go

package loads
import (
"context"
"fmt"
"git.siteworxpro.com/reloading-manager/backend/.gen/loading/public/table"
"git.siteworxpro.com/reloading-manager/backend/database"
"git.siteworxpro.com/reloading-manager/backend/handlers"
"git.siteworxpro.com/reloading-manager/backend/handlers/bullets"
"git.siteworxpro.com/reloading-manager/backend/handlers/primers"
"git.siteworxpro.com/reloading-manager/backend/models/loads"
"github.com/go-jet/jet/v2/postgres"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
"github.com/labstack/echo/v4"
"net/http"
"strings"
)
type row struct {
ID pgtype.UUID
Col float32
PowderGr float32
CartridgeID pgtype.UUID
CartridgeName string
CartridgeMeta []byte
BulletID pgtype.UUID
BulletName string
BulletDiameter int32
BulletWeight int32
BulletMeta []byte
BulletManufacturerName string
BulletManufacturerUrl pgtype.Text
PrimerID pgtype.UUID
PrimerName string
PrimerMeta []byte
PrimerManufacturerName string
PrimerManufacturerUrl pgtype.Text
PowderID pgtype.UUID
PowderName string
PowderMeta []byte
PowderManufacturerName string
PowderManufacturerUrl pgtype.Text
}
type loadResponseResults struct {
Id string `json:"id"`
Cartridge string `json:"cartridge"`
CartridgeId string `json:"cartridge_id"`
Col float32 `json:"col"`
Powder handlers.Powder `json:"powder"`
PowderGr float32 `json:"powder_gr"`
Primer primers.PrimerResponse `json:"primer"`
Bullet bullets.BulletResponse `json:"bullet"`
}
type loadResponse struct {
Total int `json:"total"`
Results []loadResponseResults `json:"results"`
}
type ResultChan[T any] struct {
Result T
Err error
}
func Post(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer func() {
_ = db.Db.Close(context.Background())
}()
cartridgeID, err := handlers.ParseUuid(c.FormValue("cartridge_id"))
if err != nil {
return handlers.BadRequest(c, "cartridge_id is not a valid UUID")
}
powderId, err := handlers.ParseUuid(c.FormValue("powder_id"))
if err != nil {
return handlers.BadRequest(c, "powder_id is not a valid UUID")
}
primerId, err := handlers.ParseUuid(c.FormValue("primer_id"))
if err != nil {
return handlers.BadRequest(c, "primer_id is not a valid UUID")
}
bulletId, err := handlers.ParseUuid(c.FormValue("bullet_id"))
if err != nil {
return handlers.BadRequest(c, "bullet_id is not a valid UUID")
}
file, err := handlers.ReadFile(c, "photo")
if err != nil {
return handlers.BadRequest(c, "photo is not valid")
}
meta := c.FormValue("meta")
if meta == "" {
meta = "{}"
}
powderGr := c.FormValue("powder_gr")
if powderGr == "" {
return handlers.BadRequest(c, "powder_gr is not valid")
}
powderGrFl, err := handlers.ParseFloat32(powderGr)
if err != nil {
return handlers.BadRequest(c, "powder_gr is not valid")
}
col := c.FormValue("col")
if col == "" {
return handlers.BadRequest(c, "col is not valid")
}
colFl, err := handlers.ParseFloat32(col)
if err != nil {
return handlers.BadRequest(c, "col is not valid")
}
uid, err := db.Loads.CreateLoad(context.Background(), loads.CreateLoadParams{
CartridgeID: *cartridgeID,
Col: colFl,
PowderID: *powderId,
PowderGr: powderGrFl,
PrimerID: *primerId,
BulletID: *bulletId,
Photo: file,
Meta: []byte(meta),
})
if err != nil {
return err
}
return c.JSON(http.StatusCreated, handlers.Response[string]{Payload: uid.String()})
}
func Get(c echo.Context) error {
id := c.Param("id")
cResults := make(chan ResultChan[[]loadResponseResults])
if id != "" {
go execResultsQuery(cResults, c)
results := <-cResults
if results.Err != nil {
return results.Err
}
if len(results.Result) == 0 {
return handlers.NotFound(c, "load not found")
}
return c.JSON(http.StatusOK, handlers.Response[loadResponseResults]{Status: http.StatusText(http.StatusOK), Payload: results.Result[0]})
}
cTotal := make(chan ResultChan[int64])
go func(ch chan ResultChan[int64]) {
db := database.GetNewDatabase()
defer func(Db *pgx.Conn, ctx context.Context) {
_ = Db.Close(ctx)
}(db.Db, context.Background())
q := getQuery(c, true)
if q == nil {
ch <- ResultChan[int64]{Result: 0}
return
}
sql, params := q.Sql()
var total int64
r := db.Db.QueryRow(context.Background(), sql, params...)
err := r.Scan(&total)
if err != nil {
ch <- ResultChan[int64]{Err: err}
} else {
ch <- ResultChan[int64]{Result: total}
}
}(cTotal)
go execResultsQuery(cResults, c)
total := <-cTotal
if total.Err != nil {
return total.Err
}
results := <-cResults
if results.Err != nil {
return results.Err
}
return c.JSON(http.StatusOK, handlers.Response[loadResponse]{Status: http.StatusText(http.StatusOK), Payload: loadResponse{Total: int(total.Result), Results: results.Result}})
}
func execResultsQuery(ch chan ResultChan[[]loadResponseResults], c echo.Context) {
db := database.GetNewDatabase()
defer func(Db *pgx.Conn, ctx context.Context) {
_ = Db.Close(ctx)
}(db.Db, context.Background())
q := getQuery(c, false)
if q == nil {
ch <- ResultChan[[]loadResponseResults]{Result: []loadResponseResults{}}
return
}
fmt.Println(q.DebugSql())
sql, params := q.Sql()
rows, err := db.Db.Query(context.Background(), sql, params...)
if err != nil {
ch <- ResultChan[[]loadResponseResults]{Err: err}
return
}
results := make([]loadResponseResults, 0)
for rows.Next() {
row := row{}
err = rows.Scan(
&row.ID,
&row.Col,
&row.PowderGr,
&row.CartridgeID,
&row.CartridgeName,
&row.CartridgeMeta,
&row.BulletID,
&row.BulletName,
&row.BulletDiameter,
&row.BulletWeight,
&row.BulletMeta,
&row.BulletManufacturerName,
&row.BulletManufacturerUrl,
&row.PrimerID,
&row.PrimerName,
&row.PrimerMeta,
&row.PrimerManufacturerName,
&row.PrimerManufacturerUrl,
&row.PowderID,
&row.PowderName,
&row.PowderMeta,
&row.PowderManufacturerName,
&row.PowderManufacturerUrl,
)
if err != nil {
ch <- ResultChan[[]loadResponseResults]{Err: err}
return
}
results = append(results, loadResponseResults{
Id: row.ID.String(),
Cartridge: row.CartridgeName,
CartridgeId: row.CartridgeID.String(),
Col: row.Col,
Powder: handlers.Powder{
Id: row.PowderID.String(),
Name: row.PowderName,
Meta: string(row.PowderMeta),
Manufacturer: handlers.Manufacturer{
Name: row.PowderManufacturerName,
Url: row.PowderManufacturerUrl.String,
},
},
PowderGr: row.PowderGr,
Primer: primers.PrimerResponse{
ID: row.PrimerID.String(),
Name: row.PrimerName,
Manufacturer: handlers.Manufacturer{
Name: row.PrimerManufacturerName,
Url: row.PrimerManufacturerUrl.String,
},
},
Bullet: bullets.BulletResponse{
Id: row.BulletID.String(),
Name: row.BulletName,
Diameter: row.BulletDiameter,
Weight: row.BulletWeight,
Manufacturer: handlers.Manufacturer{
Name: row.BulletManufacturerName,
Url: row.BulletManufacturerUrl.String,
},
},
})
}
ch <- ResultChan[[]loadResponseResults]{Result: results}
}
func getQuery(c echo.Context, countOnly bool) postgres.SelectStatement {
l := table.Loads.AS("l")
ctg := table.Cartridges.AS("c")
b := table.Bullets.AS("b")
bm := table.Manufacturers.AS("b_m")
p := table.Primers.AS("p")
pm := table.Manufacturers.AS("p_m")
pwd := table.Powders.AS("pw")
pwdm := table.Manufacturers.AS("pw_m")
tb := l.
INNER_JOIN(ctg, l.CartridgeID.EQ(ctg.ID)).
INNER_JOIN(b, l.BulletID.EQ(b.ID)).
INNER_JOIN(bm, b.ManufacturerID.EQ(bm.ID)).
INNER_JOIN(p, l.PrimerID.EQ(p.ID)).
INNER_JOIN(pm, p.ManufacturerID.EQ(pm.ID)).
INNER_JOIN(pwd, l.PowderID.EQ(pwd.ID)).
INNER_JOIN(pwdm, pwd.ManufacturerID.EQ(pwdm.ID))
var q postgres.SelectStatement
if countOnly {
q = tb.SELECT(postgres.COUNT(l.ID).AS("total"))
} else {
q = tb.SELECT(
// Load
l.ID.AS("id"),
l.Col.AS("col"),
l.PowderGr.AS("powder_gr"),
// Cartridge
ctg.ID.AS("cartridge_id"),
ctg.Name.AS("cartridge_name"),
ctg.Meta.AS("cartridge_meta"),
// Bullet
b.ID.AS("bullet_id"),
b.Name.AS("bullet_name"),
b.Diameter.AS("bullet_diameter"),
b.Weight.AS("bullet_weight"),
b.Meta.AS("bullet_meta"),
bm.Name.AS("bullet_manufacturer_name"),
bm.URL.AS("bullet_manufacturer_url"),
// Primer
p.ID.AS("primer_id"),
p.Name.AS("primer_name"),
p.Meta.AS("primer_meta"),
pm.Name.AS("primer_manufacturer_name"),
pm.URL.AS("primer_manufacturer_url"),
// Powder
pwd.ID.AS("powder_id"),
pwd.Name.AS("powder_name"),
pwd.Meta.AS("powder_meta"),
pwdm.Name.AS("powder_manufacturer_name"),
pwdm.URL.AS("powder_manufacturer_url"),
)
}
// where expressions
expressions := make([]postgres.BoolExpression, 0)
if c.Param("id") != "" {
uuid, err := handlers.ParseUuid(c.Param("id"))
if err != nil {
return nil
}
q = q.WHERE(l.ID.EQ(postgres.UUID(uuid)))
return q
}
if c.QueryParam("cartridge_id") != "" {
ids := strings.Split(c.QueryParam("cartridge_id"), ",")
if len(ids) > 0 {
expressions = append(expressions, ctg.ID.IN(getUuidExpr(ids)...))
}
}
if c.QueryParam("bullet_manufacturer_id") != "" {
ids := strings.Split(c.QueryParam("bullet_manufacturer_id"), ",")
if len(ids) > 0 {
expressions = append(expressions, bm.ID.IN(getUuidExpr(ids)...))
}
}
if c.QueryParam("bullet_id") != "" {
ids := strings.Split(c.QueryParam("bullet_id"), ",")
if len(ids) > 0 {
expressions = append(expressions, b.ID.IN(getUuidExpr(ids)...))
}
}
if c.QueryParam("primer_manufacturer_id") != "" {
ids := strings.Split(c.QueryParam("primer_manufacturer_id"), ",")
if len(ids) > 0 {
expressions = append(expressions, pm.ID.IN(getUuidExpr(ids)...))
}
}
if c.QueryParam("primer_id") != "" {
ids := strings.Split(c.QueryParam("primer_id"), ",")
if len(ids) > 0 {
expressions = append(expressions, p.ID.IN(getUuidExpr(ids)...))
}
}
if c.QueryParam("powder_manufacturer_id") != "" {
ids := strings.Split(c.QueryParam("powder_manufacturer_id"), ",")
if len(ids) > 0 {
expressions = append(expressions, pwdm.ID.IN(getUuidExpr(ids)...))
}
}
if c.QueryParam("powder_id") != "" {
ids := strings.Split(c.QueryParam("powder_id"), ",")
if len(ids) > 0 {
expressions = append(expressions, pwd.ID.IN(getUuidExpr(ids)...))
}
}
if c.QueryParam("search_cartridge") != "" {
expressions = append(expressions, ctg.Name.LIKE(postgres.String(c.QueryParam("search_cartridge")+"%")))
}
if c.QueryParam("search_bullet") != "" {
expressions = append(expressions, b.Name.LIKE(postgres.String(c.QueryParam("search_bullet")+"%")))
}
var where postgres.BoolExpression
if len(expressions) > 0 {
for _, expr := range expressions {
if where == nil {
where = expr
} else {
where = where.AND(expr)
}
}
}
if where != nil {
q = q.WHERE(where)
}
if countOnly {
return q
}
orderBy := c.QueryParam("sortField")
var sort postgres.Column
switch orderBy {
case "cartridge_name":
sort = ctg.Name
case "bullet_name":
sort = b.Name
case "bullet_manufacturer_name":
sort = bm.Name
case "primer_manufacturer_name":
sort = pm.Name
case "primer_name":
sort = p.Name
case "powder_manufacturer_name":
sort = pwdm.Name
case "powder_name":
sort = pwd.Name
case "powder_grs":
sort = l.PowderGr
default:
sort = ctg.Name
}
if c.QueryParam("sortOrder") == "desc" {
q.ORDER_BY(sort.DESC())
} else {
q.ORDER_BY(sort.ASC())
}
limit := c.QueryParam("limit")
if limit == "" {
limit = "50"
}
pageInt := handlers.ParseInt64OrDefault(c.QueryParam("page"), 1)
if pageInt < 1 {
pageInt = 1
}
offset := (pageInt - 1) * handlers.ParseInt64OrDefault(limit, 50)
q = q.LIMIT(handlers.ParseInt64OrDefault(limit, 50)).
OFFSET(offset)
return q
}
func getUuidExpr(ids []string) []postgres.Expression {
expr := make([]postgres.Expression, 0)
for _, id := range ids {
uuid, err := handlers.ParseUuid(id)
if err != nil {
continue
}
expr = append(expr, postgres.UUID(uuid))
}
return expr
}