mirror of
https://github.com/hay-kot/homebox.git
synced 2025-07-23 02:50:28 +00:00
introduce experimental adapter pattern for hdlrs
This commit is contained in:
parent
b82fbb01f5
commit
b0a9c510ad
5 changed files with 153 additions and 2 deletions
|
@ -5,7 +5,8 @@ import (
|
|||
"errors"
|
||||
)
|
||||
|
||||
type UnauthorizedError struct{}
|
||||
type UnauthorizedError struct {
|
||||
}
|
||||
|
||||
func (err *UnauthorizedError) Error() string {
|
||||
return "unauthorized"
|
||||
|
@ -28,7 +29,7 @@ func (err *InvalidRouteKeyError) Error() string {
|
|||
return "invalid route key: " + err.key
|
||||
}
|
||||
|
||||
func NewInvalidRouteKeyError(key string) error {
|
||||
func NewRouteKeyError(key string) error {
|
||||
return &InvalidRouteKeyError{key}
|
||||
}
|
||||
|
||||
|
|
95
backend/internal/web/adapters/adapters.go
Normal file
95
backend/internal/web/adapters/adapters.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Package adapters provides functions to adapt functions to the server.Handler interface.
|
||||
package adapters
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/server"
|
||||
)
|
||||
|
||||
type AdapterFunc[T any, Y any] func(context.Context, T) (Y, error)
|
||||
type IDFunc[T any, Y any] func(context.Context, uuid.UUID, T) (Y, error)
|
||||
|
||||
// Query is a server.Handler that decodes a query from the request and calls the provided function.
|
||||
func Query[T any, Y any](f AdapterFunc[T, Y], ok int) server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
q, err := decodeQuery[T](r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := f(r.Context(), q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return server.Respond(w, ok, res)
|
||||
}
|
||||
}
|
||||
|
||||
// QueryID is a server.Handler that decodes a query and an ID from the request and calls the provided function.
|
||||
func QueryID[T any, Y any](param string, f IDFunc[T, Y], ok int) server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
ID, err := routeUUID(r, param)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q, err := decodeQuery[T](r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := f(r.Context(), ID, q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return server.Respond(w, ok, res)
|
||||
}
|
||||
}
|
||||
|
||||
// Action is a function that adapts a function to the server.Handler interface.
|
||||
// It decodes the request body into a value of type T and passes it to the function f.
|
||||
// The function f is expected to return a value of type Y and an error.
|
||||
//
|
||||
// Note: Action differs from Query in that it decodes the request body.
|
||||
func Action[T any, Y any](f AdapterFunc[T, Y], ok int) server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
v, err := decode[T](r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := f(r.Context(), v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return server.Respond(w, ok, res)
|
||||
}
|
||||
}
|
||||
|
||||
// ActionID functions the same as Action, but it also decodes a UUID from the URL path.
|
||||
func ActionID[T any, Y any](param string, f IDFunc[T, Y], ok int) server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
ID, err := routeUUID(r, param)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v, err := decode[T](r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := f(r.Context(), ID, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return server.Respond(w, ok, res)
|
||||
}
|
||||
}
|
52
backend/internal/web/adapters/decoders.go
Normal file
52
backend/internal/web/adapters/decoders.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
package adapters
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/schema"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/server"
|
||||
)
|
||||
|
||||
var queryDecoder = schema.NewDecoder()
|
||||
|
||||
func decodeQuery[T any](r *http.Request) (T, error) {
|
||||
var v T
|
||||
err := queryDecoder.Decode(&v, r.URL.Query())
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
|
||||
err = validate.Check(v)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func decode[T any](r *http.Request) (T, error) {
|
||||
var v T
|
||||
|
||||
err := server.Decode(r, &v)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
|
||||
err = validate.Check(v)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func routeUUID(r *http.Request, key string) (uuid.UUID, error) {
|
||||
ID, err := uuid.Parse(chi.URLParam(r, key))
|
||||
if err != nil {
|
||||
return uuid.Nil, validate.NewRouteKeyError(key)
|
||||
}
|
||||
return ID, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue