Commit 7d756ab

mo khan <mo@mokhan.ca>
2025-05-20 20:45:07
feat: add ability to chain middleware to form a pipeline
1 parent 444d6e5
pkg/x/iterate.go
@@ -1,5 +1,7 @@
 package x
 
+import "slices"
+
 type Mapper[T any, K any] func(T) K
 type Predicate[T any] func(T) bool
 type Visitor[T any] func(T)
@@ -51,3 +53,8 @@ func Inject[TInput any, TOutput any](items []TInput, memo TOutput, f func(TOutpu
 func Prepend[T any](rest []T, beginning ...T) []T {
 	return append(beginning, rest...)
 }
+
+func Reverse[T any](items []T) []T {
+	slices.Reverse(items)
+	return items
+}
pkg/x/iterate_test.go
@@ -153,3 +153,11 @@ func TestPrepend(t *testing.T) {
 		assert.Equal(t, []int{7, 9, 1, 3, 6}, result)
 	})
 }
+
+func TestReverse(t *testing.T) {
+	t.Run("reverses a slice of ints", func(t *testing.T) {
+		items := []int{1, 2, 3, 4, 5}
+
+		assert.Equal(t, []int{5, 4, 3, 2, 1}, Reverse[int](items))
+	})
+}
pkg/x/middleware.go
@@ -0,0 +1,8 @@
+package x
+
+func Middleware[T any](item T, items ...Option[T]) T {
+	for _, middleware := range Reverse(items) {
+		item = middleware(item)
+	}
+	return item
+}
pkg/x/middleware_test.go
@@ -0,0 +1,52 @@
+package x
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestMiddleware(t *testing.T) {
+	mux := http.NewServeMux()
+	mux.HandleFunc("/example", func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(http.StatusTeapot)
+	})
+
+	passThrough := func(next http.Handler) http.Handler {
+		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			w.Header().Add("x-pass-through", "true")
+			next.ServeHTTP(w, r)
+		})
+	}
+
+	unauthorized := func(http.Handler) http.Handler {
+		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			w.Header().Add("x-unauthorized", "true")
+			w.WriteHeader(http.StatusUnauthorized)
+		})
+	}
+
+	t.Run("executes a single middleware", func(t *testing.T) {
+		r := httptest.NewRequest("GET", "/example", nil)
+		w := httptest.NewRecorder()
+
+		Middleware[http.Handler](mux, passThrough).ServeHTTP(w, r)
+
+		require.Equal(t, http.StatusTeapot, w.Code)
+		assert.Equal(t, "true", w.HeaderMap.Get("x-pass-through"))
+	})
+
+	t.Run("excutes multiple middleware", func(t *testing.T) {
+		r := httptest.NewRequest("GET", "/example", nil)
+		w := httptest.NewRecorder()
+
+		Middleware[http.Handler](mux, passThrough, unauthorized).ServeHTTP(w, r)
+
+		require.Equal(t, http.StatusUnauthorized, w.Code)
+		assert.Equal(t, "true", w.HeaderMap.Get("x-pass-through"))
+		assert.Equal(t, "true", w.HeaderMap.Get("x-unauthorized"))
+	})
+}