Skip to content

Commit

Permalink
refactor: move functions to legacy handler
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelbrm committed Feb 1, 2024
1 parent 332b891 commit 53b83d6
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 91 deletions.
2 changes: 1 addition & 1 deletion services/grant/cmd/grant.go
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ func setupRouter(ctx context.Context, logger *zerolog.Logger) (context.Context,

r.Mount("/v1/credentials", skus.CredentialRouter(skusService, authMwr))
r.Mount("/v2/credentials", skus.CredentialV2Router(skusService, authMwr))
r.Mount("/v1/orders", skus.Router(skusService, authMwr, middleware.InstrumentHandler, corsOpts))
r.Mount("/v1/orders", skus.Router(logger, skusService, authMwr, middleware.InstrumentHandler, corsOpts))

subr := chi.NewRouter()
orderh := handler.NewOrder(skusService)
Expand Down
178 changes: 92 additions & 86 deletions services/skus/controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/go-chi/chi"
"github.com/go-chi/cors"
"github.com/go-playground/validator/v10"
"github.com/rs/zerolog"
uuid "github.com/satori/go.uuid"
"github.com/stripe/stripe-go/v72"
"github.com/stripe/stripe-go/v72/webhook"
Expand All @@ -30,6 +31,7 @@ import (
"github.com/brave-intl/bat-go/libs/middleware"
"github.com/brave-intl/bat-go/libs/requestutils"
"github.com/brave-intl/bat-go/libs/responses"

"github.com/brave-intl/bat-go/services/skus/handler"
"github.com/brave-intl/bat-go/services/skus/model"
)
Expand All @@ -41,6 +43,7 @@ const (
type middlewareFn func(next http.Handler) http.Handler

func Router(
parentLg *zerolog.Logger,
svc *Service,
authMwr middlewareFn,
metricsMwr middleware.InstrumentHandlerDef,
Expand Down Expand Up @@ -101,10 +104,12 @@ func Router(

// Receipt validation.
{
valid := validator.New()
r.Method(http.MethodPost, "/{orderID}/submit-receipt", metricsMwr("SubmitReceipt", corsMwrPost(submitReceiptH(svc, valid))))
r.Method(http.MethodPost, "/receipt", metricsMwr("createOrderFromReceipt", corsMwrPost(createOrderFromReceiptH(svc, valid))))
r.Method(http.MethodPatch, "/{orderID}/receipt", metricsMwr("checkOrderReceipt", authMwr(checkOrderReceiptH(svc, valid))))
lg := parentLg.With().Str("module", "skus").Logger()
h := newLegacyOrderHandler(svc, &lg, validator.New())

r.Method(http.MethodPost, "/{orderID}/submit-receipt", metricsMwr("SubmitReceipt", corsMwrPost(handlers.AppHandler(h.submitReceipt))))
r.Method(http.MethodPost, "/receipt", metricsMwr("createOrderFromReceipt", corsMwrPost(handlers.AppHandler(h.createOrderFromReceipt))))
r.Method(http.MethodPatch, "/{orderID}/receipt", metricsMwr("checkOrderReceipt", authMwr(handlers.AppHandler(h.checkOrderReceipt))))
}

r.Route("/{orderID}/credentials", func(cr chi.Router) {
Expand Down Expand Up @@ -1400,97 +1405,103 @@ func HandleStripeWebhook(service *Service) handlers.AppHandler {
}
}

// submitReceiptH handles receipt submission requests.
func submitReceiptH(svc *Service, valid *validator.Validate) handlers.AppHandler {
return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError {
ctx := r.Context()

l := logging.Logger(ctx, "skus").With().Str("func", "SubmitReceipt").Logger()
type legacyOrderHandler struct {
svc *Service
lg *zerolog.Logger
valid *validator.Validate
}

orderID, err := uuid.FromString(chi.URLParamFromCtx(ctx, "orderID"))
if err != nil {
l.Warn().Err(err).Msg("failed to decode orderID")
func newLegacyOrderHandler(svc *Service, lg *zerolog.Logger, valid *validator.Validate) *legacyOrderHandler {
result := &legacyOrderHandler{
svc: svc,
lg: lg,
valid: valid,
}

// Preserve the legacy error in case anything depends on it.
return handlers.ValidationError("request", map[string]interface{}{"orderID": inputs.ErrIDDecodeNotUUID})
}
return result
}

payload, err := requestutils.Read(ctx, r.Body)
if err != nil {
l.Warn().Err(err).Msg("failed to read body")
// submitReceipt handles receipt submission requests.
func (h *legacyOrderHandler) submitReceipt(w http.ResponseWriter, r *http.Request) *handlers.AppError {
ctx := r.Context()

return handlers.ValidationError("request", map[string]interface{}{"request-body": err.Error()})
}
lg := h.lg.With().Str("func", "SubmitReceipt").Logger()

// TODO(clD11): remove when no longer needed.
payloadS := string(payload)
l.Info().Interface("payload_byte", payload).Str("payload_str", payloadS).Msg("payload")
orderID, err := uuid.FromString(chi.URLParamFromCtx(ctx, "orderID"))
if err != nil {
lg.Warn().Err(err).Msg("failed to decode orderID")

req, err := parseSubmitReceiptRequest(payload)
if err != nil {
l.Warn().Err(err).Msg("failed to deserialize request")
// Preserve the legacy error in case anything depends on it.
return handlers.ValidationError("request", map[string]interface{}{"orderID": inputs.ErrIDDecodeNotUUID})
}

return handlers.ValidationError("request", map[string]interface{}{"request-body": err.Error()})
}
payload, err := requestutils.Read(ctx, r.Body)
if err != nil {
lg.Warn().Err(err).Msg("failed to read body")

if err := valid.StructCtx(ctx, &req); err != nil {
verrs, ok := collectValidationErrors(err)
if !ok {
return handlers.ValidationError("request", map[string]interface{}{"request-body": err.Error()})
}
return handlers.ValidationError("request", map[string]interface{}{"request-body": err.Error()})
}

return handlers.ValidationError("request", verrs)
}
// TODO(clD11): remove when no longer needed.
payloadS := string(payload)
lg.Info().Interface("payload_byte", payload).Str("payload_str", payloadS).Msg("payload")

// TODO(clD11): remove when no longer needed.
l.Info().Interface("req_decoded", req).Msg("req decoded")
req, err := parseSubmitReceiptRequest(payload)
if err != nil {
lg.Warn().Err(err).Msg("failed to deserialize request")

extID, err := svc.validateReceipt(ctx, req)
if err != nil {
l.Warn().Err(err).Msg("failed to validate receipt with vendor")
return handlers.ValidationError("request", map[string]interface{}{"request-body": err.Error()})
}

return handleReceiptErr(err)
if err := h.valid.StructCtx(ctx, &req); err != nil {
verrs, ok := collectValidationErrors(err)
if !ok {
return handlers.ValidationError("request", map[string]interface{}{"request-body": err.Error()})
}

{
exists, err := svc.ExternalIDExists(ctx, extID)
if err != nil {
l.Warn().Err(err).Msg("failed to lookup external id")
return handlers.ValidationError("request", verrs)
}

return handlers.WrapError(err, "failed to lookup external id", http.StatusInternalServerError)
}
// TODO(clD11): remove when no longer needed.
lg.Info().Interface("req_decoded", req).Msg("req decoded")

if exists {
return handlers.WrapError(err, "receipt has already been submitted", http.StatusBadRequest)
}
}
extID, err := h.svc.validateReceipt(ctx, req)
if err != nil {
lg.Warn().Err(err).Msg("failed to validate receipt with vendor")

return handleReceiptErr(err)
}

mdata := newMobileOrderMdata(req, extID)
{
exists, err := h.svc.ExternalIDExists(ctx, extID)
if err != nil {
lg.Warn().Err(err).Msg("failed to lookup external id")

if err := svc.UpdateOrderStatusPaidWithMetadata(ctx, &orderID, mdata); err != nil {
l.Warn().Err(err).Msg("failed to update order with vendor metadata")
return handlers.WrapError(err, "failed to store status of order", http.StatusInternalServerError)
return handlers.WrapError(err, "failed to lookup external id", http.StatusInternalServerError)
}

result := struct {
ExternalID string `json:"externalId"`
Vendor string `json:"vendor"`
}{ExternalID: extID, Vendor: req.Type.String()}
if exists {
return handlers.WrapError(err, "receipt has already been submitted", http.StatusBadRequest)
}
}

return handlers.RenderContent(ctx, result, w, http.StatusOK)
})
}
mdata := newMobileOrderMdata(req, extID)

func createOrderFromReceiptH(svc *Service, valid *validator.Validate) handlers.AppHandler {
return func(w http.ResponseWriter, r *http.Request) *handlers.AppError {
return createOrderFromReceipth(w, r, svc, valid)
if err := h.svc.UpdateOrderStatusPaidWithMetadata(ctx, &orderID, mdata); err != nil {
lg.Warn().Err(err).Msg("failed to update order with vendor metadata")
return handlers.WrapError(err, "failed to store status of order", http.StatusInternalServerError)
}
}

func createOrderFromReceipth(w http.ResponseWriter, r *http.Request, svc *Service, valid *validator.Validate) *handlers.AppError {
ctx := r.Context()
result := struct {
ExternalID string `json:"externalId"`
Vendor string `json:"vendor"`
}{ExternalID: extID, Vendor: req.Type.String()}

return handlers.RenderContent(ctx, result, w, http.StatusOK)
}

lg := logging.Logger(ctx, "skus").With().Str("func", "createOrderFromReceipt").Logger()
func (h *legacyOrderHandler) createOrderFromReceipt(w http.ResponseWriter, r *http.Request) *handlers.AppError {
lg := h.lg.With().Str("func", "createOrderFromReceipt").Logger()

raw, err := io.ReadAll(io.LimitReader(r.Body, reqBodyLimit10MB))
if err != nil {
Expand All @@ -1506,7 +1517,9 @@ func createOrderFromReceipth(w http.ResponseWriter, r *http.Request, svc *Servic
return handlers.ValidationError("request", map[string]interface{}{"request-body": err.Error()})
}

if err := valid.StructCtx(ctx, &req); err != nil {
ctx := r.Context()

if err := h.valid.StructCtx(ctx, &req); err != nil {
verrs, ok := collectValidationErrors(err)
if !ok {
return handlers.ValidationError("request", map[string]interface{}{"request-body": err.Error()})
Expand All @@ -1515,15 +1528,15 @@ func createOrderFromReceipth(w http.ResponseWriter, r *http.Request, svc *Servic
return handlers.ValidationError("request", verrs)
}

extID, err := svc.validateReceipt(ctx, req)
extID, err := h.svc.validateReceipt(ctx, req)
if err != nil {
lg.Warn().Err(err).Msg("failed to validate receipt with vendor")

return handleReceiptErr(err)
}

{
ord, err := svc.orderRepo.GetByExternalID(ctx, svc.Datastore.RawDB(), extID)
ord, err := h.svc.orderRepo.GetByExternalID(ctx, h.svc.Datastore.RawDB(), extID)
if err != nil && !errors.Is(err, model.ErrOrderNotFound) {
lg.Warn().Err(err).Msg("failed to lookup external id")

Expand All @@ -1537,7 +1550,7 @@ func createOrderFromReceipth(w http.ResponseWriter, r *http.Request, svc *Servic
}
}

ord, err := svc.createOrderWithReceipt(ctx, req, extID)
ord, err := h.svc.createOrderWithReceipt(ctx, req, extID)
if err != nil {
lg.Warn().Err(err).Msg("failed to create order")

Expand All @@ -1547,19 +1560,12 @@ func createOrderFromReceipth(w http.ResponseWriter, r *http.Request, svc *Servic
result := model.CreateOrderWithReceiptResponse{ID: ord.ID.String()}

return handlers.RenderContent(ctx, result, w, http.StatusCreated)

}

func checkOrderReceiptH(svc *Service, valid *validator.Validate) handlers.AppHandler {
return func(w http.ResponseWriter, r *http.Request) *handlers.AppError {
return checkOrderReceipth(w, r, svc, valid)
}
}

func checkOrderReceipth(w http.ResponseWriter, r *http.Request, svc *Service, valid *validator.Validate) *handlers.AppError {
func (h *legacyOrderHandler) checkOrderReceipt(w http.ResponseWriter, r *http.Request) *handlers.AppError {
ctx := r.Context()

lg := logging.Logger(ctx, "skus").With().Str("func", "checkOrderReceipt").Logger()
lg := h.lg.With().Str("func", "checkOrderReceipt").Logger()

orderID, err := uuid.FromString(chi.URLParamFromCtx(ctx, "orderID"))
if err != nil {
Expand All @@ -1582,7 +1588,7 @@ func checkOrderReceipth(w http.ResponseWriter, r *http.Request, svc *Service, va
return handlers.ValidationError("request", map[string]interface{}{"request-body": err.Error()})
}

if err := valid.StructCtx(ctx, &req); err != nil {
if err := h.valid.StructCtx(ctx, &req); err != nil {
verrs, ok := collectValidationErrors(err)
if !ok {
return handlers.ValidationError("request", map[string]interface{}{"request-body": err.Error()})
Expand All @@ -1591,14 +1597,14 @@ func checkOrderReceipth(w http.ResponseWriter, r *http.Request, svc *Service, va
return handlers.ValidationError("request", verrs)
}

extID, err := svc.validateReceipt(ctx, req)
extID, err := h.svc.validateReceipt(ctx, req)
if err != nil {
lg.Warn().Err(err).Msg("failed to validate receipt with vendor")

return handleReceiptErr(err)
}

if err := svc.checkOrderReceipt(ctx, orderID, extID); err != nil {
if err := h.svc.checkOrderReceipt(ctx, orderID, extID); err != nil {
lg.Warn().Err(err).Msg("failed to check order receipt")

switch {
Expand Down
13 changes: 9 additions & 4 deletions services/skus/controllers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/go-chi/cors"
"github.com/golang/mock/gomock"
"github.com/linkedin/goavro"
"github.com/rs/zerolog"
uuid "github.com/satori/go.uuid"
"github.com/shopspring/decimal"
"github.com/stretchr/testify/suite"
Expand Down Expand Up @@ -1022,7 +1023,8 @@ func (suite *ControllersTestSuite) TestE2EAnonymousCard() {
return h
}

router := Router(suite.service, authMwr, instrumentHandler, newCORSOptsEnv())
lg := zerolog.Nop()
router := Router(&lg, suite.service, authMwr, instrumentHandler, newCORSOptsEnv())

router.Mount("/vote", VoteRouter(suite.service, instrumentHandler))
server := &http.Server{Addr: ":8080", Handler: router}
Expand Down Expand Up @@ -1540,7 +1542,8 @@ func (suite *ControllersTestSuite) TestE2E_CreateOrderCreds_StoreSignedOrderCred
return h
}

router := Router(skuService, authMwr, instrumentHandler, newCORSOptsEnv())
lg := zerolog.Nop()
router := Router(&lg, skuService, authMwr, instrumentHandler, newCORSOptsEnv())

server := &http.Server{Addr: ":8080", Handler: router}
server.Handler.ServeHTTP(rw, r)
Expand Down Expand Up @@ -1686,7 +1689,8 @@ func (suite *ControllersTestSuite) TestE2E_CreateOrderCreds_StoreSignedOrderCred
return h
}

router := Router(skuService, authMwr, instrumentHandler, newCORSOptsEnv())
lg := zerolog.Nop()
router := Router(&lg, skuService, authMwr, instrumentHandler, newCORSOptsEnv())

server := &http.Server{Addr: ":8080", Handler: router}
server.Handler.ServeHTTP(rw, r)
Expand Down Expand Up @@ -1803,7 +1807,8 @@ func (suite *ControllersTestSuite) TestCreateOrderCreds_SingleUse_ExistingOrderC
return h
}

router := Router(service, authMwr, instrumentHandler, newCORSOptsEnv())
lg := zerolog.Nop()
router := Router(&lg, service, authMwr, instrumentHandler, newCORSOptsEnv())

server := &http.Server{Addr: ":8080", Handler: router}
server.Handler.ServeHTTP(rw, r)
Expand Down

0 comments on commit 53b83d6

Please sign in to comment.