Permalink
Browse files

Move to go-chi http router

  • Loading branch information...
brycekahle committed Jul 18, 2017
1 parent bcd970d commit ae72df0ddb47915625ddf58635300ef49f710e25
Showing with 980 additions and 709 deletions.
  1. +1 −1 Dockerfile
  2. +100 −67 api/api.go
  3. +1 −3 api/api_test.go
  4. +120 −51 api/auth.go
  5. +30 −0 api/context.go
  6. +6 −7 api/coupons.go
  7. +4 −9 api/coupons_test.go
  8. BIN api/debug.test
  9. +22 −29 api/download.go
  10. +1 −2 api/index.go
  11. +80 −0 api/log.go
  12. +55 −77 api/order.go
  13. +159 −106 api/order_test.go
  14. +0 −1 api/params.go
  15. +42 −83 api/payments.go
  16. +144 −65 api/payments_test.go
  17. +2 −3 api/reports.go
  18. +40 −88 api/user.go
  19. +138 −69 api/user_test.go
  20. +20 −7 api/utils_test.go
  21. +3 −4 api/vatnumbers.go
  22. +1 −1 claims/claims.go
  23. +1 −1 conf/configuration.go
  24. +0 −33 context/context.go
  25. +4 −0 coupons/coupons.go
  26. +4 −2 glide.lock
  27. +2 −0 glide.yaml
View
@@ -1,4 +1,4 @@
FROM brycekahle/go-glide:v0.12.3
FROM netlify/go-glide:v0.12.3
ADD . /go/src/github.com/netlify/gocommerce
View
@@ -4,17 +4,16 @@ import (
"context"
"net/http"
"regexp"
"time"
"github.com/pkg/errors"
"github.com/Sirupsen/logrus"
"github.com/guregu/kami"
"github.com/jinzhu/gorm"
"github.com/pborman/uuid"
"github.com/rs/cors"
"github.com/zenazn/goji/web/mutil"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/netlify/gocommerce/assetstores"
"github.com/netlify/gocommerce/conf"
gcontext "github.com/netlify/gocommerce/context"
@@ -32,7 +31,6 @@ type API struct {
db *gorm.DB
config *conf.GlobalConfiguration
httpClient *http.Client
log *logrus.Entry
version string
}
@@ -59,59 +57,104 @@ func NewSingleTenantAPIWithVersion(globalConfig *conf.GlobalConfiguration, confi
// NewAPIWithVersion instantiates a new REST API.
func NewAPIWithVersion(ctx context.Context, config *conf.GlobalConfiguration, db *gorm.DB, version string) *API {
api := &API{
log: logrus.WithField("component", "api"),
config: config,
db: db,
httpClient: &http.Client{},
version: version,
}
mux := kami.New()
mux.Context = ctx
mux.Use("/", api.populateContext)
mux.Use("/", api.withToken)
mux.LogHandler = api.logCompleted
r := chi.NewRouter()
r.Use(requestIDCtx)
r.Use(newStructuredLogger(logrus.StandardLogger()))
r.Use(middleware.Recoverer)
r.Use(api.instanceConfigCtx)
r.Use(withTokenCtx)
// endpoints
mux.Get("/", api.Index)
r.Get("/", api.Index)
r.Route("/orders", func(r chi.Router) {
r.With(authRequired).Get("/", api.OrderList)
r.Post("/", api.OrderCreate)
r.Route("/{order_id}", func(r chi.Router) {
r.Use(api.orderCtx)
// TODO should anonymous order viewing be allowed?
r.With(authRequired).Get("/", api.OrderView)
r.With(adminRequired).Put("/", api.OrderUpdate)
r.Route("/payments", func(r chi.Router) {
r.With(authRequired).Get("/", api.PaymentListForOrder)
r.Post("/", api.PaymentCreate)
})
r.Get("/downloads", api.DownloadList)
r.Post("/receipt", api.ResendOrderReceipt)
})
})
r.Route("/users", func(r chi.Router) {
r.Use(authRequired)
r.With(adminRequired).Get("/", api.UserList)
r.Route("/{user_id}", func(r chi.Router) {
r.Use(api.userCtx)
r.Use(ensureUserAccess)
r.Get("/", api.UserView)
r.With(adminRequired).Delete("/", api.UserDelete)
r.Get("/payments", api.PaymentListForUser)
r.Get("/orders", api.OrderList)
r.Route("/addresses", func(r chi.Router) {
r.Get("/", api.AddressList)
r.With(adminRequired).Post("/", api.CreateNewAddress)
r.Route("/{addr_id}", func(r chi.Router) {
r.Get("/", api.AddressView)
r.With(adminRequired).Delete("/", api.AddressDelete)
})
})
})
})
mux.Get("/orders", api.OrderList)
mux.Post("/orders", api.OrderCreate)
mux.Get("/orders/:id", api.OrderView)
mux.Put("/orders/:id", api.OrderUpdate)
mux.Get("/orders/:order_id/payments", api.PaymentListForOrder)
mux.Post("/orders/:order_id/payments", api.PaymentCreate)
mux.Post("/orders/:order_id/receipt", api.ResendOrderReceipt)
r.Route("/downloads", func(r chi.Router) {
r.With(authRequired).Get("/", api.DownloadList)
r.Get("/{download_id}", api.DownloadURL)
})
mux.Get("/users", api.UserList)
mux.Get("/users/:user_id", api.UserView)
mux.Get("/users/:user_id/payments", api.PaymentListForUser)
mux.Delete("/users/:user_id", api.UserDelete)
mux.Get("/users/:user_id/addresses", api.AddressList)
mux.Get("/users/:user_id/addresses/:addr_id", api.AddressView)
mux.Delete("/users/:user_id/addresses/:addr_id", api.AddressDelete)
mux.Get("/users/:user_id/orders", api.OrderList)
r.Route("/vatnumbers", func(r chi.Router) {
r.Get("/{vat_number}", api.VatNumberLookup)
})
mux.Get("/downloads/:id", api.DownloadURL)
mux.Get("/downloads", api.DownloadList)
mux.Get("/orders/:order_id/downloads", api.DownloadList)
r.Route("/payments", func(r chi.Router) {
r.Use(adminRequired)
mux.Get("/vatnumbers/:number", api.VatNumberLookup)
r.Get("/", api.PaymentList)
r.Route("/{payment_id}", func(r chi.Router) {
r.Get("/", api.PaymentView)
r.Post("/refund", api.PaymentRefund)
})
})
mux.Get("/payments", api.PaymentList)
mux.Get("/payments/:pay_id", api.PaymentView)
mux.Post("/payments/:pay_id/refund", api.PaymentRefund)
r.Route("/paypal", func(r chi.Router) {
r.Post("/", api.PreauthorizePayment)
// TODO is this needed? I did not see a use case in the PayPal payment flow.
// r.Get("/{payment_id}", api.PayPalGetPayment)
})
mux.Post("/paypal", api.PreauthorizePayment)
// TODO is this needed? I did not see a use case in the PayPal payment flow.
// mux.Get("/paypal/:payment_id", api.PayPalGetPayment)
r.Route("/reports", func(r chi.Router) {
r.Use(adminRequired)
mux.Get("/reports/sales", api.SalesReport)
mux.Get("/reports/products", api.ProductsReport)
r.Get("/sales", api.SalesReport)
r.Get("/products", api.ProductsReport)
})
mux.Get("/coupons/:code", api.CouponView)
r.Route("/coupons", func(r chi.Router) {
r.Get("/{coupon_code}", api.CouponView)
})
mux.Post("/claim", api.ClaimOrders)
r.With(authRequired).Post("/claim", api.ClaimOrders)
corsHandler := cors.New(cors.Options{
AllowedMethods: []string{"GET", "POST", "PATCH", "PUT", "DELETE"},
@@ -120,42 +163,32 @@ func NewAPIWithVersion(ctx context.Context, config *conf.GlobalConfiguration, db
AllowCredentials: true,
})
api.handler = corsHandler.Handler(mux)
api.handler = corsHandler.Handler(chi.ServerBaseContext(r, ctx))
return api
}
func (a *API) logCompleted(ctx context.Context, wp mutil.WriterProxy, r *http.Request) {
log := gcontext.GetLogger(ctx).WithField("status", wp.Status())
start := gcontext.GetStartTime(ctx)
if start != nil {
log = log.WithField("duration", time.Since(*start))
func requestIDCtx(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
id := uuid.NewRandom().String()
ctx := r.Context()
ctx = gcontext.WithRequestID(ctx, id)
next.ServeHTTP(w, r.WithContext(ctx))
}
log.Infof("Completed request %s. path: %s, method: %s, status: %d", gcontext.GetRequestID(ctx), r.URL.Path, r.Method, wp.Status())
return http.HandlerFunc(fn)
}
func (a *API) populateContext(ctx context.Context, w http.ResponseWriter, r *http.Request) context.Context {
id := uuid.NewRandom().String()
log := a.log.WithField("request_id", id)
log = log.WithFields(logrus.Fields{
"method": r.Method,
"path": r.URL.Path,
})
ctx = gcontext.WithRequestID(ctx, id)
ctx = gcontext.WithLogger(ctx, log)
ctx = gcontext.WithStartTime(ctx, time.Now())
func (a *API) instanceConfigCtx(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if gcontext.GetPaymentProviders(ctx) == nil {
internalServerError(w, "No payment providers configured")
return nil
}
if gcontext.GetPaymentProviders(ctx) == nil {
internalServerError(w, "No payment providers configured")
return
}
log.Info("request started")
return ctx
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func withTenantConfig(ctx context.Context, config *conf.Configuration) (context.Context, error) {
View
@@ -5,7 +5,6 @@ import (
"net/http/httptest"
"testing"
"github.com/Sirupsen/logrus"
"github.com/Sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -14,14 +13,13 @@ import (
)
func TestTraceWrapper(t *testing.T) {
l, hook := test.NewNullLogger()
hook := test.NewGlobal()
globalConfig := new(conf.GlobalConfiguration)
config := new(conf.Configuration)
config.Payment.Stripe.Enabled = true
config.Payment.Stripe.SecretKey = "secret"
api := NewAPI(globalConfig, config, nil)
api.log = logrus.NewEntry(l)
server := httptest.NewServer(api.handler)
defer server.Close()
Oops, something went wrong.

0 comments on commit ae72df0

Please sign in to comment.