Skip to content

Commit

Permalink
Implement HTTP middlewares for EventsAPIServer
Browse files Browse the repository at this point in the history
  • Loading branch information
fgrosse committed Jan 30, 2022
1 parent daf92d1 commit 2b20644
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 6 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
Nothing so far
- Add new `Config.EventsAPIConfig.Middlewar` configuration and corresponding `WithMiddleware(…)` option.
This fixes #19 by allowing the user to inject a custom HTTP middleware for the
`EventsAPIServer`, e.g. in order to enable custom (health) endpoints.

## [v2.1.0] - 2020-07-25
- Add new `EventsAPIAdapter` function to support integrating with Slack via the
Expand Down
7 changes: 6 additions & 1 deletion events_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,14 @@ func NewEventsAPIServer(ctx context.Context, listenAddr string, conf Config) (*E
},
))

var handler http.Handler = http.HandlerFunc(a.httpHandler)
if conf.EventsAPI.Middleware != nil {
handler = conf.EventsAPI.Middleware(handler)
}

a.http = &http.Server{
Addr: listenAddr,
Handler: http.HandlerFunc(a.httpHandler),
Handler: handler,
ErrorLog: zap.NewStdLog(conf.Logger),
TLSConfig: conf.EventsAPI.TLSConf,
ReadTimeout: conf.EventsAPI.ReadTimeout,
Expand Down
46 changes: 42 additions & 4 deletions events_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"

"github.com/go-joe/joe"
Expand All @@ -16,14 +18,21 @@ import (
"github.com/slack-go/slack/slackevents"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
"go.uber.org/zap/zaptest/observer"
)

func newTestEventsAPIServer(t *testing.T) (_ *EventsAPIServer, finish func() (events []interface{})) {
func newTestEventsAPIServer(t *testing.T, optionalConf ...Config) (_ *EventsAPIServer, finish func() (events []interface{})) {
var conf Config
if len(optionalConf) > 0 {
conf = optionalConf[0]
}

ctx := context.Background()
conf := Config{
Logger: zaptest.NewLogger(t),
Debug: true,
conf.Debug = true
if conf.Logger == nil {
conf.Logger = zaptest.NewLogger(t)
}

slackAPI := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -153,6 +162,35 @@ func TestEventsAPIServer_HandleReactionAddedEvent(t *testing.T) {
assert.Equal(t, "+1", actual.Reaction.Shortcode)
}

func TestEventsAPIServer_HTTPHandlerMiddleware(t *testing.T) {
middleware := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if req.URL.Path == "/health" {
_, _ = fmt.Fprint(w, "Everything is fine!")
return
}

next.ServeHTTP(w, req)
})
}

obs, errorLogs := observer.New(zap.ErrorLevel)
s, _ := newTestEventsAPIServer(t, Config{
Logger: zap.New(obs),
EventsAPI: EventsAPIConfig{
Middleware: middleware,
},
})

req := httptest.NewRequest("GET", "/health", strings.NewReader("Are you okay?"))
resp := httptest.NewRecorder()

s.http.Handler.ServeHTTP(resp, req)

assert.Equal(t, "Everything is fine!", resp.Body.String())
assert.Empty(t, errorLogs.All())
}

func toJSON(req interface{}) io.Reader {
b := new(bytes.Buffer)
err := json.NewEncoder(b).Encode(req)
Expand Down
11 changes: 11 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package slack
import (
"crypto/tls"
"errors"
"net/http"
"time"

"github.com/slack-go/slack"
Expand Down Expand Up @@ -38,6 +39,7 @@ type Config struct {

// EventsAPIConfig contains the configuration of an EventsAPIServer.
type EventsAPIConfig struct {
Middleware func(next http.Handler) http.Handler
ShutdownTimeout time.Duration
ReadTimeout time.Duration
WriteTimeout time.Duration
Expand Down Expand Up @@ -167,3 +169,12 @@ func WithWriteTimeout(d time.Duration) Option {
return nil
}
}

// WithMiddleware is an option for the EventsAPIServer that allows the user to
// inject an HTTP middleware to the HTTP server.
func WithMiddleware(mw func(next http.Handler) http.Handler) Option {
return func(conf *Config) error {
conf.EventsAPI.Middleware = mw
return nil
}
}
19 changes: 19 additions & 0 deletions options_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package slack

import (
"net/http"
"testing"

"github.com/go-joe/joe"
Expand Down Expand Up @@ -85,3 +86,21 @@ func TestWithListenPassive(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, true, conf.ListenPassive)
}

func TestWithMiddleware(t *testing.T) {
var ok bool
middleware := func(next http.Handler) http.Handler {
ok = true // proof this function executed
return next
}

conf, err := newConf("my-secret-token", joeConf(t), []Option{
WithMiddleware(middleware),
})

require.NoError(t, err)
require.NotNil(t, conf.EventsAPI.Middleware)

conf.EventsAPI.Middleware(nil)
assert.True(t, ok)
}

0 comments on commit 2b20644

Please sign in to comment.