Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Production 2024-10-30_01 #2694

Merged
merged 6 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 38 additions & 11 deletions services/rewards/cmd/rest_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@ import (
"strconv"
"time"

"github.com/getsentry/sentry-go"
"github.com/go-chi/chi"
"github.com/spf13/cobra"
"github.com/spf13/viper"

cmdutils "github.com/brave-intl/bat-go/cmd"
appctx "github.com/brave-intl/bat-go/libs/context"
"github.com/brave-intl/bat-go/libs/handlers"
"github.com/brave-intl/bat-go/libs/middleware"
"github.com/brave-intl/bat-go/services/cmd"
"github.com/brave-intl/bat-go/services/rewards"
"github.com/getsentry/sentry-go"
"github.com/go-chi/chi"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/brave-intl/bat-go/services/rewards/handler"
)

// RestRun - Main entrypoint of the REST subcommand
Expand Down Expand Up @@ -48,7 +51,6 @@ func RestRun(command *cobra.Command, args []string) {

ctx = context.WithValue(ctx, appctx.ParametersVBATDeadlineCTXKey, viper.GetTime("vbat-deadline"))
ctx = context.WithValue(ctx, appctx.ParametersTransitionCTXKey, viper.GetBool("transition"))
// parse default-monthly-choices and default-tip-choices

var monthlyChoices []float64
if err := viper.UnmarshalKey("default-monthly-choices", &monthlyChoices); err != nil {
Expand All @@ -75,8 +77,24 @@ func RestRun(command *cobra.Command, args []string) {
lg.Fatal().Err(err).Msg("error retrieving rewards terms of service version")
}

cfg := rewards.Config{
// Get the bucket from the context and not os.Getenv so we don't diverge. GetParameters uses the context on
// each request and this will need to be refactored before we can remove it.
cardsBucket, ok := ctx.Value(appctx.ParametersMergeBucketCTXKey).(string)
if !ok {
lg.Fatal().Err(err).Msg("failed to get envar for cards bucket")
}

cardsKey := "cards.json"
if ck := os.Getenv("CARDS-KEY"); ck != "" {
cardsKey = ck
}

cfg := &rewards.Config{
TOSVersion: tosVersion,
Cards: &rewards.CardsConfig{
Bucket: cardsBucket,
Key: cardsKey,
},
}

s, err := rewards.InitService(ctx, cfg)
Expand All @@ -86,12 +104,12 @@ func RestRun(command *cobra.Command, args []string) {

lg.Info().Str("service", fmt.Sprintf("%+v", s)).Msg("initialized service")

// do rest endpoints
r := cmd.SetupRouter(ctx)
r.Get("/v1/parameters", middleware.InstrumentHandler(
"GetParametersHandler", rewards.GetParametersHandler(s)).ServeHTTP)

// make sure exceptions go to sentry
r.Get("/v1/parameters", middleware.InstrumentHandler("GetParametersHandler", rewards.GetParametersHandler(s)).ServeHTTP)

r.Mount("/v1/cards", newCardsRouter(s))

defer sentry.Flush(time.Second * 2)

go func() {
Expand All @@ -102,7 +120,6 @@ func RestRun(command *cobra.Command, args []string) {
}
}()

// setup server, and run
srv := http.Server{
Addr: viper.GetString("address"),
Handler: chi.ServerBaseContext(ctx, r),
Expand All @@ -115,3 +132,13 @@ func RestRun(command *cobra.Command, args []string) {
lg.Fatal().Err(err).Msg("HTTP server start failed!")
}
}

func newCardsRouter(svc *rewards.Service) chi.Router {
cardsRouter := chi.NewRouter()

ch := handler.NewCardsHandler(svc)

cardsRouter.Method(http.MethodGet, "/", middleware.InstrumentHandler("GetCards", handlers.AppHandler(ch.GetCardsHandler)))

return cardsRouter
}
48 changes: 19 additions & 29 deletions services/rewards/controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,38 @@ import (
"github.com/brave-intl/bat-go/libs/logging"
)

// GetParametersHandler - handler to get reward parameters
func GetParametersHandler(service *Service) handlers.AppHandler {
return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError {
// get context from request
ctx := r.Context()

var (
currencyInput = r.URL.Query().Get("currency")
parameters *ParametersV1
err error
)

return func(w http.ResponseWriter, r *http.Request) *handlers.AppError {
var currencyInput = r.URL.Query().Get("currency")
if currencyInput == "" {
currencyInput = "USD"
}

// get logger from context
logger := logging.Logger(ctx, "rewards.GetParametersHandler")
ctx := r.Context()

lg := logging.Logger(ctx, "rewards").With().Str("func", "GetParametersHandler").Logger()

currency := new(BaseCurrency)
if err := inputs.DecodeAndValidate(ctx, currency, []byte(currencyInput)); err != nil {
lg.Error().Err(err).Msg("failed decode and validate")

// in here we need to validate our currency
var currency = new(BaseCurrency)
if err = inputs.DecodeAndValidate(ctx, currency, []byte(currencyInput)); err != nil {
if errors.Is(err, ErrBaseCurrencyInvalid) {
logger.Error().Err(err).Msg("invalid currency input from caller")
return handlers.ValidationError(
"Error validating currency url parameter",
map[string]interface{}{
"err": err.Error(),
"currency": "invalid currency",
},
)
return handlers.ValidationError("Error validating currency url parameter", map[string]interface{}{
"err": err.Error(),
"currency": "invalid currency",
})
}
// degraded, unknown error when validating/decoding
logger.Error().Err(err).Msg("unforseen error in decode and validation")

return handlers.WrapError(err, "degraded: ", http.StatusInternalServerError)
}

parameters, err = service.GetParameters(ctx, currency)
parameters, err := service.GetParameters(ctx, currency)
if err != nil {
logger.Error().Err(err).Msg("failed to get reward parameters")
lg.Error().Err(err).Msg("failed to get reward parameters")

return handlers.WrapError(err, "failed to get parameters", http.StatusInternalServerError)
}

return handlers.RenderContent(ctx, parameters, w, http.StatusOK)
})
}
}
72 changes: 20 additions & 52 deletions services/rewards/controllers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ import (
"github.com/golang/mock/gomock"
)

type mockGetObjectAPI func(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error)

func (m mockGetObjectAPI) GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) {
return m(ctx, params, optFns...)
}

func TestGetParametersController(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
Expand All @@ -40,54 +34,29 @@ func TestGetParametersController(t *testing.T) {
"usd": decimal.New(10, 0),
}}, nil)

var mockS3PayoutStatus = mockGetObjectAPI(func(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) {
return &s3.GetObjectOutput{
Body: io.NopCloser(bytes.NewBufferString(`{
"uphold":"processing",
"gemini":"off",
"bitflyer":"off",
"unverified":"off"
mockS3Svc := &mockS3Service{
fnGetObject: func(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) {
if *params.Key == "payout-status.json" {
body := io.NopCloser(bytes.NewBufferString(`{"uphold":"processing","gemini":"off","bitflyer":"off","unverified":"off"}`))

return &s3.GetObjectOutput{Body: body}, nil
}
`)),
}, nil
})

var mockS3CustodianRegions = mockGetObjectAPI(func(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) {
return &s3.GetObjectOutput{
Body: io.NopCloser(bytes.NewBufferString(`{
"uphold": {
"allow": [],
"block": []
},
"gemini": {
"allow": [],
"block": []
},
"bitflyer": {
"allow": [],
"block": []
}
}`)),
}, nil
})

var mockS3 = mockGetObjectAPI(func(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) {
if *params.Key == "payout-status.json" {
return mockS3PayoutStatus(ctx, params, optFns...)
}

if *params.Key == "custodian-regions.json" {
return mockS3CustodianRegions(ctx, params, optFns...)
}

return nil, errors.New("invalid key")
})

if *params.Key == "custodian-regions.json" {
body := io.NopCloser(bytes.NewBufferString(`{"uphold":{"allow":[],"block":[]},"gemini":{"allow":[],"block":[]},"bitflyer":{"allow":[],"block":[]}}`))

return &s3.GetObjectOutput{Body: body}, nil
}

return nil, errors.New("invalid key")
},
}

s := &Service{
cfg: Config{TOSVersion: 1},
ratios: mockRatios,
s3Client: mockS3,
cacheMu: new(sync.RWMutex),
cfg: &Config{TOSVersion: 1},
ratios: mockRatios,
cacheMu: new(sync.RWMutex),
s3Svc: mockS3Svc,
}

req, err := http.NewRequest(http.MethodGet, "/v1/parameters", nil)
Expand All @@ -114,7 +83,6 @@ func TestGetParametersController(t *testing.T) {
assert.Equal(t, "processing", params.PayoutStatus.Uphold)
assert.ElementsMatch(t, []float64{3, 5, 7, 10, 20}, params.AutoContribute.Choices)
assert.Equal(t, float64(10), params.BATRate)
assert.Equal(t, 1, params.TOSVersion)
}

func setupRouter(s *Service) *chi.Mux {
Expand Down
52 changes: 52 additions & 0 deletions services/rewards/handler/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package handler

import (
"context"
"net/http"

"github.com/brave-intl/bat-go/libs/handlers"
"github.com/brave-intl/bat-go/libs/logging"
"github.com/brave-intl/bat-go/services/rewards"
"github.com/brave-intl/bat-go/services/rewards/model"
)

type cardService interface {
GetCardsAsBytes(ctx context.Context) (rewards.CardBytes, error)
}

type CardsHandler struct {
cardSvc cardService
}

func NewCardsHandler(cardSvc cardService) *CardsHandler {
return &CardsHandler{
cardSvc: cardSvc,
}
}

const errSomethingWentWrong model.Error = "something went wrong"

func (c *CardsHandler) GetCardsHandler(w http.ResponseWriter, r *http.Request) *handlers.AppError {
ctx := r.Context()

l := logging.Logger(ctx, "handler").With().Str("func", "GetCardsHandler").Logger()

cards, err := c.cardSvc.GetCardsAsBytes(ctx)
if err != nil {
l.Err(err).Msg("failed to get cards as bytes")

return handlers.WrapError(errSomethingWentWrong, errSomethingWentWrong.Error(), http.StatusInternalServerError)
}

w.WriteHeader(http.StatusOK)

w.Header().Set("Content-Type", "application/json")

if _, err := w.Write(cards); err != nil {
kdenhartog marked this conversation as resolved.
Show resolved Hide resolved
l.Err(err).Msg("failed to write response")

return handlers.WrapError(errSomethingWentWrong, errSomethingWentWrong.Error(), http.StatusInternalServerError)
}

return nil
}
Loading
Loading