middleware/query/filter.go

105 lines
2.4 KiB
Go
Raw Normal View History

2024-01-07 03:00:59 +00:00
package query
import (
"context"
"net/http"
"go.pkg.cx/middleware"
)
// Context keys
var (
FilterCtxKey = &middleware.CtxKey{Pkg: "go.pkg.cx/middleware/query", Name: "Filter"}
)
// FilterOptions turns a list of option instances into an option
func FilterOptions[T any](opts ...FilterOption[T]) FilterOption[T] {
return func(f *filter[T]) {
for _, opt := range opts {
opt(f)
}
}
}
// FilterOption configures filter middleware
type FilterOption[T any] func(f *filter[T])
// WithParseFilterFn adds filter parse function to the list
func WithParseFilterFn[T any](fn func(r *http.Request) (T, error)) FilterOption[T] {
return func(f *filter[T]) {
f.parseFilterFns = append(f.parseFilterFns, fn)
}
}
// SetFilterDefault sets filter default value
func SetFilterDefault[T any](defaultValue T) FilterOption[T] {
return func(f *filter[T]) {
f.defaultFilter = defaultValue
}
}
// SetResponseHandler sets filter response handler
func SetResponseHandler[T any](fn middleware.ResponseHandle) FilterOption[T] {
return func(f *filter[T]) {
f.responseHandler = fn
}
}
type filter[T any] struct {
defaultFilter T
parseFilterFns []func(r *http.Request) (T, error)
responseHandler middleware.ResponseHandle
}
// Filter returns filter middleware
func Filter[T any](opts ...FilterOption[T]) func(next http.Handler) http.Handler {
f := &filter[T]{
defaultFilter: *new(T),
parseFilterFns: []func(r *http.Request) (T, error){},
2024-01-07 03:09:11 +00:00
responseHandler: middleware.RespondWithBadRequest,
2024-01-07 03:00:59 +00:00
}
for _, opt := range opts {
opt(f)
}
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
filter := f.defaultFilter
var err error
for _, fn := range f.parseFilterFns {
if filter, err = fn(r); err == nil {
break
}
}
if err != nil {
f.responseHandler(w, r, err)
return
}
ctx := r.Context()
ctx = context.WithValue(ctx, FilterCtxKey, filter)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
// FilterFromContext returns filter from context
func FilterFromContext[T any](ctx context.Context) T {
if filter, ok := ctx.Value(FilterCtxKey).(T); ok {
return filter
}
return *new(T)
}
// FilterFromQuery returns filter string from query params
func FilterFromQuery(filterParam string) func(r *http.Request) string {
return func(r *http.Request) string {
return r.URL.Query().Get(filterParam)
}
}