Compare commits
	
		
			2 Commits
		
	
	
		
			a5f83606cd
			...
			fe3c788fba
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | fe3c788fba | ||
|  | e34a8aa7fd | 
| @ -28,7 +28,7 @@ var DefaultOptions = Options( | ||||
| 	WithFindTokenFn(middleware.TokenFromAuthorizationHeader), | ||||
| 	WithFindTokenFn(middleware.TokenFromQuery("jwt")), | ||||
| 	WithFindTokenFn(middleware.TokenFromCookie("jwt")), | ||||
| 	SetResponseHandler(RespondWithUnauthorized), | ||||
| 	SetResponseHandler(middleware.RespondWithUnauthorized), | ||||
| 	SetValidateTokenFn(allowAll), | ||||
| ) | ||||
| 
 | ||||
| @ -160,11 +160,6 @@ func Middleware(key interface{}, alg jwa.SignatureAlgorithm, opts ...Option) fun | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // RespondWithUnauthorized is a default response handler
 | ||||
| func RespondWithUnauthorized(w http.ResponseWriter, _ *http.Request, _ error) { | ||||
| 	http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) | ||||
| } | ||||
| 
 | ||||
| func allowAll(_ jwt.Token) (bool, interface{}) { | ||||
| 	return true, nil | ||||
| } | ||||
|  | ||||
| @ -25,7 +25,7 @@ var DefaultOptions = Options( | ||||
| 	WithFindTokenFn(middleware.TokenFromHeader("X-Token")), | ||||
| 	WithFindTokenFn(middleware.TokenFromQuery("token")), | ||||
| 	SetValidateTokenFn(rejectAll), | ||||
| 	SetResponseHandler(RespondWithUnauthorized), | ||||
| 	SetResponseHandler(middleware.RespondWithUnauthorized), | ||||
| ) | ||||
| 
 | ||||
| // Options turns a list of option instances into an option
 | ||||
| @ -113,11 +113,6 @@ func Middleware(opts ...Option) func(next http.Handler) http.Handler { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // RespondWithUnauthorized is a default response handler
 | ||||
| func RespondWithUnauthorized(w http.ResponseWriter, _ *http.Request, _ error) { | ||||
| 	http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) | ||||
| } | ||||
| 
 | ||||
| func rejectAll(_ string) (bool, interface{}) { | ||||
| 	return false, nil | ||||
| } | ||||
|  | ||||
| @ -1,12 +1,5 @@ | ||||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| ) | ||||
| 
 | ||||
| // ResponseHandle is a function that middleware call in case of stop chain
 | ||||
| type ResponseHandle func(w http.ResponseWriter, r *http.Request, err error) | ||||
| 
 | ||||
| // CtxKey is a key to use with context.WithValue
 | ||||
| type CtxKey struct { | ||||
| 	Pkg  string | ||||
| @ -24,7 +24,7 @@ var DefaultOptions = Options( | ||||
| 	WithFindPaginationFn(PaginationFromQuery("page", "pageSize")), | ||||
| 	SetPaginationDefaults(1, 50), | ||||
| 	SetValidatePaginationFn(allowAll), | ||||
| 	SetResponseHandler(RespondWithBadRequest), | ||||
| 	SetResponseHandler(middleware.RespondWithBadRequest), | ||||
| ) | ||||
| 
 | ||||
| // Pagination represents pagination info
 | ||||
| @ -149,11 +149,6 @@ func PaginationFromQuery(pageParam string, pageSizeParam string) func(r *http.Re | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // RespondWithBadRequest is a default response handler
 | ||||
| func RespondWithBadRequest(w http.ResponseWriter, _ *http.Request, _ error) { | ||||
| 	http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) | ||||
| } | ||||
| 
 | ||||
| func allowAll(_ *Pagination) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
							
								
								
									
										104
									
								
								query/filter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								query/filter.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,104 @@ | ||||
| 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){}, | ||||
| 		responseHandler: middleware.RespondWithBadRequest, | ||||
| 	} | ||||
| 
 | ||||
| 	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) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										98
									
								
								query/filter_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								query/filter_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | ||||
| package query | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"net/http" | ||||
| 	"net/http/httptest" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| func TestFilterFromQuery(t *testing.T) { | ||||
| 	req, err := http.NewRequest("GET", "/?filter=foo", http.NoBody) | ||||
| 	assert.NoError(t, err) | ||||
| 
 | ||||
| 	filter := FilterFromQuery("filter")(req) | ||||
| 	assert.Equal(t, "foo", filter) | ||||
| } | ||||
| 
 | ||||
| func TestFilterFromContext(t *testing.T) { | ||||
| 	ctx := context.Background() | ||||
| 	ctx = context.WithValue(ctx, FilterCtxKey, "foo") | ||||
| 
 | ||||
| 	assert.Equal(t, 0, FilterFromContext[int](ctx)) | ||||
| 	assert.Equal(t, "foo", FilterFromContext[string](ctx)) | ||||
| } | ||||
| 
 | ||||
| func TestFilterNoOptions(t *testing.T) { | ||||
| 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		filter := FilterFromContext[int](r.Context()) | ||||
| 		assert.NotNil(t, filter) | ||||
| 		assert.Equal(t, 0, filter) | ||||
| 	}) | ||||
| 
 | ||||
| 	middleware := Filter[int]()(handler) | ||||
| 
 | ||||
| 	w := httptest.NewRecorder() | ||||
| 	req := httptest.NewRequest("GET", "/", http.NoBody) | ||||
| 	middleware.ServeHTTP(w, req) | ||||
| 
 | ||||
| 	assert.Equal(t, http.StatusOK, w.Code) | ||||
| } | ||||
| 
 | ||||
| func TestFilterDefault(t *testing.T) { | ||||
| 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		filter := FilterFromContext[string](r.Context()) | ||||
| 		assert.NotNil(t, filter) | ||||
| 		assert.Equal(t, "foo", filter) | ||||
| 	}) | ||||
| 
 | ||||
| 	middleware := Filter(SetFilterDefault("foo"))(handler) | ||||
| 
 | ||||
| 	w := httptest.NewRecorder() | ||||
| 	req := httptest.NewRequest("GET", "/", http.NoBody) | ||||
| 	middleware.ServeHTTP(w, req) | ||||
| 
 | ||||
| 	assert.Equal(t, http.StatusOK, w.Code) | ||||
| } | ||||
| 
 | ||||
| func TestFilterValid(t *testing.T) { | ||||
| 	parseFilter := func(r *http.Request) (string, error) { | ||||
| 		return r.URL.Query().Get("filter"), nil | ||||
| 	} | ||||
| 
 | ||||
| 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		filter := FilterFromContext[string](r.Context()) | ||||
| 		assert.NotNil(t, filter) | ||||
| 		assert.Equal(t, "foo", filter) | ||||
| 	}) | ||||
| 
 | ||||
| 	middleware := Filter(WithParseFilterFn(parseFilter))(handler) | ||||
| 
 | ||||
| 	w := httptest.NewRecorder() | ||||
| 	req := httptest.NewRequest("GET", "/?filter=foo", http.NoBody) | ||||
| 	middleware.ServeHTTP(w, req) | ||||
| 
 | ||||
| 	assert.Equal(t, http.StatusOK, w.Code) | ||||
| } | ||||
| 
 | ||||
| func TestFilterInvalid(t *testing.T) { | ||||
| 	parseFilter := func(r *http.Request) (string, error) { | ||||
| 		return "", errors.New("invalid filter") | ||||
| 	} | ||||
| 
 | ||||
| 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		filter := FilterFromContext[string](r.Context()) | ||||
| 		assert.Nil(t, filter) | ||||
| 	}) | ||||
| 
 | ||||
| 	middleware := Filter(WithParseFilterFn(parseFilter))(handler) | ||||
| 
 | ||||
| 	w := httptest.NewRecorder() | ||||
| 	req := httptest.NewRequest("GET", "/?filter=foo", http.NoBody) | ||||
| 	middleware.ServeHTTP(w, req) | ||||
| 
 | ||||
| 	assert.Equal(t, http.StatusBadRequest, w.Code) | ||||
| } | ||||
| @ -12,7 +12,7 @@ import ( | ||||
| var DefaultOptions = Options( | ||||
| 	SetLogStackFn(defaultLogStackFn), | ||||
| 	SetLogRecoverFn(defaultLogRecoverFn), | ||||
| 	SetResponseHandler(RespondWithInternalServerError), | ||||
| 	SetResponseHandler(middleware.RespondWithInternalServerError), | ||||
| ) | ||||
| 
 | ||||
| // Options turns a list of option instances into an option
 | ||||
| @ -78,11 +78,6 @@ func Middleware(opts ...Option) func(next http.Handler) http.Handler { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // RespondWithInternalServerError is a default response handler
 | ||||
| func RespondWithInternalServerError(w http.ResponseWriter, _ *http.Request, _ error) { | ||||
| 	http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) | ||||
| } | ||||
| 
 | ||||
| func defaultLogStackFn(stack []byte) { | ||||
| 	fmt.Println(string(stack)) | ||||
| } | ||||
|  | ||||
							
								
								
									
										33
									
								
								response.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								response.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| ) | ||||
| 
 | ||||
| // ResponseHandle is a function that middleware call in case of stop chain
 | ||||
| type ResponseHandle func(w http.ResponseWriter, r *http.Request, err error) | ||||
| 
 | ||||
| // RespondWithBadRequest is a default bad request response handler
 | ||||
| func RespondWithBadRequest(w http.ResponseWriter, _ *http.Request, _ error) { | ||||
| 	http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) | ||||
| } | ||||
| 
 | ||||
| // RespondWithUnauthorized is a default unathorized response handler
 | ||||
| func RespondWithUnauthorized(w http.ResponseWriter, _ *http.Request, _ error) { | ||||
| 	http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) | ||||
| } | ||||
| 
 | ||||
| // RespondWithTooManyRequests is a default too many requests response handler
 | ||||
| func RespondWithTooManyRequests(w http.ResponseWriter, _ *http.Request, _ error) { | ||||
| 	http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests) | ||||
| } | ||||
| 
 | ||||
| // RespondWithInternalServerError is a default internal server error response handler
 | ||||
| func RespondWithInternalServerError(w http.ResponseWriter, _ *http.Request, _ error) { | ||||
| 	http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) | ||||
| } | ||||
| 
 | ||||
| // RespondWithTimeout is a default gateway timeout response handler
 | ||||
| func RespondWithGatewayTimeout(w http.ResponseWriter, _ *http.Request, _ error) { | ||||
| 	http.Error(w, http.StatusText(http.StatusGatewayTimeout), http.StatusGatewayTimeout) | ||||
| } | ||||
| @ -19,7 +19,7 @@ var ( | ||||
| // DefaultOptions represents default throttle middleware options
 | ||||
| var DefaultOptions = Options( | ||||
| 	SetLimit(100), | ||||
| 	SetResponseHandler(RespondWithTooManyRequests), | ||||
| 	SetResponseHandler(middleware.RespondWithTooManyRequests), | ||||
| ) | ||||
| 
 | ||||
| // Options turns a list of option instances into an option
 | ||||
| @ -169,8 +169,3 @@ func Middleware(opts ...Option) func(http.Handler) http.Handler { | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // RespondWithTooManyRequests is a default response handler
 | ||||
| func RespondWithTooManyRequests(w http.ResponseWriter, _ *http.Request, _ error) { | ||||
| 	http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests) | ||||
| } | ||||
|  | ||||
| @ -11,7 +11,7 @@ import ( | ||||
| // DefaultOptions represents default timeout middleware options
 | ||||
| var DefaultOptions = Options( | ||||
| 	SetTimeout(time.Second*30), | ||||
| 	SetResponseHandler(RespondWithTimeout), | ||||
| 	SetResponseHandler(middleware.RespondWithGatewayTimeout), | ||||
| ) | ||||
| 
 | ||||
| // Options turns a list of option instances into an option
 | ||||
| @ -74,8 +74,3 @@ func Middleware(opts ...Option) func(next http.Handler) http.Handler { | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // RespondWithTimeout is a default response handler
 | ||||
| func RespondWithTimeout(w http.ResponseWriter, _ *http.Request, _ error) { | ||||
| 	http.Error(w, http.StatusText(http.StatusGatewayTimeout), http.StatusGatewayTimeout) | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user