diff --git a/Dockerfile b/Dockerfile index eb433e2..d5d44d1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,8 +36,5 @@ ENV OFFERS_POSTGRES_HOST localhost ENV OFFERS_POSTGRES_PASSWORD "" ENV OFFERS_POSTGRES_PORT 8585 ENV OFFERS_POSTGRES_USER offers -ENV OFFERS_REDIS_HOST localhost -ENV OFFERS_REDIS_PASSWORD "" -ENV OFFERS_REDIS_PORT 6333 CMD /app/offers start -v2 -c /app/config/local.yaml diff --git a/README.md b/README.md index 9e9d2a7..f8626cc 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ Offers is a service meant to handle offers/promotions in your games. ### Dependencies * Go 1.7 * Postgres >= 9.5 -* Redis ### Setup First, set your $GOPATH ([Go Lang](https://golang.org/doc/install)) env variable and add $GOPATH/bin to your $PATH @@ -52,12 +51,6 @@ Offers uses PostgreSQL to store offers information. The container takes environm * `OFFERS_POSTGRES_PASSWORD` - Password of the PostgreSQL Server to connect to; * `OFFERS_POSTGRES_USER` - PostgreSQL user; -Offers also uses Redis to store offers views and purchases information. The container takes environment variables to specify this connection: - -* `OFFERS_REDIS_HOST` - Redis host to connect to; -* `OFFERS_REDIS_PORT` - Redis port to connect to; -* `OFFERS_REDIS_PASSWORD` - Password of the Redis database to connect to; - Offers uses basic auth to restrict access to routes that are not used directly by a client consuming the offers. * `OFFERS_BASICAUTH_USERNAME` - Basic Auth user; diff --git a/api/api_suite_test.go b/api/api_suite_test.go index 607d1e8..18efa13 100644 --- a/api/api_suite_test.go +++ b/api/api_suite_test.go @@ -40,6 +40,9 @@ var _ = BeforeSuite(func() { db, err = oTesting.GetTestDB() Expect(err).NotTo(HaveOccurred()) + err = oTesting.ClearOfferPlayers(db) + Expect(err).NotTo(HaveOccurred()) + err = oTesting.LoadFixtures(db) Expect(err).NotTo(HaveOccurred()) @@ -62,8 +65,6 @@ var _ = AfterEach(func() { err := app.DB.(*runner.Tx).Rollback() Expect(err).NotTo(HaveOccurred()) app.DB = db - status := app.RedisClient.Client.FlushAll() - Expect(status.Err()).NotTo(HaveOccurred()) app.Clock = oTesting.MockClock{ CurrentTime: 1486678000, } @@ -77,9 +78,6 @@ var _ = AfterSuite(func() { db = nil } - status := app.RedisClient.Client.FlushAll() - Expect(status.Err()).NotTo(HaveOccurred()) - if closer != nil { closer.Close() closer = nil diff --git a/api/app.go b/api/app.go index 5dfccea..f3a5222 100644 --- a/api/app.go +++ b/api/app.go @@ -25,7 +25,6 @@ import ( "github.com/topfreegames/offers/errors" "github.com/topfreegames/offers/metadata" "github.com/topfreegames/offers/models" - "github.com/topfreegames/offers/util" runner "gopkg.in/mgutz/dat.v2/sqlx-runner" ) @@ -39,7 +38,6 @@ type App struct { Logger logrus.FieldLogger MaxAge int64 NewRelic newrelic.Application - RedisClient *util.RedisClient Router *mux.Router Server *http.Server Cache *cache.Cache @@ -208,11 +206,6 @@ func (a *App) configureApp() error { return err } - err = a.configureRedisClient() - if err != nil { - return err - } - err = a.configureNewRelic() if err != nil { return err @@ -243,31 +236,6 @@ func (a *App) configureCache() { a.OffersCacheMaxAge = maxAge } -func (a *App) configureRedisClient() error { - redisHost := a.Config.GetString("redis.host") - redisPort := a.Config.GetInt("redis.port") - redisPass := a.Config.GetString("redis.password") - redisDB := a.Config.GetInt("redis.db") - redisMaxPoolSize := a.Config.GetInt("redis.maxPoolSize") - - l := a.Logger.WithFields(logrus.Fields{ - "redis.host": redisHost, - "redis.port": redisPort, - "redis.redisDB": redisDB, - "redis.redisMaxPoolSize": redisMaxPoolSize, - }) - l.Debug("Connecting to Redis...") - cli, err := util.GetRedisClient(redisHost, redisPort, redisPass, redisDB, redisMaxPoolSize, a.Logger) - if err != nil { - l.WithError(err).Error("Connection to redis failed.") - return err - } - l.Debug("Successful connection to redis.") - a.RedisClient = cli - - return nil -} - func (a *App) configureDatabase() error { db, err := a.getDB() if err != nil { diff --git a/api/game_test.go b/api/game_test.go index 5121a5a..60693b8 100644 --- a/api/game_test.go +++ b/api/game_test.go @@ -211,7 +211,7 @@ var _ = Describe("Game Handler", func() { var obj []map[string]interface{} err := json.Unmarshal([]byte(recorder.Body.String()), &obj) Expect(err).NotTo(HaveOccurred()) - Expect(obj).To(HaveLen(9)) + Expect(obj).To(HaveLen(13)) for i := 0; i < len(obj); i++ { Expect(obj[i]).To(HaveKey("id")) Expect(obj[i]).To(HaveKey("name")) @@ -220,7 +220,9 @@ var _ = Describe("Game Handler", func() { }) It("should return empty list if no games", func() { - _, err := app.DB.DeleteFrom("offer_instances").Exec() + _, err := app.DB.DeleteFrom("offer_players").Exec() + Expect(err).NotTo(HaveOccurred()) + _, err = app.DB.DeleteFrom("offer_instances").Exec() Expect(err).NotTo(HaveOccurred()) _, err = app.DB.DeleteFrom("offers").Exec() Expect(err).NotTo(HaveOccurred()) diff --git a/api/offer_request.go b/api/offer_request.go index 37c6532..f483815 100644 --- a/api/offer_request.go +++ b/api/offer_request.go @@ -111,7 +111,7 @@ func (h *OfferRequestHandler) getOffers(w http.ResponseWriter, r *http.Request) var offers map[string][]*models.OfferToReturn err = mr.WithSegment(models.SegmentModel, func() error { - offers, err = models.GetAvailableOffers(h.App.DB, h.App.RedisClient, h.App.Cache, gameID, playerID, currentTime, h.App.OffersCacheMaxAge, filterAttrs, allowInefficientQueries, mr) + offers, err = models.GetAvailableOffers(h.App.DB, h.App.Cache, gameID, playerID, currentTime, h.App.OffersCacheMaxAge, filterAttrs, allowInefficientQueries, mr) return err }) @@ -152,7 +152,6 @@ func (h *OfferRequestHandler) claimOffer(w http.ResponseWriter, r *http.Request) contents, alreadyClaimed, nextAt, err := models.ClaimOffer( h.App.DB, - h.App.RedisClient, payload.GameID, payload.OfferInstanceID, payload.PlayerID, @@ -203,7 +202,6 @@ func (h *OfferRequestHandler) viewOffer(w http.ResponseWriter, r *http.Request) alreadyViewed, nextAt, err := models.ViewOffer( h.App.DB, - h.App.RedisClient, payload.GameID, offerInstanceID, payload.PlayerID, @@ -267,7 +265,7 @@ func (h *OfferRequestHandler) offerInfo(w http.ResponseWriter, r *http.Request) var err error var offer *models.OfferToReturn err = mr.WithSegment(models.SegmentModel, func() error { - offer, err = models.GetOfferInfo(h.App.DB, h.App.RedisClient, gameID, playerID, offerInstanceID, h.App.OffersCacheMaxAge, mr) + offer, err = models.GetOfferInfo(h.App.DB, gameID, playerID, offerInstanceID, h.App.OffersCacheMaxAge, mr) return err }) diff --git a/api/offer_request_test.go b/api/offer_request_test.go index 7428c8e..c90fd04 100644 --- a/api/offer_request_test.go +++ b/api/offer_request_test.go @@ -623,8 +623,7 @@ var _ = Describe("Offer Handler", func() { offerID := "dd21ec96-2890-4ba0-b8e2-40ea67196990" gameID := "offers-game" playerID := "player-1" - claimCounterKey := models.GetClaimCounterKey(playerID, offerID) - claimTimestampKey := models.GetClaimTimestampKey(playerID, offerID) + offerReader := JSONFor(JSON{ "gameId": gameID, "playerId": playerID, @@ -640,13 +639,10 @@ var _ = Describe("Offer Handler", func() { Expect(recorder.Body.String()).To(Equal(fmt.Sprintf(`{"contents":{"gems":5,"gold":100},"nextAt":%v}`, app.Clock.GetTime().Unix()+1))) Expect(recorder.Code).To(Equal(http.StatusOK)) - claimCount, err := app.RedisClient.Client.Get(claimCounterKey).Int64() - Expect(err).ToNot(HaveOccurred()) - Expect(claimCount).To(Equal(int64(1))) - - claimTimestamp, err := app.RedisClient.Client.Get(claimTimestampKey).Int64() - Expect(err).ToNot(HaveOccurred()) - Expect(claimTimestamp).To(Equal(app.Clock.GetTime().Unix())) + offerPlayer, err := models.GetOfferPlayer(app.DB, gameID, playerID, offerID, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(offerPlayer.ClaimCounter).To(Equal(1)) + Expect(offerPlayer.ClaimTimestamp.Time.Unix()).To(Equal(app.Clock.GetTime().Unix())) }) It("should claim valid offer even if id is not passed", func() { @@ -661,21 +657,16 @@ var _ = Describe("Offer Handler", func() { }) request, _ := http.NewRequest("PUT", "/offers/claim", offerReader) offerID := "dd21ec96-2890-4ba0-b8e2-40ea67196990" - claimCounterKey := models.GetClaimCounterKey(playerID, offerID) - claimTimestampKey := models.GetClaimTimestampKey(playerID, offerID) app.Router.ServeHTTP(recorder, request) Expect(recorder.Header().Get("Content-Type")).To(Equal("application/json")) Expect(recorder.Body.String()).To(Equal(fmt.Sprintf(`{"contents":{"gems":5,"gold":100},"nextAt":%v}`, app.Clock.GetTime().Unix()+1))) Expect(recorder.Code).To(Equal(http.StatusOK)) - claimCount, err := app.RedisClient.Client.Get(claimCounterKey).Int64() - Expect(err).ToNot(HaveOccurred()) - Expect(claimCount).To(Equal(int64(1))) - - claimTimestamp, err := app.RedisClient.Client.Get(claimTimestampKey).Int64() - Expect(err).ToNot(HaveOccurred()) - Expect(claimTimestamp).To(Equal(app.Clock.GetTime().Unix())) + offerPlayer, err := models.GetOfferPlayer(app.DB, gameID, playerID, offerID, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(offerPlayer.ClaimCounter).To(Equal(1)) + Expect(offerPlayer.ClaimTimestamp.Time.Unix()).To(Equal(app.Clock.GetTime().Unix())) }) It("should not claim a claimed offer", func() { @@ -709,8 +700,6 @@ var _ = Describe("Offer Handler", func() { offerInstanceID := "4407b770-5b24-4ffa-8563-0694d1a10156" gameID := "offers-game" playerID := "player-1" - claimCounterKey := models.GetClaimCounterKey(playerID, offerID) - claimTimestampKey := models.GetClaimTimestampKey(playerID, offerID) transactionID := uuid.NewV4().String() offerReader := JSONFor(JSON{ @@ -742,13 +731,10 @@ var _ = Describe("Offer Handler", func() { Expect(recorder.Body.String()).To(Equal(fmt.Sprintf(`{"contents":{"gems":5,"gold":100},"nextAt":%v}`, app.Clock.GetTime().Unix()+12*60*60))) Expect(recorder.Code).To(Equal(http.StatusConflict)) - claimCount, err := app.RedisClient.Client.Get(claimCounterKey).Int64() - Expect(err).ToNot(HaveOccurred()) - Expect(claimCount).To(Equal(int64(1))) - - claimTimestamp, err := app.RedisClient.Client.Get(claimTimestampKey).Int64() - Expect(err).ToNot(HaveOccurred()) - Expect(claimTimestamp).To(Equal(app.Clock.GetTime().Unix())) + offerPlayer, err := models.GetOfferPlayer(app.DB, gameID, playerID, offerID, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(offerPlayer.ClaimCounter).To(Equal(1)) + Expect(offerPlayer.ClaimTimestamp.Time.Unix()).To(Equal(app.Clock.GetTime().Unix())) }) It("should not return nextAt if max is achieved", func() { @@ -756,8 +742,6 @@ var _ = Describe("Offer Handler", func() { offerID := "9456f6c4-f9f1-4dd9-8841-9e5770c8e62c" gameID := "offers-game-max-freq" playerID := "player-1" - claimCounterKey := models.GetClaimCounterKey(playerID, offerID) - claimTimestampKey := models.GetClaimTimestampKey(playerID, offerID) offerReader := JSONFor(JSON{ "gameId": gameID, "playerId": playerID, @@ -773,13 +757,10 @@ var _ = Describe("Offer Handler", func() { Expect(recorder.Body.String()).To(Equal(`{"contents":{"gems":5,"gold":100}}`)) Expect(recorder.Code).To(Equal(http.StatusOK)) - claimCount, err := app.RedisClient.Client.Get(claimCounterKey).Int64() - Expect(err).ToNot(HaveOccurred()) - Expect(claimCount).To(Equal(int64(1))) - - claimTimestamp, err := app.RedisClient.Client.Get(claimTimestampKey).Int64() - Expect(err).ToNot(HaveOccurred()) - Expect(claimTimestamp).To(Equal(app.Clock.GetTime().Unix())) + offerPlayer, err := models.GetOfferPlayer(app.DB, gameID, playerID, offerID, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(offerPlayer.ClaimCounter).To(Equal(1)) + Expect(offerPlayer.ClaimTimestamp.Time.Unix()).To(Equal(app.Clock.GetTime().Unix())) }) It("should return 422 if invalid OfferID", func() { @@ -957,8 +938,7 @@ var _ = Describe("Offer Handler", func() { offerID := "dd21ec96-2890-4ba0-b8e2-40ea67196990" gameID := "offers-game" playerID := "player-1" - viewCounterKey := models.GetViewCounterKey(playerID, offerID) - viewTimestampKey := models.GetViewTimestampKey(playerID, offerID) + offerReader := JSONFor(JSON{ "playerId": playerID, "gameId": gameID, @@ -974,13 +954,10 @@ var _ = Describe("Offer Handler", func() { Expect(err).NotTo(HaveOccurred()) Expect(int64(obj["nextAt"].(float64))).To(Equal(app.Clock.GetTime().Unix() + 1)) - viewCount, err := app.RedisClient.Client.Get(viewCounterKey).Int64() + offerPlayer, err := models.GetOfferPlayer(app.DB, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) - Expect(viewCount).To(Equal(int64(1))) - - viewTimestamp, err := app.RedisClient.Client.Get(viewTimestampKey).Int64() - Expect(err).NotTo(HaveOccurred()) - Expect(viewTimestamp).To(Equal(app.Clock.GetTime().Unix())) + Expect(offerPlayer.ViewCounter).To(Equal(1)) + Expect(offerPlayer.ViewTimestamp.Time.Unix()).To(Equal(app.Clock.GetTime().Unix())) }) It("should return the current timestamp as nextAt if offer reached max period", func() { @@ -1026,8 +1003,6 @@ var _ = Describe("Offer Handler", func() { offerID := "dd21ec96-2890-4ba0-b8e2-40ea67196990" gameID := "offers-game" playerID := "player-1" - viewCounterKey := models.GetViewCounterKey(playerID, offerID) - viewTimestampKey := models.GetViewTimestampKey(playerID, offerID) // View for the first time offerReader := JSONFor(JSON{ @@ -1060,13 +1035,10 @@ var _ = Describe("Offer Handler", func() { Expect(err).NotTo(HaveOccurred()) Expect(int64(obj["nextAt"].(float64))).To(Equal(app.Clock.GetTime().Unix() + 1)) - viewCount, err := app.RedisClient.Client.Get(viewCounterKey).Int64() - Expect(err).NotTo(HaveOccurred()) - Expect(viewCount).To(Equal(int64(2))) - - viewTimestamp, err := app.RedisClient.Client.Get(viewTimestampKey).Int64() + offerPlayer, err := models.GetOfferPlayer(app.DB, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) - Expect(viewTimestamp).To(Equal(app.Clock.GetTime().Unix())) + Expect(offerPlayer.ViewCounter).To(Equal(2)) + Expect(offerPlayer.ViewTimestamp.Time.Unix()).To(Equal(app.Clock.GetTime().Unix())) }) It("should return nextAt zero after seeing twice offer with max period 2", func() { @@ -1074,8 +1046,14 @@ var _ = Describe("Offer Handler", func() { playerID := "player-1" offerID := "aa65a3f2-7cf8-4d76-957f-0a23a1bbbd32" gameID := "limited-offers-game" - viewCounterKey := models.GetViewCounterKey(playerID, offerID) - err := app.RedisClient.Client.Set(viewCounterKey, 1, 0).Err() + + op := &models.OfferPlayer{ + GameID: gameID, + PlayerID: playerID, + OfferID: offerID, + ViewCounter: 1, + } + err := models.CreateOfferPlayer(app.DB, op, nil) Expect(err).ToNot(HaveOccurred()) offerReader := JSONFor(JSON{ @@ -1098,8 +1076,6 @@ var _ = Describe("Offer Handler", func() { offerID := "dd21ec96-2890-4ba0-b8e2-40ea67196990" gameID := "offers-game" playerID := "player-1" - viewCounterKey := models.GetViewCounterKey(playerID, offerID) - viewTimestampKey := models.GetViewTimestampKey(playerID, offerID) impressionID := uuid.NewV4().String() timestamp := app.Clock.GetTime().Unix() @@ -1134,13 +1110,10 @@ var _ = Describe("Offer Handler", func() { Expect(err).NotTo(HaveOccurred()) Expect(int64(obj["nextAt"].(float64))).To(Equal(timestamp + 2)) - viewCount, err := app.RedisClient.Client.Get(viewCounterKey).Int64() - Expect(err).NotTo(HaveOccurred()) - Expect(viewCount).To(Equal(int64(1))) - - viewTimestamp, err := app.RedisClient.Client.Get(viewTimestampKey).Int64() + offerPlayer, err := models.GetOfferPlayer(app.DB, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) - Expect(viewTimestamp).To(Equal(timestamp)) + Expect(offerPlayer.ViewCounter).To(Equal(1)) + Expect(offerPlayer.ViewTimestamp.Time.Unix()).To(Equal(timestamp)) }) It("should not increment when is a retry request and rechead max view", func() { @@ -1148,18 +1121,19 @@ var _ = Describe("Offer Handler", func() { offerID := "dd21ec96-2890-4ba0-b8e2-40ea67196990" gameID := "offers-game" playerID := "player-1" - viewCounterKey := models.GetViewCounterKey(playerID, offerID) - viewTimestampKey := models.GetViewTimestampKey(playerID, offerID) - impressionKey := models.GetImpressionsKey(playerID, gameID) impressionID := uuid.NewV4().String() timestamp := app.Clock.GetTime().Unix() // already seen once - err := app.RedisClient.Client.Set(viewCounterKey, 1, 0).Err() - Expect(err).ToNot(HaveOccurred()) - err = app.RedisClient.Client.Set(viewTimestampKey, timestamp, 0).Err() - Expect(err).ToNot(HaveOccurred()) - err = app.RedisClient.Client.SAdd(impressionKey, impressionID).Err() + op := &models.OfferPlayer{ + GameID: gameID, + PlayerID: playerID, + OfferID: offerID, + ViewCounter: 1, + ViewTimestamp: dat.NullTimeFrom(time.Unix(timestamp, 0)), + Impressions: dat.JSON([]byte(fmt.Sprintf(`["%s"]`, impressionID))), + } + err := models.CreateOfferPlayer(app.DB, op, nil) Expect(err).ToNot(HaveOccurred()) offerReader := JSONFor(JSON{ @@ -1176,9 +1150,10 @@ var _ = Describe("Offer Handler", func() { Expect(err).NotTo(HaveOccurred()) Expect(int64(obj["nextAt"].(float64))).To(Equal(timestamp + 1)) - viewCount, err := app.RedisClient.Client.Get(viewCounterKey).Int64() + offerPlayer, err := models.GetOfferPlayer(app.DB, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) - Expect(viewCount).To(Equal(int64(1))) + Expect(offerPlayer.ViewCounter).To(Equal(1)) + Expect(offerPlayer.ViewTimestamp.Time.Unix()).To(Equal(timestamp)) }) It("should return status code 422 if invalid parameters", func() { diff --git a/config/acc.yaml b/config/acc.yaml index 28e32ac..5fa46d5 100644 --- a/config/acc.yaml +++ b/config/acc.yaml @@ -6,12 +6,6 @@ postgres: sslMode: "disable" maxIdleConns: 10 maxOpenConns: 10 -redis: - host: localhost - port: 6333 - password: "" - db: 0 - maxPoolSize: 20 cache: maxAgeSeconds: 43200 offersCache: diff --git a/config/local.yaml b/config/local.yaml index 5db94a1..3aac896 100644 --- a/config/local.yaml +++ b/config/local.yaml @@ -9,12 +9,6 @@ postgres: newrelic: app: offers key: -redis: - host: localhost - port: 6333 - password: "" - db: 0 - maxPoolSize: 20 cache: maxAgeSeconds: 43200 offersCache: diff --git a/config/perf.yaml b/config/perf.yaml index 398ada9..2634a49 100644 --- a/config/perf.yaml +++ b/config/perf.yaml @@ -4,12 +4,6 @@ postgres: host: "localhost" port: 8585 sslMode: "disable" -redis: - host: localhost - port: 6333 - password: "" - db: 0 - maxPoolSize: 20 offersCache: maxAgeSeconds: 300 cleanupInterval: 30 diff --git a/config/test.yaml b/config/test.yaml index dda9691..df44254 100644 --- a/config/test.yaml +++ b/config/test.yaml @@ -9,12 +9,6 @@ postgres: newrelic: app: offers key: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -redis: - host: localhost - port: 6333 - password: "" - db: 0 - maxPoolSize: 20 cache: maxAgeSeconds: 43200 offersCache: diff --git a/docker-compose.yml b/docker-compose.yml index dc5e8b0..4051ce8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,3 @@ services: image: postgres:9.5 ports: - "8585:5432" - redis: - image: redis - ports: - - "6333:6379" diff --git a/docs/source/hosting.md b/docs/source/hosting.md index 5a679ad..8b760ca 100644 --- a/docs/source/hosting.md +++ b/docs/source/hosting.md @@ -13,12 +13,6 @@ Offers uses PostgreSQL to store offers information. The container takes environm * `OFFERS_POSTGRES_PASSWORD` - Password of the PostgreSQL Server to connect to; * `OFFERS_POSTGRES_USER` - PostgreSQL user; -Offers also uses Redis to store offers views and purchases information. The container takes environment variables to specify this connection: - -* `OFFERS_REDIS_HOST` - Redis host to connect to; -* `OFFERS_REDIS_PORT` - Redis port to connect to; -* `OFFERS_REDIS_PASSWORD` - Password of the Redis database to connect to; - Offers uses basic auth to restrict access to routes that are not used directly by a client consuming the offers. * `OFFERS_BASICAUTH_USERNAME` - Basic Auth user; diff --git a/docs/source/overview.md b/docs/source/overview.md index 9336cb4..c2226a4 100644 --- a/docs/source/overview.md +++ b/docs/source/overview.md @@ -20,7 +20,6 @@ Offers is composed of an API responsible for creation of games and templates, an Our code is in Golang, with: * Database - Postgres >= 9.5; -* Redis; ## Who's Using it diff --git a/features/godogs_step_definitions_test.go b/features/godogs_step_definitions_test.go index dacac93..d90f0bc 100644 --- a/features/godogs_step_definitions_test.go +++ b/features/godogs_step_definitions_test.go @@ -38,7 +38,6 @@ import ( "github.com/topfreegames/offers/errors" "github.com/topfreegames/offers/models" "github.com/topfreegames/offers/testing" - redis "gopkg.in/redis.v5" ) var app *api.App @@ -186,7 +185,7 @@ func theFollowingPlayersExistInTheGame(gameID string, players *gherkin.DataTable } currentTime := time.Unix(int64(unixTime), 0) - if _, err := models.GetAvailableOffers(app.DB, app.RedisClient, gameID, playerID, currentTime, nil); err != nil { + if _, err := models.GetAvailableOffers(app.DB, gameID, playerID, currentTime, nil); err != nil { return err } offer, err := selectOfferInstanceByOfferNameAndPlayerAndGame(offerName, playerID, gameID) @@ -194,7 +193,7 @@ func theFollowingPlayersExistInTheGame(gameID string, players *gherkin.DataTable return err } - _, _, err = models.ViewOffer(app.DB, app.RedisClient, gameID, offer.ID, playerID, uuid.NewV4().String(), currentTime, nil) + _, _, err = models.ViewOffer(app.DB, gameID, offer.ID, playerID, uuid.NewV4().String(), currentTime, nil) if err != nil { return err } @@ -392,13 +391,11 @@ func playerOfGameHasSeenOfferInstance(playerID, gameID, offerName string) error return err } - viewCounterKey := models.GetViewCounterKey(playerID, offerInstance.OfferID) - viewCounter, err := app.RedisClient.Client.Get(viewCounterKey).Int64() - - if err != nil && err != redis.Nil { + offerPlayer, err := models.GetOfferPlayer(app.DB, gameID, playerID, offerInstance.OfferID, nil) + if err != nil && !models.IsNoRowsInResultSetError(err) { return err } - if viewCounter == 0 { + if offerPlayer.ViewCounter == 0 { return fmt.Errorf("Expected player %s of game %s to has seen offer %s", playerID, gameID, offerName) } @@ -493,7 +490,7 @@ func theFollowingPlayersClaimedInTheGame(gameID string, players *gherkin.DataTab } currentTime := time.Unix(int64(unixTime), 0) - if _, err = models.GetAvailableOffers(app.DB, app.RedisClient, gameID, playerID, currentTime, nil); err != nil { + if _, err = models.GetAvailableOffers(app.DB, gameID, playerID, currentTime, nil); err != nil { return err } offerInstance, err := selectOfferInstanceByOfferNameAndPlayerAndGame(offerName, playerID, gameID) @@ -501,7 +498,7 @@ func theFollowingPlayersClaimedInTheGame(gameID string, players *gherkin.DataTab return err } - _, _, _, err = models.ClaimOffer(app.DB, app.RedisClient, gameID, offerInstance.ID, playerID, "", uuid.NewV4().String(), currentTime.Unix(), currentTime, nil) + _, _, _, err = models.ClaimOffer(app.DB, gameID, offerInstance.ID, playerID, "", uuid.NewV4().String(), currentTime.Unix(), currentTime, nil) if err != nil { return err } diff --git a/fixtures/games.yml b/fixtures/games.yml index cd0d8e3..8597603 100644 --- a/fixtures/games.yml +++ b/fixtures/games.yml @@ -29,6 +29,10 @@ id: another-game name: another-game created_at: 2016-01-01 12:30:12 +- + id: another-game-2 + name: another-game-2 + created_at: 2016-01-01 12:30:12 - id: offers-game-maxage name: offers-game-maxage @@ -42,3 +46,15 @@ id: non-existing-offers-game name: non-existing-offers-game created_at: 2016-01-01 12:30:12 +- + id: another-game-v8 + name: another-game-v8 + created_at: 2016-01-01 12:30:12 +- + id: offers-game-max-period + name: offers-game-max-period + created_at: 2016-01-01 12:30:12 +- + id: offers-game-max-freq + name: offers-game-max-freq + created_at: 2016-01-01 12:30:12 diff --git a/fixtures/offers.yml b/fixtures/offers.yml index 92dffd0..0583919 100644 --- a/fixtures/offers.yml +++ b/fixtures/offers.yml @@ -200,3 +200,15 @@ placement: store filters: '{}' enabled: true +- + id: f1f74fcd-17ae-4ccd-a248-f77c60e78c82 + name: template-12 + product_id: com.tfg.sample + game_id: another-game-2 + contents: '{"gems": 5, "gold": 100}' + metadata: '{}' + period: '{"every": "10s", "max": 2}' + frequency: '{"max": 2}' + trigger: '{"from": 1486678000, "to": 1486679000}' + placement: unique-place + enabled: true diff --git a/glide.lock b/glide.lock index 572b4f3..d72ac94 100644 --- a/glide.lock +++ b/glide.lock @@ -1,14 +1,14 @@ -hash: abafd531d29b514b81ec94ecda010a4b9d93f53dccee4c1c05a261dbf8fb5c8a -updated: 2017-04-24T16:46:44.094074497-03:00 +hash: cc16cedcf6beabcfa53f11d41c6fd363241c9d59fdad699e414155d059a88be6 +updated: 2018-02-16T14:05:02.727541506-02:00 imports: - name: github.com/asaskevich/govalidator version: 7b3beb6df3c42abd3509abfc3bcacc0fbfb7c877 - name: github.com/cenkalti/backoff - version: 32cd0c5b3aef12c76ed64aaf678f6c79736be7dc + version: 61153c768f31ee5f130071d08fc82b85208528de - name: github.com/certifi/gocertifi - version: a9c833d2837d3b16888d55d5aafa9ffe9afb22b0 + version: a4ab0227d360091084658029ec8ae45f989fba3e - name: github.com/DATA-DOG/godog - version: cf8fbb4ad0d5fab464a5c9b79abbcfbd7c27e85b + version: 4dc98b0e2b130c3c9d06868a050320e5b310d3e7 subpackages: - colors - gherkin @@ -22,11 +22,11 @@ imports: - name: github.com/getsentry/raven-go version: 3f7439d3e74d88e21d196ba20eb61a5a958bc118 - name: github.com/go-testfixtures/testfixtures - version: d55cd3ac660195e9c5115fad1c0c9e3ac3bcf527 + version: f79bf941e2785516a66bcf4de9a21b0b827ed716 - name: github.com/gorilla/context version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42 - name: github.com/gorilla/mux - version: 392c28fe23e1c45ddba891b0320b3b5df220beea + version: 53c1911da2b537f792e7cafcb446b05ffe33b996 - name: github.com/GuiaBolso/darwin version: 86919dfcf80873d58ea2e527c15b1e02a5636ead - name: github.com/hashicorp/hcl @@ -69,15 +69,16 @@ imports: - name: github.com/mitchellh/mapstructure version: ca63d7c062ee3c9f34db231e352b60012b4fd0c1 - name: github.com/newrelic/go-agent - version: 29ec3cd1bb2f21d21d36da37dae52695cb2c3a17 + version: f5bce3387232559bcbe6a5f8227c4bf508dac1ba subpackages: - internal + - internal/cat - internal/jsonx - internal/logger - internal/sysinfo - internal/utilization - name: github.com/onsi/ginkgo - version: 77a8c1e5c40d6bb6c5eb4dd4bdce9763564f6298 + version: 9eda700730cba42af70d53180f9dcce9266bc2bc subpackages: - config - internal/codelocation @@ -97,7 +98,7 @@ imports: - reporters/stenographer/support/go-isatty - types - name: github.com/onsi/gomega - version: 334b8f472b3af5d541c5642701c1e29e2126f486 + version: 003f63b7f4cff3fc95357005358af2de0f5fe152 subpackages: - format - internal/assertion @@ -148,6 +149,13 @@ imports: - ed25519 - ed25519/internal/edwards25519 - ssh +- name: golang.org/x/net + version: 2a824cf9226006580a06d9fa8f10901c17b49ed5 + subpackages: + - context + - html + - html/atom + - html/charset - name: golang.org/x/sys version: a646d33e2ee3172a661fc09bca23bb4889a41bc8 subpackages: @@ -155,6 +163,20 @@ imports: - name: golang.org/x/text version: d69c40b4be55797923cec7457fac7a244d91a9b6 subpackages: + - encoding + - encoding/charmap + - encoding/htmlindex + - encoding/internal + - encoding/internal/identifier + - encoding/japanese + - encoding/korean + - encoding/simplifiedchinese + - encoding/traditionalchinese + - encoding/unicode + - internal/tag + - internal/utf8internal + - language + - runes - transform - unicode/norm - name: gopkg.in/mgutz/dat.v2 @@ -166,14 +188,6 @@ imports: - postgres - reflectx - sqlx-runner -- name: gopkg.in/redis.v5 - version: a16aeec10ff407b1e7be6dd35797ccf5426ef0f0 - subpackages: - - internal - - internal/consistenthash - - internal/hashtag - - internal/pool - - internal/proto - name: gopkg.in/yaml.v2 version: e4d366fc3c7938e2958e662b4258c7a89e1f0e3e testImports: [] diff --git a/glide.yaml b/glide.yaml index aa91c9d..bf08c11 100644 --- a/glide.yaml +++ b/glide.yaml @@ -24,8 +24,6 @@ import: version: ^5.0.0 - package: gopkg.in/mgutz/dat.v2 version: v2 -- package: gopkg.in/redis.v5 - version: ^5.2.9 - package: github.com/patrickmn/go-cache - package: github.com/getsentry/raven-go - package: github.com/certifi/gocertifi diff --git a/migrations/0010-CreateOfferPlayerTable.sql b/migrations/0010-CreateOfferPlayerTable.sql new file mode 100644 index 0000000..ab22bb6 --- /dev/null +++ b/migrations/0010-CreateOfferPlayerTable.sql @@ -0,0 +1,14 @@ +CREATE TABLE offer_players ( + id char(36) PRIMARY KEY DEFAULT uuid_generate_v4(), + game_id varchar(255) NOT NULL REFERENCES games(id), + player_id varchar(1000) NOT NULL, + offer_id varchar(255) NOT NULL REFERENCES offers(id), + claim_counter integer, + claim_timestamp timestamp WITH TIME ZONE, + view_counter integer, + view_timestamp timestamp WITH TIME ZONE, + transactions JSONB NOT NULL DEFAULT '[]'::JSONB, + impressions JSONB NOT NULL DEFAULT '[]'::JSONB +); + +CREATE UNIQUE INDEX game_id_player_id_offer_id ON offer_players (game_id, player_id, offer_id) diff --git a/migrations/migrations.go b/migrations/migrations.go index 045fbd5..c846d50 100644 --- a/migrations/migrations.go +++ b/migrations/migrations.go @@ -9,6 +9,7 @@ // migrations/0007-RemoveProductIdConstraintFromOfferInstances.sql // migrations/0008-AddCostToOffers.sql // migrations/0009-AddCostToOfferInstances.sql +// migrations/0010-CreateOfferPlayerTable.sql // DO NOT EDIT! package migrations @@ -111,7 +112,7 @@ func migrations0002CreateofferstableSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "migrations/0002-CreateOffersTable.sql", size: 692, mode: os.FileMode(420), modTime: time.Unix(1493235957, 0)} + info := bindataFileInfo{name: "migrations/0002-CreateOffersTable.sql", size: 692, mode: os.FileMode(420), modTime: time.Unix(1498737211, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -131,7 +132,7 @@ func migrations0003CreateofferintancestableSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "migrations/0003-CreateOfferIntancesTable.sql", size: 597, mode: os.FileMode(420), modTime: time.Unix(1493235957, 0)} + info := bindataFileInfo{name: "migrations/0003-CreateOfferIntancesTable.sql", size: 597, mode: os.FileMode(420), modTime: time.Unix(1518780975, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -191,7 +192,7 @@ func migrations0006RemoveproductidconstraintfromoffersSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "migrations/0006-RemoveProductIdConstraintFromOffers.sql", size: 58, mode: os.FileMode(420), modTime: time.Unix(1498652520, 0)} + info := bindataFileInfo{name: "migrations/0006-RemoveProductIdConstraintFromOffers.sql", size: 58, mode: os.FileMode(420), modTime: time.Unix(1498673123, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -211,7 +212,7 @@ func migrations0007RemoveproductidconstraintfromofferinstancesSql() (*asset, err return nil, err } - info := bindataFileInfo{name: "migrations/0007-RemoveProductIdConstraintFromOfferInstances.sql", size: 67, mode: os.FileMode(420), modTime: time.Unix(1498652614, 0)} + info := bindataFileInfo{name: "migrations/0007-RemoveProductIdConstraintFromOfferInstances.sql", size: 67, mode: os.FileMode(420), modTime: time.Unix(1498673123, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -231,7 +232,7 @@ func migrations0008AddcosttooffersSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "migrations/0008-AddCostToOffers.sql", size: 72, mode: os.FileMode(420), modTime: time.Unix(1498652529, 0)} + info := bindataFileInfo{name: "migrations/0008-AddCostToOffers.sql", size: 72, mode: os.FileMode(420), modTime: time.Unix(1498673123, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -251,7 +252,27 @@ func migrations0009AddcosttoofferinstancesSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "migrations/0009-AddCostToOfferInstances.sql", size: 81, mode: os.FileMode(420), modTime: time.Unix(1498652628, 0)} + info := bindataFileInfo{name: "migrations/0009-AddCostToOfferInstances.sql", size: 81, mode: os.FileMode(420), modTime: time.Unix(1498673123, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _migrations0010CreateofferplayertableSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x92\x5f\x6f\x82\x30\x14\xc5\xdf\xf9\x14\xf7\x4d\x48\x7c\x70\x7f\xdc\x83\x7b\x42\xbd\x66\x6c\x58\x36\x2c\xd9\xdc\xb2\x34\x0d\x5c\x5d\x13\x41\xd3\x56\x97\x7d\xfb\x25\x45\x81\x2c\x7b\xe0\x8d\xe4\x9c\xdf\xa1\x3d\xa7\xb3\x14\x43\x8e\xc0\xc3\x69\x8c\xb0\xdf\x6c\x48\x8b\xc3\x4e\xfe\x90\x36\xe0\x7b\x00\x00\xaa\x80\xfc\x4b\x6a\xff\xe6\x2e\x80\xe7\x34\x5a\x86\xe9\x1a\x9e\x70\x0d\x73\x5c\x84\x59\xcc\xe1\x78\x54\x85\xd8\x52\x45\x5a\x5a\x12\xa7\x5b\x3f\x18\x3a\x6e\x2b\x4b\x12\xaa\x80\x93\xd4\x8e\xbf\x1e\x8f\x03\x60\x09\x07\x96\xc5\x31\xa4\xb8\xc0\x14\xd9\x0c\x57\xce\x68\x7c\x55\x9c\xb9\xfa\xef\x5d\xf2\x6a\x34\x1a\xb5\x68\xed\xaa\x4f\xda\x27\xde\x39\x3b\xf9\xf9\x4e\xaa\x52\xe4\xfb\x63\x65\x49\x83\xaa\x2c\x6d\x49\x77\x25\xab\x4a\x32\x56\x96\x07\x68\xbf\x5e\x23\xfe\x00\x3c\x5a\x22\xbc\x27\x0c\x6b\xf7\x49\xd1\xf7\xff\x39\x4e\xe9\x1b\x63\xb5\xac\x8c\xcc\xad\xda\x57\x06\x1e\x57\x09\x9b\xb6\xd7\xb8\x74\x3c\xf8\xf8\x1c\x4c\x26\x4e\xac\x21\x55\x1e\x34\x19\xd3\x93\xf1\x82\x7b\xcf\x3b\x0f\x9d\xb1\xe8\x25\x43\x88\xd8\x1c\xdf\x2e\x1b\x89\xa6\x73\xd1\xf4\x9a\xb0\xbf\xaf\xe1\x6c\x1e\xb6\x0b\x0d\x9b\x19\x02\xef\x37\x00\x00\xff\xff\xa4\xfb\xad\xcc\x48\x02\x00\x00") + +func migrations0010CreateofferplayertableSqlBytes() ([]byte, error) { + return bindataRead( + _migrations0010CreateofferplayertableSql, + "migrations/0010-CreateOfferPlayerTable.sql", + ) +} + +func migrations0010CreateofferplayertableSql() (*asset, error) { + bytes, err := migrations0010CreateofferplayertableSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "migrations/0010-CreateOfferPlayerTable.sql", size: 584, mode: os.FileMode(420), modTime: time.Unix(1518801650, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -317,6 +338,7 @@ var _bindata = map[string]func() (*asset, error){ "migrations/0007-RemoveProductIdConstraintFromOfferInstances.sql": migrations0007RemoveproductidconstraintfromofferinstancesSql, "migrations/0008-AddCostToOffers.sql": migrations0008AddcosttooffersSql, "migrations/0009-AddCostToOfferInstances.sql": migrations0009AddcosttoofferinstancesSql, + "migrations/0010-CreateOfferPlayerTable.sql": migrations0010CreateofferplayertableSql, } // AssetDir returns the file names below a certain @@ -369,6 +391,7 @@ var _bintree = &bintree{nil, map[string]*bintree{ "0007-RemoveProductIdConstraintFromOfferInstances.sql": &bintree{migrations0007RemoveproductidconstraintfromofferinstancesSql, map[string]*bintree{}}, "0008-AddCostToOffers.sql": &bintree{migrations0008AddcosttooffersSql, map[string]*bintree{}}, "0009-AddCostToOfferInstances.sql": &bintree{migrations0009AddcosttoofferinstancesSql, map[string]*bintree{}}, + "0010-CreateOfferPlayerTable.sql": &bintree{migrations0010CreateofferplayertableSql, map[string]*bintree{}}, }}, }} diff --git a/models/constants.go b/models/constants.go index 03e67af..a278875 100644 --- a/models/constants.go +++ b/models/constants.go @@ -30,21 +30,3 @@ const SegmentSelect = "Database/Select" //SegmentInsect represents a segment const SegmentInsect = "Database/Insect" - -//SegmentSIsMember represents a redis op -const SegmentSIsMember = "Redis/SISMEMBER" - -//SegmentGet represents a redis op -const SegmentGet = "Redis/GET" - -//SegmentIncr represents a redis op -const SegmentIncr = "Redis/INCR" - -//SegmentSet represents a redis op -const SegmentSet = "Redis/SET" - -//SegmentSAdd represents a redis op -const SegmentSAdd = "Redis/SADD" - -//SegmentRedis represents a redis op -const SegmentRedis = "Redis" diff --git a/models/game_test.go b/models/game_test.go index b1d0b58..99cba12 100644 --- a/models/game_test.go +++ b/models/game_test.go @@ -82,7 +82,7 @@ var _ = Describe("Games Model", func() { It("Should return the full list of games", func() { games, err := models.ListGames(db, nil) Expect(err).NotTo(HaveOccurred()) - Expect(games).To(HaveLen(9)) + Expect(games).To(HaveLen(13)) }) }) diff --git a/models/helpers.go b/models/helpers.go index 40cdd03..53d7559 100644 --- a/models/helpers.go +++ b/models/helpers.go @@ -38,36 +38,6 @@ type OfferImpressionPayload struct { ImpressionID string `json:"impressionId" valid:"uuidv4,required"` } -//GetTransactionsKey returns the key of the player's purchase transactions in redis -func GetTransactionsKey(gameID, playerID string) string { - return fmt.Sprintf("transactions:%s:%s", gameID, playerID) -} - -//GetClaimCounterKey returns the key of the player's claim counter -func GetClaimCounterKey(playerID, offerID string) string { - return fmt.Sprintf("claim:counter:%s:%s", playerID, offerID) -} - -//GetClaimTimestampKey returns the key of the player's last claim timestamp -func GetClaimTimestampKey(playerID, offerID string) string { - return fmt.Sprintf("claim:timestamp:%s:%s", playerID, offerID) -} - -//GetImpressionsKey returns the key of the player's impressions in redis -func GetImpressionsKey(playerID, gameID string) string { - return fmt.Sprintf("impressions:%s:%s", gameID, playerID) -} - -//GetViewCounterKey returns the key of the player's impressions counter -func GetViewCounterKey(playerID, offerID string) string { - return fmt.Sprintf("view:counter:%s:%s", playerID, offerID) -} - -//GetViewTimestampKey returns the key of the player's last impression timestamp -func GetViewTimestampKey(playerID, offerID string) string { - return fmt.Sprintf("view:timestamp:%s:%s", playerID, offerID) -} - //GetEnabledOffersKey returns the key of the current enabled offers func GetEnabledOffersKey(gameID string) string { return fmt.Sprintf("offers:enabled:%s", gameID) diff --git a/models/mixed_metrics_reporter.go b/models/mixed_metrics_reporter.go index ecdc503..ef3133d 100644 --- a/models/mixed_metrics_reporter.go +++ b/models/mixed_metrics_reporter.go @@ -48,20 +48,6 @@ func (m *MixedMetricsReporter) WithDatastoreSegment(table, operation string, f f return f() } -//WithRedisSegment with redis segment -func (m *MixedMetricsReporter) WithRedisSegment(operation string, f func() error) error { - if m == nil { - return f() - } - - for _, mr := range m.MetricsReporters { - data := mr.StartDatastoreSegment("Redis", "redis", operation) - defer mr.EndDatastoreSegment(data) - } - - return f() -} - //WithExternalSegment that calls all the other metrics reporters func (m *MixedMetricsReporter) WithExternalSegment(url string, f func() error) error { if m == nil { diff --git a/models/mixed_metrics_reporter_test.go b/models/mixed_metrics_reporter_test.go index 6d73d88..38184d5 100644 --- a/models/mixed_metrics_reporter_test.go +++ b/models/mixed_metrics_reporter_test.go @@ -92,32 +92,6 @@ var _ = Describe("Mixed Metrics Reporter Model", func() { }) }) - Describe("WithRedisSegment", func() { - It("Should execute the given function if mr is nil", func() { - var mixedMetricsReporter *models.MixedMetricsReporter - f := func() error { - sendToChan(testChan) - return nil - } - err := mixedMetricsReporter.WithRedisSegment("op", f) - Expect(err).NotTo(HaveOccurred()) - Eventually(testChan).Should(Receive()) - }) - - It("Should execute StartSegment and EndSegment and the given function if mr is not nil", func() { - mr := testing.FakeMetricsReporter{} - mixedMetricsReporter := models.NewMixedMetricsReporter() - mixedMetricsReporter.AddReporter(mr) - f := func() error { - sendToChan(testChan) - return nil - } - err := mixedMetricsReporter.WithRedisSegment("op", f) - Expect(err).NotTo(HaveOccurred()) - Eventually(testChan).Should(Receive()) - }) - }) - Describe("WithExternalSegment", func() { It("Should execute the given function if mr is nil", func() { var mixedMetricsReporter *models.MixedMetricsReporter diff --git a/models/models_suite_test.go b/models/models_suite_test.go index 73c4f39..b508b7d 100644 --- a/models/models_suite_test.go +++ b/models/models_suite_test.go @@ -8,21 +8,20 @@ package models_test import ( + "time" + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" runner "gopkg.in/mgutz/dat.v2/sqlx-runner" - "time" "testing" "github.com/pmylund/go-cache" oTesting "github.com/topfreegames/offers/testing" - "github.com/topfreegames/offers/util" ) var conn runner.Connection var db *runner.Tx -var redisClient *util.RedisClient var offersCache *cache.Cache func TestApi(t *testing.T) { @@ -38,9 +37,6 @@ var _ = BeforeSuite(func() { err = oTesting.LoadFixtures(conn) Expect(err).NotTo(HaveOccurred()) - redisClient, err = oTesting.GetTestRedis() - Expect(err).NotTo(HaveOccurred()) - offersCache = cache.New(300*time.Second, 30*time.Second) }) @@ -55,8 +51,6 @@ var _ = AfterEach(func() { err := db.Rollback() Expect(err).NotTo(HaveOccurred()) db = nil - status := redisClient.Client.FlushAll() - Expect(status.Err()).NotTo(HaveOccurred()) }) var _ = AfterSuite(func() { diff --git a/models/offer_instance.go b/models/offer_instance.go index 5676780..1f001ea 100644 --- a/models/offer_instance.go +++ b/models/offer_instance.go @@ -14,10 +14,8 @@ import ( "time" "github.com/pmylund/go-cache" - "github.com/topfreegames/offers/util" "gopkg.in/mgutz/dat.v2/dat" runner "gopkg.in/mgutz/dat.v2/sqlx-runner" - redis "gopkg.in/redis.v5" ) //OfferInstance represents a tenant in offers API it cannot be updated, only inserted @@ -143,7 +141,6 @@ func getClaimedOfferNextAt( //ClaimOffer claims the offer func ClaimOffer( db runner.Connection, - redisClient *util.RedisClient, gameID, offerInstanceID, playerID, productID, transactionID string, timestamp int64, t time.Time, @@ -152,9 +149,8 @@ func ClaimOffer( // If an offer instance id is sent var offerInstance *OfferInstance + var previousOfferPlayer bool var err error - var isReplay bool - var claimCount, claimTimestamp int64 var nextAt int64 if offerInstanceID != "" { @@ -168,70 +164,67 @@ func ClaimOffer( return nil, false, 0, err } } + offerPlayer, err := GetOfferPlayer(db, gameID, playerID, offerInstance.OfferID, mr) + if err == nil { + previousOfferPlayer = true + } else if !IsNoRowsInResultSetError(err) { + return nil, false, 0, err + } else { + offerPlayer = &OfferPlayer{ + GameID: gameID, + PlayerID: playerID, + OfferID: offerInstance.OfferID, + Transactions: dat.JSON([]byte(`[]`)), + Impressions: dat.JSON([]byte(`[]`)), + } + } - transactionsKey := GetTransactionsKey(playerID, gameID) - claimCounterKey := GetClaimCounterKey(playerID, offerInstance.OfferID) - claimTimestampKey := GetClaimTimestampKey(playerID, offerInstance.OfferID) + isReplay := false + var transactions []string + err = offerPlayer.Transactions.Unmarshal(&transactions) - err = mr.WithRedisSegment(SegmentSIsMember, func() error { - isReplay, err = redisClient.Client.SIsMember(transactionsKey, transactionID).Result() - return err - }) if err != nil { return nil, false, 0, err } - + for _, tr := range transactions { + if transactionID == tr { + isReplay = true + break + } + } if isReplay { - pipe := redisClient.Client.TxPipeline() - - claimCountGetOp := pipe.Get(claimCounterKey) - claimTimestampGetOp := pipe.Get(claimTimestampKey) - - err = mr.WithRedisSegment(SegmentGet, func() error { - _, err = pipe.Exec() - return err - }) + nextAt, err = getClaimedOfferNextAt( + db, gameID, offerInstance.OfferID, + offerPlayer.ClaimCounter, offerPlayer.ClaimTimestamp.Time, mr) if err != nil { return nil, false, 0, err } + return offerInstance.Contents, true, nextAt, nil + } - claimCount, err = claimCountGetOp.Int64() + if previousOfferPlayer { + jsonTr, err := dat.NewJSON(append(transactions, transactionID)) if err != nil { return nil, false, 0, err } - - claimTimestamp, err = claimTimestampGetOp.Int64() + offerPlayer.Transactions = *jsonTr + err = ClaimOfferPlayer(db, offerPlayer, time.Unix(timestamp, 0), mr) if err != nil { return nil, false, 0, err } - - nextAt, err = getClaimedOfferNextAt(db, gameID, offerInstance.OfferID, int(claimCount), time.Unix(claimTimestamp, 0), mr) + } else { + offerPlayer.ClaimCounter = 1 + offerPlayer.ClaimTimestamp = dat.NullTimeFrom(time.Unix(timestamp, 0)) + offerPlayer.Transactions = dat.JSON([]byte(fmt.Sprintf(`["%s"]`, transactionID))) + err = CreateOfferPlayer(db, offerPlayer, mr) if err != nil { return nil, false, 0, err } - return offerInstance.Contents, true, nextAt, nil - } - - pipe := redisClient.Client.TxPipeline() - - claimCountIncrOp := pipe.Incr(claimCounterKey) - pipe.Set(claimTimestampKey, timestamp, 0) - pipe.SAdd(transactionsKey, transactionID) - - err = mr.WithRedisSegment(SegmentRedis, func() error { - _, err = pipe.Exec() - return err - }) - if err != nil { - return nil, false, 0, err } - claimCount, err = claimCountIncrOp.Result() - if err != nil { - return nil, false, 0, err - } - - nextAt, err = getClaimedOfferNextAt(db, gameID, offerInstance.OfferID, int(claimCount), time.Unix(timestamp, 0), mr) + nextAt, err = getClaimedOfferNextAt( + db, gameID, offerInstance.OfferID, + offerPlayer.ClaimCounter, time.Unix(timestamp, 0), mr) if err != nil { return nil, false, 0, err } @@ -289,11 +282,12 @@ func getViewedOfferNextAt( //ViewOffer views the offer func ViewOffer( db runner.Connection, - redisClient *util.RedisClient, gameID, offerInstanceID, playerID, impressionID string, t time.Time, mr *MixedMetricsReporter, ) (bool, int64, error) { + var nextAt int64 + var previousOfferPlayer bool offerInstance, err := GetOfferInstanceAndOfferEnabled(db, gameID, offerInstanceID, mr) if err != nil { @@ -305,57 +299,63 @@ func ViewOffer( return false, 0, nil } - impressionsKey := GetImpressionsKey(playerID, gameID) - viewCounterKey := GetViewCounterKey(playerID, offerInstance.OfferID) - viewTimestampKey := GetViewTimestampKey(playerID, offerInstance.OfferID) + offerPlayer, err := GetOfferPlayer(db, gameID, playerID, offerInstance.OfferID, mr) + if err == nil { + previousOfferPlayer = true + } else if !IsNoRowsInResultSetError(err) { + return false, 0, err + } else { + offerPlayer = &OfferPlayer{ + GameID: gameID, + PlayerID: playerID, + OfferID: offerInstance.OfferID, + Transactions: dat.JSON([]byte(`[]`)), + Impressions: dat.JSON([]byte(`[]`)), + } + } - var isReplay bool - var viewCount int64 - var nextAt int64 - err = mr.WithRedisSegment(SegmentSIsMember, func() error { - isReplay, err = redisClient.Client.SIsMember(impressionsKey, impressionID).Result() - return err - }) + isReplay := false + var impressions []string + err = offerPlayer.Impressions.Unmarshal(&impressions) if err != nil { return false, 0, err } + for _, imp := range impressions { + if impressionID == imp { + isReplay = true + break + } + } if isReplay { - err = mr.WithRedisSegment(SegmentGet, func() error { - viewCount, err = redisClient.Client.Get(viewCounterKey).Int64() - return err - }) + nextAt, err = getViewedOfferNextAt(db, gameID, offerInstance.OfferID, offerPlayer.ViewCounter, t, mr) if err != nil { return false, 0, err } + return true, nextAt, nil + } - nextAt, err = getViewedOfferNextAt(db, gameID, offerInstance.OfferID, int(viewCount), t, mr) + if previousOfferPlayer { + jsonTr, err := dat.NewJSON(append(impressions, impressionID)) + if err != nil { + return false, 0, err + } + offerPlayer.Impressions = *jsonTr + err = ViewOfferPlayer(db, offerPlayer, t, mr) + if err != nil { + return false, 0, err + } + } else { + offerPlayer.ViewCounter = 1 + offerPlayer.ViewTimestamp = dat.NullTimeFrom(t) + offerPlayer.Impressions = dat.JSON([]byte(fmt.Sprintf(`["%s"]`, impressionID))) + err = CreateOfferPlayer(db, offerPlayer, mr) if err != nil { return false, 0, err } - return true, nextAt, nil - } - - pipe := redisClient.Client.TxPipeline() - - viewCounterIncrOp := pipe.Incr(viewCounterKey) - pipe.Set(viewTimestampKey, t.Unix(), 0) - pipe.SAdd(impressionsKey, impressionID) - - err = mr.WithRedisSegment(SegmentRedis, func() error { - _, err = pipe.Exec() - return err - }) - if err != nil { - return false, 0, err - } - - viewCount, err = viewCounterIncrOp.Result() - if err != nil { - return false, 0, err } - nextAt, err = getViewedOfferNextAt(db, gameID, offerInstance.OfferID, int(viewCount), t, mr) + nextAt, err = getViewedOfferNextAt(db, gameID, offerInstance.OfferID, offerPlayer.ViewCounter, t, mr) if err != nil { return false, 0, err } @@ -365,7 +365,6 @@ func ViewOffer( //GetAvailableOffers returns the offers that match the criteria of enabled offer templates func GetAvailableOffers( db runner.Connection, - redisClient *util.RedisClient, offersCache *cache.Cache, gameID, playerID string, t time.Time, @@ -401,7 +400,12 @@ func GetAvailableOffers( return offersByPlacement, nil } - filteredOffers, err = filterOffersByFrequencyAndPeriod(redisClient, playerID, filteredOffers, t, mr) + offersByPlayer, err := GetOffersByPlayer(db, gameID, playerID, mr) + if err != nil { + return nil, err + } + + filteredOffers, err = filterOffersByFrequencyAndPeriod(playerID, filteredOffers, offersByPlayer, t, mr) if err != nil { return nil, err } @@ -511,12 +515,16 @@ func filterTemplatesByTrigger(trigger Trigger, offers []*Offer, t time.Time) ([] } func filterOffersByFrequencyAndPeriod( - redisClient *util.RedisClient, playerID string, offers []*Offer, + playerOffers []*OfferPlayer, t time.Time, mr *MixedMetricsReporter, ) ([]*Offer, error) { + playerOffersByOfferID := map[string]*OfferPlayer{} + for _, playerOffer := range playerOffers { + playerOffersByOfferID[playerOffer.OfferID] = playerOffer + } var err error var filteredOffers []*Offer for _, offer := range offers { @@ -531,56 +539,12 @@ func filterOffersByFrequencyAndPeriod( return nil, err } - claimCounterKey := GetClaimCounterKey(playerID, offer.ID) - claimTimestampKey := GetClaimTimestampKey(playerID, offer.ID) - viewCounterKey := GetViewCounterKey(playerID, offer.ID) - viewTimestampKey := GetViewTimestampKey(playerID, offer.ID) - - var claimCounter int64 - var claimTimestamp int64 - var viewCounter int64 - var viewTimestamp int64 - - pipe := redisClient.Client.TxPipeline() - claimCounterGetOp := pipe.Get(claimCounterKey) - claimTimestampGetOp := pipe.Get(claimTimestampKey) - viewCounterGetOp := pipe.Get(viewCounterKey) - viewTimestampGetOp := pipe.Get(viewTimestampKey) - - err = mr.WithRedisSegment(SegmentRedis, func() error { - _, err = pipe.Exec() - return err - }) - // If err == redis.Nil, then Get didn't found claimCounter fot that key - // Either player doesn't exist, or it was never inserted yet. - // Since claimCounter already is 0 and this key will be crated in Incr, just go on. - if err != nil && err != redis.Nil { - return nil, err - } - - claimCounter, err = claimCounterGetOp.Int64() - if err != nil && err != redis.Nil { - return nil, err - } - - claimTimestamp, err = claimTimestampGetOp.Int64() - if err != nil && err != redis.Nil { - return nil, err - } - lastClaimAt := time.Unix(claimTimestamp, 0) - - viewCounter, err = viewCounterGetOp.Int64() - if err != nil && err != redis.Nil { - return nil, err - } - - viewTimestamp, err = viewTimestampGetOp.Int64() - if err != nil && err != redis.Nil { - return nil, err + offerPlayer := &OfferPlayer{} + if val, ok := playerOffersByOfferID[offer.ID]; ok { + offerPlayer = val } - lastViewAt := time.Unix(viewTimestamp, 0) - if f.Max != 0 && int(viewCounter) >= f.Max { + if f.Max != 0 && offerPlayer.ViewCounter >= f.Max { continue } if f.Every != "" { @@ -588,11 +552,11 @@ func filterOffersByFrequencyAndPeriod( if err != nil { return nil, err } - if lastViewAt.Add(duration).After(t) { + if offerPlayer.ViewTimestamp.Time.Add(duration).After(t) { continue } } - if p.Max != 0 && int(claimCounter) >= p.Max { + if p.Max != 0 && offerPlayer.ClaimCounter >= p.Max { continue } if p.Every != "" { @@ -600,7 +564,7 @@ func filterOffersByFrequencyAndPeriod( if err != nil { return nil, err } - if lastClaimAt.Add(duration).After(t) { + if offerPlayer.ClaimTimestamp.Time.Add(duration).After(t) { continue } } @@ -637,7 +601,6 @@ func getOfferToReturn( //GetOfferInfo returns the offers that match the criteria of enabled offer templates func GetOfferInfo( db runner.Connection, - redisClient *util.RedisClient, gameID, playerID, offerInstanceID string, expireDuration time.Duration, mr *MixedMetricsReporter, diff --git a/models/offer_instance_test.go b/models/offer_instance_test.go index a677d79..bdfcc14 100644 --- a/models/offer_instance_test.go +++ b/models/offer_instance_test.go @@ -85,11 +85,8 @@ var _ = Describe("Offer Instance Model", func() { playerID := "player-1" transactionID := uuid.NewV4().String() - claimCounterKey := models.GetClaimCounterKey(playerID, offerID) - claimTimestampKey := models.GetClaimTimestampKey(playerID, offerID) - //When - contents, alreadyClaimed, nextAt, err := models.ClaimOffer(db, redisClient, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) + contents, alreadyClaimed, nextAt, err := models.ClaimOffer(db, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) //Then Expect(err).NotTo(HaveOccurred()) @@ -97,13 +94,10 @@ var _ = Describe("Offer Instance Model", func() { Expect(alreadyClaimed).To(BeFalse()) Expect(nextAt).To(Equal(currentTime.Unix() + 1)) - claimCounter, err := redisClient.Client.Get(claimCounterKey).Int64() - Expect(err).NotTo(HaveOccurred()) - claimTimestamp, err := redisClient.Client.Get(claimTimestampKey).Int64() + offerPlayer, err := models.GetOfferPlayer(db, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) - - Expect(claimCounter).To(Equal(int64(1))) - Expect(claimTimestamp).To(Equal(currentTime.Unix())) + Expect(offerPlayer.ClaimCounter).To(Equal(1)) + Expect(offerPlayer.ClaimTimestamp.Time.Unix()).To(Equal(currentTime.Unix())) }) It("should claim valid offer without the offer instance id", func() { @@ -115,11 +109,8 @@ var _ = Describe("Offer Instance Model", func() { playerID := "player-1" transactionID := uuid.NewV4().String() - claimCounterKey := models.GetClaimCounterKey(playerID, offerID) - claimTimestampKey := models.GetClaimTimestampKey(playerID, offerID) - //When - contents, alreadyClaimed, nextAt, err := models.ClaimOffer(db, redisClient, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) + contents, alreadyClaimed, nextAt, err := models.ClaimOffer(db, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) //Then Expect(err).NotTo(HaveOccurred()) @@ -127,13 +118,10 @@ var _ = Describe("Offer Instance Model", func() { Expect(alreadyClaimed).To(BeFalse()) Expect(nextAt).To(Equal(currentTime.Unix() + 1)) - claimCounter, err := redisClient.Client.Get(claimCounterKey).Int64() + offerPlayer, err := models.GetOfferPlayer(db, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) - claimTimestamp, err := redisClient.Client.Get(claimTimestampKey).Int64() - Expect(err).NotTo(HaveOccurred()) - - Expect(claimCounter).To(Equal(int64(1))) - Expect(claimTimestamp).To(Equal(currentTime.Unix())) + Expect(offerPlayer.ClaimCounter).To(Equal(1)) + Expect(offerPlayer.ClaimTimestamp.Time.Unix()).To(Equal(currentTime.Unix())) }) It("should claim valid offer before trigger begins", func() { @@ -145,11 +133,8 @@ var _ = Describe("Offer Instance Model", func() { playerID := "player-1" transactionID := uuid.NewV4().String() - claimCounterKey := models.GetClaimCounterKey(playerID, offerID) - claimTimestampKey := models.GetClaimTimestampKey(playerID, offerID) - //When - contents, alreadyClaimed, nextAt, err := models.ClaimOffer(db, redisClient, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) + contents, alreadyClaimed, nextAt, err := models.ClaimOffer(db, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) //Then Expect(contents).NotTo(BeNil()) @@ -157,13 +142,10 @@ var _ = Describe("Offer Instance Model", func() { Expect(err).NotTo(HaveOccurred()) Expect(nextAt).To(Equal(currentTime.Unix() + 1)) - claimCounter, err := redisClient.Client.Get(claimCounterKey).Int64() + offerPlayer, err := models.GetOfferPlayer(db, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) - claimTimestamp, err := redisClient.Client.Get(claimTimestampKey).Int64() - Expect(err).NotTo(HaveOccurred()) - - Expect(claimCounter).To(Equal(int64(1))) - Expect(claimTimestamp).To(Equal(currentTime.Unix())) + Expect(offerPlayer.ClaimCounter).To(Equal(1)) + Expect(offerPlayer.ClaimTimestamp.Time.Unix()).To(Equal(currentTime.Unix())) }) It("should claim valid offer after trigger begins", func() { @@ -175,11 +157,8 @@ var _ = Describe("Offer Instance Model", func() { playerID := "player-1" transactionID := uuid.NewV4().String() - claimCounterKey := models.GetClaimCounterKey(playerID, offerID) - claimTimestampKey := models.GetClaimTimestampKey(playerID, offerID) - //When - contents, alreadyClaimed, nextAt, err := models.ClaimOffer(db, redisClient, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) + contents, alreadyClaimed, nextAt, err := models.ClaimOffer(db, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) //Then Expect(contents).NotTo(BeNil()) @@ -187,13 +166,10 @@ var _ = Describe("Offer Instance Model", func() { Expect(err).NotTo(HaveOccurred()) Expect(nextAt).To(Equal(currentTime.Unix() + 1)) - claimCounter, err := redisClient.Client.Get(claimCounterKey).Int64() + offerPlayer, err := models.GetOfferPlayer(db, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) - claimTimestamp, err := redisClient.Client.Get(claimTimestampKey).Int64() - Expect(err).NotTo(HaveOccurred()) - - Expect(claimCounter).To(Equal(int64(1))) - Expect(claimTimestamp).To(Equal(currentTime.Unix())) + Expect(offerPlayer.ClaimCounter).To(Equal(1)) + Expect(offerPlayer.ClaimTimestamp.Time.Unix()).To(Equal(currentTime.Unix())) }) It("should claim and receive 0 nextAt if reached max purchases", func() { @@ -205,11 +181,8 @@ var _ = Describe("Offer Instance Model", func() { playerID := "player-1" transactionID := uuid.NewV4().String() - claimCounterKey := models.GetClaimCounterKey(playerID, offerID) - claimTimestampKey := models.GetClaimTimestampKey(playerID, offerID) - //When - contents, alreadyClaimed, nextAt, err := models.ClaimOffer(db, redisClient, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) + contents, alreadyClaimed, nextAt, err := models.ClaimOffer(db, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) //Then Expect(contents).NotTo(BeNil()) @@ -217,13 +190,10 @@ var _ = Describe("Offer Instance Model", func() { Expect(nextAt).To(Equal(int64(0))) Expect(err).NotTo(HaveOccurred()) - claimCounter, err := redisClient.Client.Get(claimCounterKey).Int64() + offerPlayer, err := models.GetOfferPlayer(db, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) - claimTimestamp, err := redisClient.Client.Get(claimTimestampKey).Int64() - Expect(err).NotTo(HaveOccurred()) - - Expect(claimCounter).To(Equal(int64(1))) - Expect(claimTimestamp).To(Equal(currentTime.Unix())) + Expect(offerPlayer.ClaimCounter).To(Equal(1)) + Expect(offerPlayer.ClaimTimestamp.Time.Unix()).To(Equal(currentTime.Unix())) }) It("should claim and receive 0 nextAt if offer was disabled", func() { @@ -235,11 +205,8 @@ var _ = Describe("Offer Instance Model", func() { playerID := "player-14" transactionID := uuid.NewV4().String() - claimCounterKey := models.GetClaimCounterKey(playerID, offerID) - claimTimestampKey := models.GetClaimTimestampKey(playerID, offerID) - //When - contents, alreadyClaimed, nextAt, err := models.ClaimOffer(db, redisClient, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) + contents, alreadyClaimed, nextAt, err := models.ClaimOffer(db, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) //Then Expect(contents).NotTo(BeNil()) @@ -247,13 +214,10 @@ var _ = Describe("Offer Instance Model", func() { Expect(nextAt).To(Equal(int64(0))) Expect(err).NotTo(HaveOccurred()) - claimCounter, err := redisClient.Client.Get(claimCounterKey).Int64() - Expect(err).NotTo(HaveOccurred()) - claimTimestamp, err := redisClient.Client.Get(claimTimestampKey).Int64() + offerPlayer, err := models.GetOfferPlayer(db, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) - - Expect(claimCounter).To(Equal(int64(1))) - Expect(claimTimestamp).To(Equal(currentTime.Unix())) + Expect(offerPlayer.ClaimCounter).To(Equal(1)) + Expect(offerPlayer.ClaimTimestamp.Time.Unix()).To(Equal(currentTime.Unix())) }) It("should claim and receive nextAt equal to currentTime if no every frequency or period", func() { @@ -265,11 +229,8 @@ var _ = Describe("Offer Instance Model", func() { playerID := "player-11" transactionID := uuid.NewV4().String() - claimCounterKey := models.GetClaimCounterKey(playerID, offerID) - claimTimestampKey := models.GetClaimTimestampKey(playerID, offerID) - //When - contents, alreadyClaimed, nextAt, err := models.ClaimOffer(db, redisClient, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) + contents, alreadyClaimed, nextAt, err := models.ClaimOffer(db, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) //Then Expect(contents).NotTo(BeNil()) @@ -277,13 +238,10 @@ var _ = Describe("Offer Instance Model", func() { Expect(nextAt).To(Equal(currentTime.Unix())) Expect(err).NotTo(HaveOccurred()) - claimCounter, err := redisClient.Client.Get(claimCounterKey).Int64() - Expect(err).NotTo(HaveOccurred()) - claimTimestamp, err := redisClient.Client.Get(claimTimestampKey).Int64() + offerPlayer, err := models.GetOfferPlayer(db, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) - - Expect(claimCounter).To(Equal(int64(1))) - Expect(claimTimestamp).To(Equal(currentTime.Unix())) + Expect(offerPlayer.ClaimCounter).To(Equal(1)) + Expect(offerPlayer.ClaimTimestamp.Time.Unix()).To(Equal(currentTime.Unix())) }) It("should claim and receive biggest nextAt considering period and freq", func() { @@ -295,11 +253,8 @@ var _ = Describe("Offer Instance Model", func() { playerID := "player-11" transactionID := uuid.NewV4().String() - claimCounterKey := models.GetClaimCounterKey(playerID, offerID) - claimTimestampKey := models.GetClaimTimestampKey(playerID, offerID) - //When - contents, alreadyClaimed, nextAt, err := models.ClaimOffer(db, redisClient, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) + contents, alreadyClaimed, nextAt, err := models.ClaimOffer(db, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) //Then Expect(contents).NotTo(BeNil()) @@ -307,13 +262,10 @@ var _ = Describe("Offer Instance Model", func() { Expect(nextAt).To(Equal(currentTime.Unix() + 30)) Expect(err).NotTo(HaveOccurred()) - claimCounter, err := redisClient.Client.Get(claimCounterKey).Int64() - Expect(err).NotTo(HaveOccurred()) - claimTimestamp, err := redisClient.Client.Get(claimTimestampKey).Int64() + offerPlayer, err := models.GetOfferPlayer(db, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) - - Expect(claimCounter).To(Equal(int64(1))) - Expect(claimTimestamp).To(Equal(currentTime.Unix())) + Expect(offerPlayer.ClaimCounter).To(Equal(1)) + Expect(offerPlayer.ClaimTimestamp.Time.Unix()).To(Equal(currentTime.Unix())) }) It("should not claim twice the same offer", func() { @@ -327,13 +279,10 @@ var _ = Describe("Offer Instance Model", func() { firstTime := time.Unix(to+500, 0) secondTime := time.Unix(to+1000, 0) - claimCounterKey := models.GetClaimCounterKey(playerID, offerID) - claimTimestampKey := models.GetClaimTimestampKey(playerID, offerID) - //When - contents1, alreadyClaimed1, nextAt1, err := models.ClaimOffer(db, redisClient, gameID, id, playerID, defaultProductID, transactionID, firstTime.Unix(), firstTime, nil) + contents1, alreadyClaimed1, nextAt1, err := models.ClaimOffer(db, gameID, id, playerID, defaultProductID, transactionID, firstTime.Unix(), firstTime, nil) Expect(err).NotTo(HaveOccurred()) - contents2, alreadyClaimed2, nextAt2, err := models.ClaimOffer(db, redisClient, gameID, id, playerID, defaultProductID, transactionID, secondTime.Unix(), secondTime, nil) + contents2, alreadyClaimed2, nextAt2, err := models.ClaimOffer(db, gameID, id, playerID, defaultProductID, transactionID, secondTime.Unix(), secondTime, nil) Expect(err).NotTo(HaveOccurred()) //Then @@ -345,13 +294,10 @@ var _ = Describe("Offer Instance Model", func() { Expect(alreadyClaimed2).To(BeTrue()) Expect(nextAt2).To(Equal(int64(to + 501))) - claimCounter, err := redisClient.Client.Get(claimCounterKey).Int64() - Expect(err).NotTo(HaveOccurred()) - claimTimestamp, err := redisClient.Client.Get(claimTimestampKey).Int64() + offerPlayer, err := models.GetOfferPlayer(db, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) - - Expect(claimCounter).To(Equal(int64(1))) - Expect(claimTimestamp).To(Equal(firstTime.Unix())) + Expect(offerPlayer.ClaimCounter).To(Equal(1)) + Expect(offerPlayer.ClaimTimestamp.Time.Unix()).To(Equal(firstTime.Unix())) }) It("should claim twice the same offer if different transactionIDs", func() { @@ -366,13 +312,10 @@ var _ = Describe("Offer Instance Model", func() { firstTime := time.Unix(to+500, 0) secondTime := time.Unix(to+1000, 0) - claimCounterKey := models.GetClaimCounterKey(playerID, offerID) - claimTimestampKey := models.GetClaimTimestampKey(playerID, offerID) - //When - contents1, alreadyClaimed1, nextAt1, err := models.ClaimOffer(db, redisClient, gameID, id, playerID, defaultProductID, transactionID1, firstTime.Unix(), firstTime, nil) + contents1, alreadyClaimed1, nextAt1, err := models.ClaimOffer(db, gameID, id, playerID, defaultProductID, transactionID1, firstTime.Unix(), firstTime, nil) Expect(err).NotTo(HaveOccurred()) - contents2, alreadyClaimed2, nextAt2, err := models.ClaimOffer(db, redisClient, gameID, id, playerID, defaultProductID, transactionID2, secondTime.Unix(), secondTime, nil) + contents2, alreadyClaimed2, nextAt2, err := models.ClaimOffer(db, gameID, id, playerID, defaultProductID, transactionID2, secondTime.Unix(), secondTime, nil) Expect(err).NotTo(HaveOccurred()) //Then @@ -384,13 +327,10 @@ var _ = Describe("Offer Instance Model", func() { Expect(alreadyClaimed2).To(BeFalse()) Expect(nextAt2).To(Equal(secondTime.Unix() + 1)) - claimCounter, err := redisClient.Client.Get(claimCounterKey).Int64() + offerPlayer, err := models.GetOfferPlayer(db, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) - claimTimestamp, err := redisClient.Client.Get(claimTimestampKey).Int64() - Expect(err).NotTo(HaveOccurred()) - - Expect(claimCounter).To(Equal(int64(2))) - Expect(claimTimestamp).To(Equal(secondTime.Unix())) + Expect(offerPlayer.ClaimCounter).To(Equal(2)) + Expect(offerPlayer.ClaimTimestamp.Time.Unix()).To(Equal(secondTime.Unix())) }) It("should not claim an offer that doesn't exist", func() { @@ -401,7 +341,7 @@ var _ = Describe("Offer Instance Model", func() { playerID := "player-1" transactionID := uuid.NewV4().String() //When - _, _, _, err := models.ClaimOffer(db, redisClient, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) + _, _, _, err := models.ClaimOffer(db, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) //Then Expect(err).To(HaveOccurred()) @@ -423,7 +363,7 @@ var _ = Describe("Offer Instance Model", func() { Expect(err).NotTo(HaveOccurred()) db.(*runner.DB).DB.Close() // make DB connection unavailable - _, _, _, err = models.ClaimOffer(db, redisClient, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) + _, _, _, err = models.ClaimOffer(db, gameID, id, playerID, defaultProductID, transactionID, currentTime.Unix(), currentTime, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("sql: database is closed")) }) @@ -470,23 +410,17 @@ var _ = Describe("Offer Instance Model", func() { impressionID := uuid.NewV4().String() currentTime := time.Now() - viewCounterKey := models.GetViewCounterKey(playerID, offerID) - viewTimestampKey := models.GetViewTimestampKey(playerID, offerID) - //When - isReplay, nextAt, err := models.ViewOffer(db, redisClient, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) + isReplay, nextAt, err := models.ViewOffer(db, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) Expect(isReplay).To(BeFalse()) Expect(err).NotTo(HaveOccurred()) - viewCounter, err := redisClient.Client.Get(viewCounterKey).Int64() - Expect(err).NotTo(HaveOccurred()) - viewTimestamp, err := redisClient.Client.Get(viewTimestampKey).Int64() - Expect(err).NotTo(HaveOccurred()) //Then + offerPlayer, err := models.GetOfferPlayer(db, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) + Expect(offerPlayer.ViewCounter).To(Equal(1)) + Expect(offerPlayer.ViewTimestamp.Time.Unix()).To(Equal(currentTime.Unix())) Expect(nextAt).To(Equal(currentTime.Unix() + 1)) - Expect(viewCounter).To(Equal(int64(1))) - Expect(viewTimestamp).To(Equal(currentTime.Unix())) }) It("should return 0 nextAt if offer reached max period", func() { @@ -498,7 +432,7 @@ var _ = Describe("Offer Instance Model", func() { impressionID := uuid.NewV4().String() //When - isReplay, nextAt, err := models.ViewOffer(db, redisClient, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) + isReplay, nextAt, err := models.ViewOffer(db, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) Expect(isReplay).To(BeFalse()) //Then @@ -515,7 +449,7 @@ var _ = Describe("Offer Instance Model", func() { impressionID := uuid.NewV4().String() //When - isReplay, nextAt, err := models.ViewOffer(db, redisClient, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) + isReplay, nextAt, err := models.ViewOffer(db, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) Expect(isReplay).To(BeFalse()) //Then @@ -532,21 +466,16 @@ var _ = Describe("Offer Instance Model", func() { offerID := "5fed76ab-1fd7-4a91-972d-bca228ce80c4" impressionID := uuid.NewV4().String() - viewCounterKey := models.GetViewCounterKey(playerID, offerID) - viewTimestampKey := models.GetViewTimestampKey(playerID, offerID) - //When - isReplay, nextAt, err := models.ViewOffer(db, redisClient, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) + isReplay, nextAt, err := models.ViewOffer(db, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) Expect(isReplay).To(BeFalse()) Expect(err).NotTo(HaveOccurred()) - viewCounter, err := redisClient.Client.Get(viewCounterKey).Int64() - Expect(err).NotTo(HaveOccurred()) - viewTimestamp, err := redisClient.Client.Get(viewTimestampKey).Int64() - Expect(err).NotTo(HaveOccurred()) //Then - Expect(viewCounter).To(Equal(int64(1))) - Expect(viewTimestamp).To(Equal(currentTime.Unix())) + offerPlayer, err := models.GetOfferPlayer(db, gameID, playerID, offerID, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(offerPlayer.ViewCounter).To(Equal(1)) + Expect(offerPlayer.ViewTimestamp.Time.Unix()).To(Equal(currentTime.Unix())) Expect(nextAt).To(Equal(currentTime.Unix())) }) @@ -559,7 +488,7 @@ var _ = Describe("Offer Instance Model", func() { currentTime := time.Now() //When - isReplay, nextAt, err := models.ViewOffer(db, redisClient, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) + isReplay, nextAt, err := models.ViewOffer(db, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) Expect(isReplay).To(BeFalse()) //Then @@ -577,7 +506,7 @@ var _ = Describe("Offer Instance Model", func() { currentTime := time.Now() //When - isReplay, nextAt, err := models.ViewOffer(db, redisClient, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) + isReplay, nextAt, err := models.ViewOffer(db, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) Expect(isReplay).To(BeFalse()) //Then @@ -595,7 +524,7 @@ var _ = Describe("Offer Instance Model", func() { currentTime := time.Now() //When - isReplay, nextAt, err := models.ViewOffer(db, redisClient, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) + isReplay, nextAt, err := models.ViewOffer(db, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) Expect(isReplay).To(BeFalse()) //Then @@ -613,26 +542,20 @@ var _ = Describe("Offer Instance Model", func() { impressionID := uuid.NewV4().String() currentTime := time.Now() - viewCounterKey := models.GetViewCounterKey(playerID, offerID) - viewTimestampKey := models.GetViewTimestampKey(playerID, offerID) - //When - isReplay, nextAt, err := models.ViewOffer(db, redisClient, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) + isReplay, nextAt, err := models.ViewOffer(db, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) Expect(isReplay).To(BeFalse()) Expect(err).NotTo(HaveOccurred()) - isReplay, nextAt, err = models.ViewOffer(db, redisClient, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) + isReplay, nextAt, err = models.ViewOffer(db, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) Expect(isReplay).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) - viewCounter, err := redisClient.Client.Get(viewCounterKey).Int64() - Expect(err).NotTo(HaveOccurred()) - viewTimestamp, err := redisClient.Client.Get(viewTimestampKey).Int64() - Expect(err).NotTo(HaveOccurred()) //Then + offerPlayer, err := models.GetOfferPlayer(db, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) + Expect(offerPlayer.ViewCounter).To(Equal(1)) + Expect(offerPlayer.ViewTimestamp.Time.Unix()).To(Equal(currentTime.Unix())) Expect(nextAt).To(Equal(currentTime.Unix() + 1)) - Expect(viewCounter).To(Equal(int64(1))) - Expect(viewTimestamp).To(Equal(currentTime.Unix())) }) It("should increment twice if different impressionIDs", func() { @@ -644,27 +567,21 @@ var _ = Describe("Offer Instance Model", func() { currentTime := time.Now() impressionID := uuid.NewV4().String() - viewCounterKey := models.GetViewCounterKey(playerID, offerID) - viewTimestampKey := models.GetViewTimestampKey(playerID, offerID) - //When - isReplay, nextAt, err := models.ViewOffer(db, redisClient, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) + isReplay, nextAt, err := models.ViewOffer(db, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) Expect(isReplay).To(BeFalse()) Expect(err).NotTo(HaveOccurred()) impressionID = uuid.NewV4().String() - isReplay, nextAt, err = models.ViewOffer(db, redisClient, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) + isReplay, nextAt, err = models.ViewOffer(db, gameID, offerInstanceID, playerID, impressionID, currentTime, nil) Expect(isReplay).To(BeFalse()) Expect(err).NotTo(HaveOccurred()) - viewCounter, err := redisClient.Client.Get(viewCounterKey).Int64() - Expect(err).NotTo(HaveOccurred()) - viewTimestamp, err := redisClient.Client.Get(viewTimestampKey).Int64() - Expect(err).NotTo(HaveOccurred()) //Then + offerPlayer, err := models.GetOfferPlayer(db, gameID, playerID, offerID, nil) Expect(err).NotTo(HaveOccurred()) + Expect(offerPlayer.ViewCounter).To(Equal(2)) + Expect(offerPlayer.ViewTimestamp.Time.Unix()).To(Equal(currentTime.Unix())) Expect(nextAt).To(Equal(currentTime.Unix() + 1)) - Expect(viewCounter).To(Equal(int64(2))) - Expect(viewTimestamp).To(Equal(currentTime.Unix())) }) }) @@ -677,7 +594,7 @@ var _ = Describe("Offer Instance Model", func() { filterAttrs := make(map[string]string) //When - offerInstances, err := models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + offerInstances, err := models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) //Then Expect(err).NotTo(HaveOccurred()) @@ -716,7 +633,7 @@ var _ = Describe("Offer Instance Model", func() { } //When - offerInstances, err := models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, true, nil) + offerInstances, err := models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, true, nil) //Then Expect(err).NotTo(HaveOccurred()) @@ -747,7 +664,7 @@ var _ = Describe("Offer Instance Model", func() { } //When - offerInstances, err := models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + offerInstances, err := models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) //Then Expect(err).NotTo(HaveOccurred()) @@ -764,7 +681,7 @@ var _ = Describe("Offer Instance Model", func() { } //When - offerInstances, err := models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + offerInstances, err := models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) //Then Expect(err).NotTo(HaveOccurred()) @@ -783,7 +700,7 @@ var _ = Describe("Offer Instance Model", func() { } //When - offerInstances, err := models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, true, nil) + offerInstances, err := models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, true, nil) //Then Expect(err).NotTo(HaveOccurred()) @@ -824,7 +741,7 @@ var _ = Describe("Offer Instance Model", func() { } //When - offerInstances, err := models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + offerInstances, err := models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) //Then Expect(err).NotTo(HaveOccurred()) @@ -839,7 +756,7 @@ var _ = Describe("Offer Instance Model", func() { filterAttrs := make(map[string]string) //When - offerInstances, err := models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + offerInstances, err := models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) //Then Expect(offerInstances).To(BeEmpty()) @@ -855,9 +772,9 @@ var _ = Describe("Offer Instance Model", func() { impressionID := uuid.NewV4().String() //When - _, _, err := models.ViewOffer(db, redisClient, gameID, defaultOfferInstanceID, playerID, impressionID, currentTime, nil) + _, _, err := models.ViewOffer(db, gameID, defaultOfferInstanceID, playerID, impressionID, currentTime, nil) Expect(err).NotTo(HaveOccurred()) - offerInstances, err := models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, nextTime, expireDuration, filterAttrs, false, nil) + offerInstances, err := models.GetAvailableOffers(db, offersCache, gameID, playerID, nextTime, expireDuration, filterAttrs, false, nil) Expect(err).NotTo(HaveOccurred()) //Then @@ -875,9 +792,9 @@ var _ = Describe("Offer Instance Model", func() { offerInstanceID := "4407b770-5b24-4ffa-8563-0694d1a10156" //When - _, _, _, err := models.ClaimOffer(db, redisClient, gameID, offerInstanceID, "", "", "", currentTime.Unix(), currentTime, nil) + _, _, _, err := models.ClaimOffer(db, gameID, offerInstanceID, "", "", "", currentTime.Unix(), currentTime, nil) Expect(err).NotTo(HaveOccurred()) - offerInstances, err := models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, nextTime, expireDuration, filterAttrs, false, nil) + offerInstances, err := models.GetAvailableOffers(db, offersCache, gameID, playerID, nextTime, expireDuration, filterAttrs, false, nil) Expect(err).NotTo(HaveOccurred()) //Then @@ -894,7 +811,7 @@ var _ = Describe("Offer Instance Model", func() { filterAttrs := make(map[string]string) //When - offerInstances, err := models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + offerInstances, err := models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) //Then Expect(err).NotTo(HaveOccurred()) @@ -910,17 +827,17 @@ var _ = Describe("Offer Instance Model", func() { claimTime := int64(1486678000) filterAttrs := make(map[string]string) - offerInstances, err := models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + offerInstances, err := models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) Expect(err).NotTo(HaveOccurred()) Expect(offerInstances).To(HaveLen(1)) Expect(offerInstances).To(HaveKey("store")) offerInstanceID := offerInstances["store"][0].ID - _, alreadyClaimed, _, err := models.ClaimOffer(db, redisClient, gameID, offerInstanceID, playerID, productID, transactionID, claimTime, currentTime, nil) + _, alreadyClaimed, _, err := models.ClaimOffer(db, gameID, offerInstanceID, playerID, productID, transactionID, claimTime, currentTime, nil) Expect(err).NotTo(HaveOccurred()) Expect(alreadyClaimed).To(BeFalse()) - offerInstances, err = models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + offerInstances, err = models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) Expect(err).NotTo(HaveOccurred()) Expect(offerInstances).To(HaveLen(0)) }) @@ -935,17 +852,17 @@ var _ = Describe("Offer Instance Model", func() { claimTime := int64(1486678000) filterAttrs := make(map[string]string) - offerInstances, err := models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + offerInstances, err := models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) Expect(err).NotTo(HaveOccurred()) Expect(offerInstances).To(HaveLen(1)) Expect(offerInstances).To(HaveKey("store")) offerInstanceID := offerInstances["store"][0].ID - _, alreadyClaimed, _, err := models.ClaimOffer(db, redisClient, gameID, offerInstanceID, playerID, productID, transactionID, claimTime, currentTime, nil) + _, alreadyClaimed, _, err := models.ClaimOffer(db, gameID, offerInstanceID, playerID, productID, transactionID, claimTime, currentTime, nil) Expect(err).NotTo(HaveOccurred()) Expect(alreadyClaimed).To(BeFalse()) - offerInstances, err = models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + offerInstances, err = models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) Expect(err).NotTo(HaveOccurred()) Expect(offerInstances).To(HaveLen(0)) }) @@ -958,7 +875,7 @@ var _ = Describe("Offer Instance Model", func() { filterAttrs := make(map[string]string) //When - _, err := models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + _, err := models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) //Then Expect(err).To(HaveOccurred()) @@ -973,7 +890,7 @@ var _ = Describe("Offer Instance Model", func() { filterAttrs := make(map[string]string) //When - _, err := models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + _, err := models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) //Then Expect(err).To(HaveOccurred()) @@ -994,7 +911,7 @@ var _ = Describe("Offer Instance Model", func() { Expect(err).NotTo(HaveOccurred()) db.(*runner.DB).DB.Close() // make DB connection unavailable - _, err = models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + _, err = models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("sql: database is closed")) }) @@ -1008,7 +925,7 @@ var _ = Describe("Offer Instance Model", func() { offerInstanceID := "eb7e8d2a-2739-4da3-aa31-7970b63bdad7" //When - offerInstance, err := models.GetOfferInfo(db, redisClient, gameID, playerID, offerInstanceID, expireDuration, nil) + offerInstance, err := models.GetOfferInfo(db, gameID, playerID, offerInstanceID, expireDuration, nil) //Then Expect(err).NotTo(HaveOccurred()) @@ -1027,7 +944,7 @@ var _ = Describe("Offer Instance Model", func() { offerInstanceID := "eb7e8d2a-2739-4da3-aa31-7970b63bdad7" //When - _, err := models.GetOfferInfo(db, redisClient, gameID, playerID, offerInstanceID, expireDuration, nil) + _, err := models.GetOfferInfo(db, gameID, playerID, offerInstanceID, expireDuration, nil) //Then Expect(err).To(HaveOccurred()) @@ -1049,7 +966,7 @@ var _ = Describe("Offer Instance Model", func() { db.(*runner.DB).DB.Close() // make DB connection unavailable //When - _, err = models.GetOfferInfo(db, redisClient, gameID, playerID, offerInstanceID, expireDuration, nil) + _, err = models.GetOfferInfo(db, gameID, playerID, offerInstanceID, expireDuration, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("sql: database is closed")) }) @@ -1066,13 +983,13 @@ var _ = Describe("Offer Instance Model", func() { filterAttrs := make(map[string]string) // Get fot the first time - offerInstances, err := models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + offerInstances, err := models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) Expect(err).NotTo(HaveOccurred()) Expect(offerInstances).To(HaveLen(1)) Expect(offerInstances[place]).To(HaveLen(2)) // Claim the offer instance - _, alreadyClaimed, _, err := models.ClaimOffer(db, redisClient, gameID, offerInstances[place][0].ID, playerID, "", transactionID, currentTime.Unix(), currentTime, nil) + _, alreadyClaimed, _, err := models.ClaimOffer(db, gameID, offerInstances[place][0].ID, playerID, "", transactionID, currentTime.Unix(), currentTime, nil) Expect(alreadyClaimed).To(BeFalse()) Expect(err).NotTo(HaveOccurred()) @@ -1087,7 +1004,7 @@ var _ = Describe("Offer Instance Model", func() { Expect(err).NotTo(HaveOccurred()) // Should not return the popup offer, since it was claimed for the first time - offerInstances, err = models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + offerInstances, err = models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) Expect(err).NotTo(HaveOccurred()) Expect(offerInstances).To(HaveKey(place)) Expect(offerInstances[place]).To(HaveLen(1)) @@ -1095,9 +1012,9 @@ var _ = Describe("Offer Instance Model", func() { It("should return updated offer with one remaining view", func() { playerID := "player-1" - gameID := "another-game" + gameID := "another-game-2" place := "unique-place" - offerID := "f1f74fcd-17ae-4ccd-a248-f77c60e78c8c" + offerID := "f1f74fcd-17ae-4ccd-a248-f77c60e78c82" currentTime := time.Unix(1486678000, 0) nextTime := func(currentTime time.Time) time.Time { return time.Unix(currentTime.Unix()+10, 0) @@ -1105,13 +1022,13 @@ var _ = Describe("Offer Instance Model", func() { filterAttrs := make(map[string]string) // Get offer instances - offerInstances, err := models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + offerInstances, err := models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) Expect(err).NotTo(HaveOccurred()) - Expect(offerInstances[place]).To(HaveLen(2)) + Expect(offerInstances[place]).To(HaveLen(1)) offerInstanceID := offerInstances[place][0].ID // View once - _, _, err = models.ViewOffer(db, redisClient, gameID, offerInstanceID, playerID, uuid.NewV4().String(), currentTime, nil) + _, _, err = models.ViewOffer(db, gameID, offerInstanceID, playerID, uuid.NewV4().String(), currentTime, nil) Expect(err).NotTo(HaveOccurred()) // Update Offer @@ -1119,27 +1036,26 @@ var _ = Describe("Offer Instance Model", func() { err = db.SQL("SELECT * FROM offers WHERE id = $1 AND game_id = $2", offerID, gameID).QueryStruct(offer) Expect(err).NotTo(HaveOccurred()) offer.Contents = dat.JSON([]byte(`{ "somethingNew": 100 }`)) - offer, err = models.UpdateOffer(db, offer, offersCache, nil) + _, err = models.UpdateOffer(db, offer, offersCache, nil) Expect(err).NotTo(HaveOccurred()) // Get offer currentTime = nextTime(currentTime) - offerInstances, err = models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + offerInstances, err = models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) Expect(err).NotTo(HaveOccurred()) Expect(offerInstances).To(HaveKey(place)) - Expect(offerInstances[place]).To(HaveLen(2)) + Expect(offerInstances[place]).To(HaveLen(1)) offerInstanceID = offerInstances[place][0].ID // Sees twice - _, _, err = models.ViewOffer(db, redisClient, gameID, offerInstanceID, playerID, uuid.NewV4().String(), currentTime, nil) + _, _, err = models.ViewOffer(db, gameID, offerInstanceID, playerID, uuid.NewV4().String(), currentTime, nil) Expect(err).NotTo(HaveOccurred()) // Get offer, expect unique-place to not be returned currentTime = nextTime(currentTime) - offerInstances, err = models.GetAvailableOffers(db, redisClient, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) + offerInstances, err = models.GetAvailableOffers(db, offersCache, gameID, playerID, currentTime, expireDuration, filterAttrs, false, nil) Expect(err).NotTo(HaveOccurred()) - Expect(offerInstances).To(HaveKey(place)) - Expect(offerInstances[place]).To(HaveLen(1)) + Expect(offerInstances).NotTo(HaveKey(place)) }) }) }) diff --git a/models/offer_player.go b/models/offer_player.go new file mode 100644 index 0000000..1f02ee3 --- /dev/null +++ b/models/offer_player.go @@ -0,0 +1,105 @@ +// offers api +// https://github.com/topfreegames/offers +// +// Licensed under the MIT license: +// http://www.opensource.org/licenses/mit-license +// Copyright © 2017 Top Free Games + +package models + +import ( + "time" + + dat "gopkg.in/mgutz/dat.v2/dat" + runner "gopkg.in/mgutz/dat.v2/sqlx-runner" +) + +//OfferPlayer represents an offer seen by a player +type OfferPlayer struct { + ID string `db:"id" json:"id" valid:"uuidv4,required"` + GameID string `db:"game_id" json:"gameId" valid:"matches(^[^-][a-zA-Z0-9-_]*$),stringlength(1|255),required"` + PlayerID string `db:"player_id" json:"playerId" valid:"ascii,stringlength(1|1000),required"` + OfferID string `db:"offer_id" json:"offerId" valid:"uuidv4,required"` + ClaimCounter int `db:"claim_counter" json:"claimCounter" valid:"int"` + ClaimTimestamp dat.NullTime `db:"claim_timestamp" json:"claimTimestamp" valid:""` + ViewCounter int `db:"view_counter" json:"viewCounter" valid:"int"` + ViewTimestamp dat.NullTime `db:"view_timestamp" json:"viewTimestamp" valid:""` + Transactions dat.JSON `db:"transactions" json:"transactions" valid:""` + Impressions dat.JSON `db:"impressions" json:"impressions" valid:""` +} + +//GetOfferPlayer returns an offer player +func GetOfferPlayer(db runner.Connection, gameID, playerID, offerID string, mr *MixedMetricsReporter) (*OfferPlayer, error) { + var offerPlayer OfferPlayer + err := mr.WithDatastoreSegment("offer_players", SegmentSelect, func() error { + return db. + Select("*"). + From("offer_players"). + Where("game_id = $1 AND player_id = $2 AND offer_id = $3", gameID, playerID, offerID). + QueryStruct(&offerPlayer) + }) + + return &offerPlayer, err +} + +//GetOffersByPlayer returns all offers by player +func GetOffersByPlayer(db runner.Connection, gameID, playerID string, mr *MixedMetricsReporter) ([]*OfferPlayer, error) { + var offersByPlayer []*OfferPlayer + err := mr.WithDatastoreSegment("offer_players", "select all", func() error { + return db. + Select("*"). + From("offer_players"). + Where("game_id = $1 AND player_id = $2", gameID, playerID). + QueryStructs(&offersByPlayer) + }) + + return offersByPlayer, err +} + +//CreateOfferPlayer creates an offer player +func CreateOfferPlayer(db runner.Connection, offerPlayer *OfferPlayer, mr *MixedMetricsReporter) error { + if offerPlayer.Transactions == nil { + offerPlayer.Transactions = dat.JSON([]byte(`[]`)) + } + if offerPlayer.Impressions == nil { + offerPlayer.Impressions = dat.JSON([]byte(`[]`)) + } + return mr.WithDatastoreSegment("offer_players", SegmentInsert, func() error { + return db. + InsertInto("offer_players"). + Columns("game_id", "player_id", "offer_id", "claim_counter", "claim_timestamp", "view_counter", "view_timestamp", "transactions", "impressions"). + Record(offerPlayer). + Returning("*"). + QueryStruct(offerPlayer) + }) +} + +//ClaimOfferPlayer increments the claim counter and updates the timestamp +func ClaimOfferPlayer(db runner.Connection, offerPlayer *OfferPlayer, t time.Time, mr *MixedMetricsReporter) error { + return mr.WithDatastoreSegment("offer_players", SegmentUpdate, func() error { + const incrCounter = dat.UnsafeString("claim_counter + 1") + return db. + Update("offer_players"). + Set("claim_counter", incrCounter). + Set("claim_timestamp", t). + Set("transactions", offerPlayer.Transactions). + Where("game_id = $1 AND player_id = $2 AND offer_id = $3", offerPlayer.GameID, offerPlayer.PlayerID, offerPlayer.OfferID). + Returning("claim_counter, claim_timestamp, transactions"). + QueryStruct(offerPlayer) + }) +} + +//ViewOfferPlayer increments the view counter and updates the timestamp +func ViewOfferPlayer(db runner.Connection, offerPlayer *OfferPlayer, t time.Time, mr *MixedMetricsReporter) error { + return mr.WithDatastoreSegment("offer_players", SegmentUpdate, func() error { + const incrCounter = dat.UnsafeString("view_counter + 1") + return db. + Update("offer_players"). + Set("view_counter", incrCounter). + Set("view_timestamp", t). + Set("impressions", offerPlayer.Impressions). + Where("game_id = $1 AND player_id = $2 AND offer_id = $3", offerPlayer.GameID, offerPlayer.PlayerID, offerPlayer.OfferID). + Returning("view_counter, view_timestamp, impressions"). + QueryStruct(offerPlayer) + }) +} diff --git a/perf/main.go b/perf/main.go index ef11d82..3ff150e 100644 --- a/perf/main.go +++ b/perf/main.go @@ -9,12 +9,12 @@ package main import ( "fmt" + "time" + bench "github.com/topfreegames/offers/bench" "github.com/topfreegames/offers/models" oTesting "github.com/topfreegames/offers/testing" - "github.com/topfreegames/offers/util" runner "gopkg.in/mgutz/dat.v2/sqlx-runner" - "time" ) func initParameters() { @@ -83,14 +83,14 @@ func populateOffers(db *runner.Connection, games []*models.Game) (map[string][]* return offersByGame, err } -func populateOfferInstances(db *runner.Connection, redis *util.RedisClient, offersByGame map[string][]*models.Offer) (map[string][]*models.OfferToReturn, error) { +func populateOfferInstances(db *runner.Connection, offersByGame map[string][]*models.Offer) (map[string][]*models.OfferToReturn, error) { offerInstances := make(map[string][]*models.OfferToReturn) var err error return offerInstances, err } -func populateTestDB(db *runner.Connection, redis *util.RedisClient) error { +func populateTestDB(db *runner.Connection) error { games, err := populateGames(db) if err != nil { return err @@ -101,7 +101,7 @@ func populateTestDB(db *runner.Connection, redis *util.RedisClient) error { return err } - _, err = populateOfferInstances(db, redis, offersByGame) + _, err = populateOfferInstances(db, offersByGame) if err != nil { return err } @@ -115,12 +115,7 @@ func main() { panic(err.Error()) } - redis, err := oTesting.GetTestRedis() - if err != nil { - panic(err.Error()) - } - - err = populateTestDB(&db, redis) + err = populateTestDB(&db) if err != nil { panic(err.Error()) } diff --git a/testing/models.go b/testing/models.go index 355e010..e062231 100644 --- a/testing/models.go +++ b/testing/models.go @@ -8,10 +8,8 @@ package testing import ( - "github.com/Sirupsen/logrus" "github.com/go-testfixtures/testfixtures" "github.com/topfreegames/offers/models" - "github.com/topfreegames/offers/util" runner "gopkg.in/mgutz/dat.v2/sqlx-runner" ) @@ -37,21 +35,10 @@ func GetPerfDB() (runner.Connection, error) { ) } -//GetTestRedis returns a redis client -func GetTestRedis() (*util.RedisClient, error) { - redisHost := "localhost" - redisPort := 6333 - redisPass := "" - redisDB := 0 - redisMaxPoolSize := 20 - - l := logrus.New().WithFields(logrus.Fields{ - "redis.host": redisHost, - "redis.port": redisPort, - "redis.redisDB": redisDB, - "redis.redisMaxPoolSize": redisMaxPoolSize, - }) - return util.GetRedisClient(redisHost, redisPort, redisPass, redisDB, redisMaxPoolSize, l) +//ClearOfferPlayers +func ClearOfferPlayers(db runner.Connection) error { + _, err := db.DeleteFrom("offer_players").Exec() + return err } //LoadFixtures into the DB diff --git a/util/redis.go b/util/redis.go deleted file mode 100644 index c5bf5d4..0000000 --- a/util/redis.go +++ /dev/null @@ -1,47 +0,0 @@ -// Package util for redis helpers -// https://github.com/topfreegames/offers -// -// Licensed under the MIT license: -// http://www.opensource.org/licenses/mit-license -// Copyright © 2017 Top Free Games -package util - -import ( - "fmt" - - redis "gopkg.in/redis.v5" - - "github.com/Sirupsen/logrus" -) - -// RedisClient identifies uniquely one redis client with a pool of connections -type RedisClient struct { - Logger logrus.FieldLogger - Client *redis.Client -} - -// GetRedisClient creates and returns a new redis client based on the given settings -func GetRedisClient(redisHost string, redisPort int, redisPassword string, redisDB int, maxPoolSize int, logger logrus.FieldLogger) (*RedisClient, error) { - client := redis.NewClient(&redis.Options{ - Addr: fmt.Sprintf("%s:%d", redisHost, redisPort), - Password: redisPassword, - DB: redisDB, - PoolSize: maxPoolSize, - }) - - _, err := client.Ping().Result() - if err != nil { - return nil, err - } - - cl := &RedisClient{ - Client: client, - Logger: logger, - } - return cl, nil -} - -// GetConnection return a redis connection -func (c *RedisClient) GetConnection() *redis.Client { - return c.Client -}