Skip to content

Commit fee9656

Browse files
authored
Allow to forward ctx in webhook handlers (#45)
1 parent 39888cb commit fee9656

File tree

3 files changed

+59
-2
lines changed

3 files changed

+59
-2
lines changed

changelog.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
### [v2.2.0]
4+
5+
* Fixed date filters in `ListArchives`.
6+
* Added `HandlerContext` and `WithActionContext` methods that pass http request's context to webhook handlers.
37

48
### [v2.1.0]
59

webhooks/handler.go

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package webhooks
22

33
import (
4+
"context"
45
"encoding/json"
56
"fmt"
67
"io/ioutil"
@@ -21,7 +22,7 @@ type Configuration struct {
2122

2223
type actionConfiguration struct {
2324
secretKey string
24-
handle Handler
25+
handle HandlerContext
2526
}
2627

2728
// The Handler type is used to define webhook processors.
@@ -30,6 +31,12 @@ type actionConfiguration struct {
3031
// pass a webhook body with a payload field decoded (ie. one of webhooks structures).
3132
type Handler func(*Webhook) error
3233

34+
// The HandlerContext type is used to define webhook processors.
35+
//
36+
// It can be used with WebhookHandler, in which case WebhookHandler will
37+
// pass a webhook body with a payload field decoded (ie. one of webhooks structures).
38+
type HandlerContext func(context.Context, *Webhook) error
39+
3340
// NewConfiguration creates basic WebhookHandler configuration that
3441
// processes no webhooks and uses http.Error to handle webhook processing
3542
// errors.
@@ -46,6 +53,19 @@ func NewConfiguration() *Configuration {
4653
// Otherwise, webhook's secret is strictly validated. In case of any mismatch between expected and actual secret key,
4754
// webhook processing is stopped and error is returned.
4855
func (cfg *Configuration) WithAction(action string, handler Handler, secretKey string) *Configuration {
56+
cfg.actions[action] = &actionConfiguration{
57+
handle: func(ctx context.Context, wh *Webhook) error { return handler(wh) },
58+
secretKey: secretKey,
59+
}
60+
return cfg
61+
}
62+
63+
// WithActionContext allows to attach custom webhook HandlerContext for given webhook action.
64+
//
65+
// If secretKey is an empty string, then no validation of webhook's secret is performed.
66+
// Otherwise, webhook's secret is strictly validated. In case of any mismatch between expected and actual secret key,
67+
// webhook processing is stopped and error is returned.
68+
func (cfg *Configuration) WithActionContext(action string, handler HandlerContext, secretKey string) *Configuration {
4969
cfg.actions[action] = &actionConfiguration{
5070
handle: handler,
5171
secretKey: secretKey,
@@ -145,7 +165,7 @@ func NewWebhookHandler(cfg *Configuration) http.HandlerFunc {
145165
}
146166
wh.Payload = payload
147167

148-
if err = acfg.handle(&wh); err != nil {
168+
if err = acfg.handle(r.Context(), &wh); err != nil {
149169
cfg.handleError(w, fmt.Sprintf("webhook handler error: %v", err), http.StatusInternalServerError)
150170
return
151171
}

webhooks/handler_test.go

+33
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package webhooks_test
22

33
import (
44
"bytes"
5+
"context"
56
"fmt"
67
"io/ioutil"
78
"net/http"
@@ -151,3 +152,35 @@ func TestPayloadParsingOK(t *testing.T) {
151152
}
152153
}
153154
}
155+
156+
func TestHandlerContextForwardsRequestContext(t *testing.T) {
157+
verifier := func(ctx context.Context, wh *webhooks.Webhook) error {
158+
rawVal := ctx.Value("dummy-key")
159+
val, ok := rawVal.(string)
160+
if !ok {
161+
t.Errorf("invalid type of 'dummy-key' in wh ctx: %T", rawVal)
162+
return nil
163+
}
164+
if val != "dummy-value" {
165+
t.Errorf("invalid value of 'dummy-key' in wh ctx: %v", val)
166+
return nil
167+
}
168+
return nil
169+
}
170+
action := "incoming_chat"
171+
cfg := webhooks.NewConfiguration().WithActionContext(action, verifier, "")
172+
h := webhooks.NewWebhookHandler(cfg)
173+
payload, err := ioutil.ReadFile("./testdata/" + action + ".json")
174+
if err != nil {
175+
t.Errorf("Missing test payload for action %v", action)
176+
return
177+
}
178+
req := httptest.NewRequest("POST", "https://example.com", bytes.NewBuffer(payload))
179+
req = req.WithContext(context.WithValue(context.Background(), "dummy-key", "dummy-value"))
180+
resp := httptest.NewRecorder()
181+
h(resp, req)
182+
if resp.Code != http.StatusOK {
183+
t.Errorf("invalid code: %v", resp.Code)
184+
return
185+
}
186+
}

0 commit comments

Comments
 (0)