Files
stufflog3/ports/httpapi/common.go
T
2022-07-16 12:44:52 +02:00

119 lines
2.4 KiB
Go

package httpapi
import (
"context"
"crypto/rand"
"encoding/hex"
"git.aiterp.net/stufflog3/stufflog3/models"
"github.com/gin-gonic/gin"
"log"
"strconv"
)
type statusError interface {
HttpStatus() (int, string, interface{})
}
func processorHandler[I any, O any](key, idKey string, callback func(ctx context.Context, id int, input I) (O, error)) gin.HandlerFunc {
return handler(key, func(c *gin.Context) (interface{}, error) {
input := new(I)
err := c.BindJSON(input)
if err != nil {
return nil, models.BadInputError{
Object: "body",
Field: "*",
Problem: "Bad JSON input:" + err.Error(),
}
}
id := 0
if idKey != "" {
id, err = reqInt(c, idKey)
if err != nil {
return nil, err
}
}
return callback(c.Request.Context(), id, *input)
})
}
func getterHandler[O any](key, idKey string, callback func(ctx context.Context, id int) (O, error)) gin.HandlerFunc {
return handler(key, func(c *gin.Context) (interface{}, error) {
id := 0
if idKey != "" {
parsed, err := reqInt(c, idKey)
if err != nil {
return nil, err
}
id = parsed
}
return callback(c.Request.Context(), id)
})
}
func handler(key string, callback func(c *gin.Context) (interface{}, error)) gin.HandlerFunc {
type overrider interface {
OverrideJSONResponse() bool
}
return func(c *gin.Context) {
res, err := callback(c)
if err != nil {
if statusError, ok := err.(statusError); ok {
code, msg, data := statusError.HttpStatus()
c.JSON(code, Error{
Code: code,
Message: msg,
Data: data,
})
} else {
id := make([]byte, 16)
_, _ = rand.Read(id)
idHex := hex.EncodeToString(id)
log.Println("Internal server error", idHex, "triggered by:", err)
c.JSON(500, Error{
Code: 500,
Message: "Internal server error (" + idHex + ")",
Data: nil,
})
}
return
}
if o, ok := res.(overrider); ok && o.OverrideJSONResponse() {
c.JSON(200, res)
return
}
resJson := make(map[string]interface{}, 1)
resJson[key] = res
c.JSON(200, resJson)
}
}
type Error struct {
Code int `json:"statusCode"`
Message string `json:"statusMessage"`
Data interface{} `json:"errorData,omitempty"`
}
func reqInt(c *gin.Context, key string) (int, error) {
v, err := strconv.Atoi(c.Param(key))
if err != nil {
return 0, models.BadInputError{
Object: "query",
Field: key,
Problem: "Value for " + key + " must be numeric",
}
}
return v, nil
}