Skip to content

Commit

Permalink
feat!: remove dependency on go-chi/chi
Browse files Browse the repository at this point in the history
BREAKING CHANGE

In Go 1.22, http.ServeMux was improved so that we no longer need to use
go-chi/chi, gorilla/mux, or any other thrid party HTTP router. The advantage to consumers
is that they can use any router, or none, and do not have a dependency forced on them.

Public API changes:

- `New()` has changed signature to optionally take options.

- `AttachRoutes()` has been removed. If you want to attach to an existing Chi router, you can still
do something like:
```go
b := brokerapi.New(broker, logger, brokerapi.WithBrokerCredentials(creds))

r := chi.NewRouter()
r.Handle("/*", b)
```

- The `WithRouter()` option has been removed as a Chi router can no longer be specified.

- `WithEncodedPath()` has been removed as it was deprecated and did nothing.
  • Loading branch information
blgm committed Dec 11, 2024
1 parent 8bcf18e commit 090257a
Show file tree
Hide file tree
Showing 19 changed files with 281 additions and 245 deletions.
97 changes: 80 additions & 17 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,40 +19,103 @@ import (
"log/slog"
"net/http"

"github.com/go-chi/chi/v5"
"github.com/pivotal-cf/brokerapi/v11/internal/middleware"

"github.com/pivotal-cf/brokerapi/v11/auth"
"github.com/pivotal-cf/brokerapi/v11/domain"
"github.com/pivotal-cf/brokerapi/v11/handlers"
"github.com/pivotal-cf/brokerapi/v11/middlewares"
)

type BrokerCredentials struct {
Username string
Password string
}

func New(serviceBroker ServiceBroker, logger *slog.Logger, brokerCredentials BrokerCredentials) http.Handler {
return NewWithOptions(serviceBroker, logger, WithBrokerCredentials(brokerCredentials))
type config struct {
authMiddleware []func(http.Handler) http.Handler
additionalMiddleware []func(http.Handler) http.Handler
}

func New(serviceBroker domain.ServiceBroker, logger *slog.Logger, brokerCredentials BrokerCredentials, opts ...Option) http.Handler {
return NewWithOptions(serviceBroker, logger, append([]Option{WithBrokerCredentials(brokerCredentials)}, opts...)...)
}

func NewWithCustomAuth(serviceBroker ServiceBroker, logger *slog.Logger, authMiddleware middlewareFunc) http.Handler {
func NewWithOptions(serviceBroker domain.ServiceBroker, logger *slog.Logger, opts ...Option) http.Handler {
var cfg config
WithOptions(opts...)(&cfg)

mw := append(append(cfg.authMiddleware, defaultMiddleware(logger)...), cfg.additionalMiddleware...)
r := router(serviceBroker, logger)

return middleware.Use(r, mw...)
}

func NewWithCustomAuth(serviceBroker domain.ServiceBroker, logger *slog.Logger, authMiddleware func(handler http.Handler) http.Handler) http.Handler {
return NewWithOptions(serviceBroker, logger, WithCustomAuth(authMiddleware))
}

func AttachRoutes(router chi.Router, serviceBroker ServiceBroker, logger *slog.Logger) {
attachRoutes(router, serviceBroker, logger)
type Option func(*config)

func WithBrokerCredentials(brokerCredentials BrokerCredentials) Option {
return func(c *config) {
c.authMiddleware = append(c.authMiddleware, auth.NewWrapper(brokerCredentials.Username, brokerCredentials.Password).Wrap)
}
}

// WithCustomAuth adds the specified middleware *before* any other middleware.
// Despite the name, any middleware can be added whether nor not it has anything to do with authentication.
// But `WithAdditionalMiddleware()` may be a better choice if the middleware is not related to authentication.
// Can be called multiple times.
func WithCustomAuth(authMiddleware func(handler http.Handler) http.Handler) Option {
return func(c *config) {
c.authMiddleware = append(c.authMiddleware, authMiddleware)
}
}

// WithAdditionalMiddleware adds the specified middleware *after* the default middleware.
// Can be called multiple times.
func WithAdditionalMiddleware(m func(http.Handler) http.Handler) Option {
return func(c *config) {
c.additionalMiddleware = append(c.additionalMiddleware, m)
}
}

func WithOptions(opts ...Option) Option {
return func(c *config) {
for _, o := range opts {
o(c)
}
}
}

func attachRoutes(router chi.Router, serviceBroker ServiceBroker, logger *slog.Logger) {
func router(serviceBroker ServiceBroker, logger *slog.Logger) http.Handler {
apiHandler := handlers.NewApiHandler(serviceBroker, logger)
router.Get("/v2/catalog", apiHandler.Catalog)
r := http.NewServeMux()
r.HandleFunc("GET /v2/catalog", apiHandler.Catalog)

router.Get("/v2/service_instances/{instance_id}", apiHandler.GetInstance)
router.Put("/v2/service_instances/{instance_id}", apiHandler.Provision)
router.Delete("/v2/service_instances/{instance_id}", apiHandler.Deprovision)
router.Get("/v2/service_instances/{instance_id}/last_operation", apiHandler.LastOperation)
router.Patch("/v2/service_instances/{instance_id}", apiHandler.Update)
r.HandleFunc("PUT /v2/service_instances/{instance_id}", apiHandler.Provision)
r.HandleFunc("GET /v2/service_instances/{instance_id}", apiHandler.GetInstance)
r.HandleFunc("PATCH /v2/service_instances/{instance_id}", apiHandler.Update)
r.HandleFunc("DELETE /v2/service_instances/{instance_id}", apiHandler.Deprovision)

router.Get("/v2/service_instances/{instance_id}/service_bindings/{binding_id}", apiHandler.GetBinding)
router.Put("/v2/service_instances/{instance_id}/service_bindings/{binding_id}", apiHandler.Bind)
router.Delete("/v2/service_instances/{instance_id}/service_bindings/{binding_id}", apiHandler.Unbind)
r.HandleFunc("GET /v2/service_instances/{instance_id}/last_operation", apiHandler.LastOperation)

r.HandleFunc("PUT /v2/service_instances/{instance_id}/service_bindings/{binding_id}", apiHandler.Bind)
r.HandleFunc("GET /v2/service_instances/{instance_id}/service_bindings/{binding_id}", apiHandler.GetBinding)
r.HandleFunc("DELETE /v2/service_instances/{instance_id}/service_bindings/{binding_id}", apiHandler.Unbind)

r.HandleFunc("GET /v2/service_instances/{instance_id}/service_bindings/{binding_id}/last_operation", apiHandler.LastBindingOperation)

return r
}

router.Get("/v2/service_instances/{instance_id}/service_bindings/{binding_id}/last_operation", apiHandler.LastBindingOperation)
func defaultMiddleware(logger *slog.Logger) []func(http.Handler) http.Handler {
return []func(http.Handler) http.Handler{
middlewares.APIVersionMiddleware{Logger: logger}.ValidateAPIVersionHdr,
middlewares.AddCorrelationIDToContext,
middlewares.AddOriginatingIdentityToContext,
middlewares.AddInfoLocationToContext,
middlewares.AddRequestIdentityToContext,
}
}
114 changes: 0 additions & 114 deletions api_options.go

This file was deleted.

35 changes: 8 additions & 27 deletions api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import (
"net/url"
"strings"

"github.com/go-chi/chi/v5"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gbytes"
Expand Down Expand Up @@ -109,7 +108,7 @@ var _ = Describe("Service Broker API", func() {

logBuffer = gbytes.NewBuffer()
brokerLogger = slog.New(slog.NewJSONHandler(logBuffer, nil))
brokerAPI = brokerapi.NewWithOptions(fakeServiceBroker, brokerLogger, brokerapi.WithBrokerCredentials(credentials))
brokerAPI = brokerapi.New(fakeServiceBroker, brokerLogger, credentials)
})

Describe("response headers", func() {
Expand Down Expand Up @@ -325,7 +324,7 @@ var _ = Describe("Service Broker API", func() {
}

BeforeEach(func() {
brokerAPI = brokerapi.NewWithOptions(fakeServiceBroker, brokerLogger, brokerapi.WithCustomAuth(authMiddleware))
brokerAPI = brokerapi.NewWithCustomAuth(fakeServiceBroker, brokerLogger, authMiddleware)
})

It("returns 401 when the authorization header has an incorrect bearer token", func() {
Expand Down Expand Up @@ -359,7 +358,7 @@ var _ = Describe("Service Broker API", func() {

BeforeEach(func() {
fakeServiceBroker = new(fakes.AutoFakeServiceBroker)
brokerAPI = brokerapi.NewWithOptions(fakeServiceBroker, brokerLogger, brokerapi.WithBrokerCredentials(credentials))
brokerAPI = brokerapi.New(fakeServiceBroker, brokerLogger, credentials)

testServer = httptest.NewServer(brokerAPI)
var err error
Expand Down Expand Up @@ -408,7 +407,7 @@ var _ = Describe("Service Broker API", func() {

BeforeEach(func() {
fakeServiceBroker = new(fakes.AutoFakeServiceBroker)
brokerAPI = brokerapi.NewWithOptions(fakeServiceBroker, brokerLogger, brokerapi.WithBrokerCredentials(credentials))
brokerAPI = brokerapi.New(fakeServiceBroker, brokerLogger, credentials)

testServer = httptest.NewServer(brokerAPI)
var err error
Expand Down Expand Up @@ -464,7 +463,7 @@ var _ = Describe("Service Broker API", func() {

BeforeEach(func() {
fakeServiceBroker = new(fakes.AutoFakeServiceBroker)
brokerAPI = brokerapi.NewWithOptions(fakeServiceBroker, brokerLogger, brokerapi.WithBrokerCredentials(credentials))
brokerAPI = brokerapi.New(fakeServiceBroker, brokerLogger, credentials)

testServer = httptest.NewServer(brokerAPI)
var err error
Expand Down Expand Up @@ -520,7 +519,7 @@ var _ = Describe("Service Broker API", func() {

BeforeEach(func() {
fakeServiceBroker = new(fakes.AutoFakeServiceBroker)
brokerAPI = brokerapi.NewWithOptions(fakeServiceBroker, brokerLogger, brokerapi.WithBrokerCredentials(credentials))
brokerAPI = brokerapi.New(fakeServiceBroker, brokerLogger, credentials)

testServer = httptest.NewServer(brokerAPI)
var err error
Expand Down Expand Up @@ -2726,24 +2725,6 @@ var _ = Describe("Service Broker API", func() {
}
})

Describe("WithRouter()", func() {
It("can take a supplied router", func() {
router := chi.NewRouter()
brokerAPI = brokerapi.NewWithOptions(fakeServiceBroker, brokerLogger, brokerapi.WithRouter(router))
Expect(router).To(Equal(brokerAPI))
})

It("does not attach middleware to the router", func() {
brokerAPI = brokerapi.NewWithOptions(fakeServiceBroker, brokerLogger, brokerapi.WithRouter(chi.NewRouter()))
apiVersion = "1.14" // Wrong version

instanceID := uniqueInstanceID()
response := makeInstanceProvisioningRequest(instanceID, provisionDetails, "")
Expect(response).To(HaveHTTPStatus(http.StatusCreated))
Expect(fakeServiceBroker.ProvisionedInstances).To(HaveKey(instanceID))
})
})

Describe("WithAdditionalMiddleware()", func() {
It("adds additional middleware", func() {
const (
Expand All @@ -2767,10 +2748,10 @@ var _ = Describe("Service Broker API", func() {

It("will accept URL-encoded paths", func() {
const encodedInstanceID = "foo%2Fbar"
brokerAPI = brokerapi.NewWithOptions(fakeServiceBroker, brokerLogger, brokerapi.WithBrokerCredentials(credentials))
brokerAPI = brokerapi.New(fakeServiceBroker, brokerLogger, credentials)
response := makeInstanceProvisioningRequest(encodedInstanceID, provisionDetails, "")
Expect(response).To(HaveHTTPStatus(http.StatusCreated))
Expect(fakeServiceBroker.ProvisionedInstances).To(HaveKey(encodedInstanceID))
Expect(fakeServiceBroker.ProvisionedInstances).To(HaveKey("foo/bar"))
})
})
})
Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module github.com/pivotal-cf/brokerapi/v11
go 1.22.1

require (
github.com/go-chi/chi/v5 v5.1.0
github.com/google/uuid v1.6.0
github.com/maxbrunsfeld/counterfeiter/v6 v6.9.0
github.com/onsi/ginkgo/v2 v2.22.0
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
Expand Down
5 changes: 2 additions & 3 deletions handlers/bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"log/slog"
"net/http"

"github.com/go-chi/chi/v5"
"github.com/pivotal-cf/brokerapi/v11/domain"
"github.com/pivotal-cf/brokerapi/v11/domain/apiresponses"
"github.com/pivotal-cf/brokerapi/v11/internal/blog"
Expand All @@ -19,8 +18,8 @@ const (
)

func (h APIHandler) Bind(w http.ResponseWriter, req *http.Request) {
instanceID := chi.URLParam(req, "instance_id")
bindingID := chi.URLParam(req, "binding_id")
instanceID := req.PathValue("instance_id")
bindingID := req.PathValue("binding_id")

logger := h.logger.Session(req.Context(), bindLogKey, blog.InstanceID(instanceID), blog.BindingID(bindingID))

Expand Down
2 changes: 1 addition & 1 deletion handlers/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

const getCatalogLogKey = "getCatalog"

func (h *APIHandler) Catalog(w http.ResponseWriter, req *http.Request) {
func (h APIHandler) Catalog(w http.ResponseWriter, req *http.Request) {
logger := h.logger.Session(req.Context(), getCatalogLogKey)
requestId := fmt.Sprintf("%v", req.Context().Value(middlewares.RequestIdentityKey))

Expand Down
3 changes: 1 addition & 2 deletions handlers/deprovision.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"log/slog"
"net/http"

"github.com/go-chi/chi/v5"
"github.com/pivotal-cf/brokerapi/v11/domain"
"github.com/pivotal-cf/brokerapi/v11/domain/apiresponses"
"github.com/pivotal-cf/brokerapi/v11/internal/blog"
Expand All @@ -15,7 +14,7 @@ import (
const deprovisionLogKey = "deprovision"

func (h APIHandler) Deprovision(w http.ResponseWriter, req *http.Request) {
instanceID := chi.URLParam(req, "instance_id")
instanceID := req.PathValue("instance_id")

logger := h.logger.Session(req.Context(), deprovisionLogKey, blog.InstanceID(instanceID))

Expand Down
Loading

0 comments on commit 090257a

Please sign in to comment.