Skip to content

Commit

Permalink
Allow support of multiple error handlers using # as separator for han…
Browse files Browse the repository at this point in the history
…dler type name and instance
  • Loading branch information
diorcety authored and Yann Diorcet committed Dec 20, 2023
1 parent 75eb682 commit 50643ff
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .schema/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1480,7 +1480,7 @@
"examples": [["redirect"]]
},
"handlers": {
"additionalProperties": false,
"additionalProperties": true,
"title": "Individual Error Handler Configuration",
"type": "object",
"properties": {
Expand Down
2 changes: 1 addition & 1 deletion .schemas/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1158,7 +1158,7 @@
"examples": [["redirect"]]
},
"handlers": {
"additionalProperties": false,
"additionalProperties": true,
"title": "Individual Error Handler Configuration",
"type": "object",
"properties": {
Expand Down
1 change: 1 addition & 0 deletions driver/configuration/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ type Provider interface {
}

type ProviderErrorHandlers interface {
ErrorHandlers() []string
ErrorHandlerConfig(id string, override json.RawMessage, dest interface{}) error
ErrorHandlerIsEnabled(id string) bool
ErrorHandlerFallbackSpecificity() []string
Expand Down
7 changes: 6 additions & 1 deletion driver/configuration/provider_koanf.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,9 @@ func (v *KoanfProvider) PipelineConfig(prefix, id string, override json.RawMessa
}

func (v *KoanfProvider) validatePipelineConfig(prefix, id string, marshalled []byte) error {
id_parts := strings.SplitN(id, "#", 2)
rawComponentSchema, err := schema.FS.ReadFile(fmt.Sprintf(
"pipeline/%s.%s.schema.json", strings.Split(prefix, ".")[0], id))
"pipeline/%s.%s.schema.json", strings.Split(prefix, ".")[0], id_parts[0]))
if err != nil {
return errors.WithStack(err)
}
Expand Down Expand Up @@ -368,6 +369,10 @@ func (v *KoanfProvider) validatePipelineConfig(prefix, id string, marshalled []b
return nil
}

func (v *KoanfProvider) ErrorHandlers() []string {
return v.source.MapKeys(ErrorsHandlers)
}

func (v *KoanfProvider) ErrorHandlerConfig(id string, override json.RawMessage, dest interface{}) error {
return v.PipelineConfig(ErrorsHandlers, id, override, dest)
}
Expand Down
19 changes: 15 additions & 4 deletions driver/registry_memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package driver
import (
"context"
"sync"
"strings"

"go.opentelemetry.io/otel/trace"

Expand Down Expand Up @@ -250,10 +251,20 @@ func (r *RegistryMemory) prepareErrors() {
defer r.Unlock()

if r.errors == nil {
interim := []pe.Handler{
pe.NewErrorJSON(r.c, r),
pe.NewErrorRedirect(r.c, r),
pe.NewErrorWWWAuthenticate(r.c, r),
var interim []pe.Handler
interim = []pe.Handler{
pe.NewErrorJSON("json", r.c, r),
pe.NewErrorRedirect("redirect", r.c, r),
pe.NewErrorWWWAuthenticate("www_authenticate", r.c, r),
}
for _, key := range r.c.ErrorHandlers() {
if strings.HasPrefix(key, "json#") {
interim = append(interim, pe.NewErrorJSON(key, r.c, r))
} else if strings.HasPrefix(key, "redirect#") {
interim = append(interim, pe.NewErrorRedirect(key, r.c, r))
} else if strings.HasPrefix(key, "www_authenticate#") {
interim = append(interim, pe.NewErrorWWWAuthenticate(key, r.c, r))
}
}

r.errors = map[string]pe.Handler{}
Expand Down
6 changes: 4 additions & 2 deletions pipeline/errors/error_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type (
Verbose bool `json:"verbose"`
}
ErrorJSON struct {
n string
c configuration.Provider
d errorJSONDependencies
}
Expand All @@ -31,10 +32,11 @@ type (
)

func NewErrorJSON(
n string,
c configuration.Provider,
d errorJSONDependencies,
) *ErrorJSON {
return &ErrorJSON{c: c, d: d}
return &ErrorJSON{n:n, c: c, d: d}
}

func (a *ErrorJSON) Handle(w http.ResponseWriter, r *http.Request, config json.RawMessage, _ pipeline.Rule, handleError error) error {
Expand Down Expand Up @@ -89,5 +91,5 @@ func (a *ErrorJSON) Config(config json.RawMessage) (*ErrorJSONConfig, error) {
}

func (a *ErrorJSON) GetID() string {
return "json"
return a.n
}
6 changes: 4 additions & 2 deletions pipeline/errors/error_redirect.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type (
ReturnToQueryParam string `json:"return_to_query_param"`
}
ErrorRedirect struct {
n string
c configuration.Provider
d ErrorRedirectDependencies
}
Expand All @@ -37,10 +38,11 @@ type (
)

func NewErrorRedirect(
n string,
c configuration.Provider,
d ErrorRedirectDependencies,
) *ErrorRedirect {
return &ErrorRedirect{c: c, d: d}
return &ErrorRedirect{n: n, c: c, d: d}
}

func (a *ErrorRedirect) Handle(w http.ResponseWriter, r *http.Request, config json.RawMessage, _ pipeline.Rule, _ error) error {
Expand Down Expand Up @@ -79,7 +81,7 @@ func (a *ErrorRedirect) Config(config json.RawMessage) (*ErrorRedirectConfig, er
}

func (a *ErrorRedirect) GetID() string {
return "redirect"
return a.n
}

func (a *ErrorRedirect) RedirectURL(uri *url.URL, c *ErrorRedirectConfig) string {
Expand Down
46 changes: 46 additions & 0 deletions pipeline/errors/error_redirect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,49 @@ func TestErrorReturnToRedirectURLHeaderUsage(t *testing.T) {
})
}
}

func TestErrorRedirectInstance(t *testing.T) {
conf := internal.NewConfigurationWithDefaults()
conf.SetForTest(t, "errors.handlers.redirect#forbidden.enabled", true)
reg := internal.NewRegistry(conf)

a, err := reg.PipelineErrorHandler("redirect#forbidden")
require.NoError(t, err)

t.Run("method=handle", func(t *testing.T) {
for k, tc := range []struct {
d string
header http.Header
config string
expectError error
givenError error
assert func(t *testing.T, recorder *httptest.ResponseRecorder)
}{
{
d: "should redirect with 302 - absolute (HTTP)",
givenError: &herodot.ErrNotFound,
config: `{"to":"http://test/test"}`,
assert: func(t *testing.T, rw *httptest.ResponseRecorder) {
assert.Equal(t, 302, rw.Code)
assert.Equal(t, "http://test/test", rw.Header().Get("Location"))
},
},
} {
t.Run(fmt.Sprintf("case=%d/description=%s", k, tc.d), func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/test", nil)
err := a.Handle(w, r, json.RawMessage(tc.config), nil, tc.givenError)

if tc.expectError != nil {
require.EqualError(t, err, tc.expectError.Error(), "%+v", err)
return
}

require.NoError(t, err)
if tc.assert != nil {
tc.assert(t, w)
}
})
}
})
}
6 changes: 4 additions & 2 deletions pipeline/errors/error_www_authenticate.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type (
Realm string `json:"realm"`
}
ErrorWWWAuthenticate struct {
n string
c configuration.Provider
d ErrorWWWAuthenticateDependencies
}
Expand All @@ -29,10 +30,11 @@ type (
)

func NewErrorWWWAuthenticate(
n string,
c configuration.Provider,
d ErrorWWWAuthenticateDependencies,
) *ErrorWWWAuthenticate {
return &ErrorWWWAuthenticate{c: c, d: d}
return &ErrorWWWAuthenticate{n:n, c: c, d: d}
}

func (a *ErrorWWWAuthenticate) Handle(w http.ResponseWriter, r *http.Request, config json.RawMessage, _ pipeline.Rule, _ error) error {
Expand Down Expand Up @@ -68,5 +70,5 @@ func (a *ErrorWWWAuthenticate) Config(config json.RawMessage) (*ErrorWWWAuthenti
}

func (a *ErrorWWWAuthenticate) GetID() string {
return "www_authenticate"
return a.n
}
2 changes: 1 addition & 1 deletion spec/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1486,7 +1486,7 @@
"examples": [["redirect"]]
},
"handlers": {
"additionalProperties": false,
"additionalProperties": true,
"title": "Individual Error Handler Configuration",
"type": "object",
"properties": {
Expand Down

0 comments on commit 50643ff

Please sign in to comment.