From 6eeaf757cd26c1413fbfeb69b9c0d579c2a8a99a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C5=A1per=20Dobrovoljc?= Date: Wed, 14 Aug 2024 23:17:37 +0200 Subject: [PATCH] Draw method filters fix --- api/admin.yaml | 53 +++++++++++++++++++++ api/public.yaml | 23 +++++++++ internal/api/admin/draw_method.go | 44 +++++++++++++++++ internal/api/admin/server.go | 4 ++ internal/api/middleware.go | 2 + internal/app.go | 2 + internal/database/draw_method.go | 18 +++---- internal/pkg/models/api/draw_method.go | 49 +++++++++++++++++++ internal/pkg/models/api/won_prize.go | 6 +-- internal/pkg/models/database/draw_method.go | 17 +++---- internal/pkg/models/enums/draw_method.go | 9 ++++ internal/service/draw_method.go | 32 +++++++++++++ internal/service/game.go | 7 +-- schema.hcl | 9 ++++ 14 files changed, 251 insertions(+), 24 deletions(-) create mode 100644 internal/api/admin/draw_method.go create mode 100644 internal/pkg/models/api/draw_method.go create mode 100644 internal/pkg/models/enums/draw_method.go create mode 100644 internal/service/draw_method.go diff --git a/api/admin.yaml b/api/admin.yaml index b9e81ac..54fdfbd 100644 --- a/api/admin.yaml +++ b/api/admin.yaml @@ -2,6 +2,25 @@ basePath: / consumes: - application/json definitions: + DrawMethod: + properties: + data: + type: string + draw_method_id: + type: string + method: + enum: + - first_n + - chance + type: string + name: + type: string + required: + - draw_method_id + - name + - method + - data + type: object ErrorResponse: properties: code: @@ -56,6 +75,10 @@ definitions: - id - name type: object + GetDrawMethodsResponse: + items: + $ref: '#/definitions/DrawMethod' + type: array GetGameResponse: $ref: '#/definitions/Game' GetGamesResponse: @@ -214,6 +237,36 @@ info: title: Prizer Admin API version: "1.0" paths: + /draw-methods: + get: + operationId: getDrawMethods + parameters: + - in: query + name: gameId + type: string + - in: query + name: participationMethodId + type: string + responses: + "200": + description: GetDrawMethodsResponse + schema: + $ref: '#/definitions/GetDrawMethodsResponse' + "400": + description: ErrorResponse + schema: + $ref: '#/definitions/ErrorResponse' + "403": + description: ErrorResponse + schema: + $ref: '#/definitions/ErrorResponse' + "500": + description: ErrorResponse + schema: + $ref: '#/definitions/ErrorResponse' + tags: + - admin + - drawMethods /games: get: operationId: getGames diff --git a/api/public.yaml b/api/public.yaml index a6cad66..f5195ab 100644 --- a/api/public.yaml +++ b/api/public.yaml @@ -2,6 +2,25 @@ basePath: / consumes: - application/json definitions: + DrawMethod: + properties: + data: + type: string + draw_method_id: + type: string + method: + enum: + - first_n + - chance + type: string + name: + type: string + required: + - draw_method_id + - name + - method + - data + type: object ErrorResponse: properties: code: @@ -56,6 +75,10 @@ definitions: - id - name type: object + GetDrawMethodsResponse: + items: + $ref: '#/definitions/DrawMethod' + type: array GetGameResponse: $ref: '#/definitions/Game' GetGamesResponse: diff --git a/internal/api/admin/draw_method.go b/internal/api/admin/draw_method.go new file mode 100644 index 0000000..49a75b1 --- /dev/null +++ b/internal/api/admin/draw_method.go @@ -0,0 +1,44 @@ +package admin + +import ( + "github.com/gapidobri/prizer/internal/pkg/models/api" + "github.com/gin-gonic/gin" + "net/http" +) + +func (s *Server) drawMethodRoutes() { + group := s.engine.Group("/draw-methods") + + // swagger:route GET /draw-methods admin drawMethods getDrawMethods + // + // parameters: + // + name: gameId + // in: query + // type: string + // + name: participationMethodId + // in: query + // type: string + // + // responses: + // 200: GetDrawMethodsResponse + // 400: ErrorResponse + // 403: ErrorResponse + // 500: ErrorResponse + // + group.GET("", func(c *gin.Context) { + var filter api.GetDrawMethodsFilter + err := c.ShouldBindQuery(&filter) + if err != nil { + _ = c.Error(err) + return + } + + drawMethods, err := s.drawMethodService.GetDrawMethods(c.Request.Context(), filter) + if err != nil { + _ = c.Error(err) + return + } + + c.JSON(http.StatusOK, drawMethods) + }) +} diff --git a/internal/api/admin/server.go b/internal/api/admin/server.go index fc0c6f0..c6138f0 100644 --- a/internal/api/admin/server.go +++ b/internal/api/admin/server.go @@ -15,6 +15,7 @@ type Server struct { prizeService *service.PrizeService wonPrizeService *service.WonPrizeService participationMethodService *service.ParticipationMethodService + drawMethodService *service.DrawMethodService } func NewServer( @@ -24,6 +25,7 @@ func NewServer( prizeService *service.PrizeService, wonPrizeService *service.WonPrizeService, participationMethodService *service.ParticipationMethodService, + drawMethodService *service.DrawMethodService, ) *Server { return &Server{ engine: api.NewServer(db), @@ -32,6 +34,7 @@ func NewServer( prizeService: prizeService, wonPrizeService: wonPrizeService, participationMethodService: participationMethodService, + drawMethodService: drawMethodService, } } @@ -41,6 +44,7 @@ func (s *Server) Run(address string) { s.prizeRoutes() s.wonPrizeRoutes() s.participationMethodRoutes() + s.drawMethodRoutes() log.Infof("Admin API listening on %s", address) diff --git a/internal/api/middleware.go b/internal/api/middleware.go index f4dd1ba..db90fd9 100644 --- a/internal/api/middleware.go +++ b/internal/api/middleware.go @@ -48,6 +48,8 @@ func ErrorHandler(c *gin.Context) { errString += "is missing" case "email": errString += "is not a valid email address" + case "uuid": + errString += "is not a valid UUID" default: errString += fmt.Sprintf("failed on '%s'", err.Tag()) } diff --git a/internal/app.go b/internal/app.go index 7774bde..fb1e7fe 100644 --- a/internal/app.go +++ b/internal/app.go @@ -76,6 +76,7 @@ func Run() { prizeService := service.NewPrizeService(prizeRepository) wonPrizeService := service.NewWonPrizeService(wonPrizeRepository) participationMethodService := service.NewParticipationMethodService(participationMethodRepository) + drawMethodService := service.NewDrawMethodService(drawMethodRepository) // APIs publicApi := public.NewServer(db, gameService) @@ -86,6 +87,7 @@ func Run() { prizeService, wonPrizeService, participationMethodService, + drawMethodService, ) go publicApi.Run(cfg.Http.Public.Address) diff --git a/internal/database/draw_method.go b/internal/database/draw_method.go index b3b3e21..b5fde5e 100644 --- a/internal/database/draw_method.go +++ b/internal/database/draw_method.go @@ -8,7 +8,7 @@ import ( ) type DrawMethodRepository interface { - GetDrawMethods(ctx context.Context, gameId string, filter database.GetDrawMethodsFilter) ([]database.DrawMethod, error) + GetDrawMethods(ctx context.Context, filter database.GetDrawMethodsFilter) ([]database.DrawMethod, error) } type drawMethodRepository struct { @@ -21,16 +21,18 @@ func NewDrawMethodRepository(db *sqlx.DB) DrawMethodRepository { } } -func (d *drawMethodRepository) GetDrawMethods(ctx context.Context, gameId string, filter database.GetDrawMethodsFilter) ([]database.DrawMethod, error) { +func (d *drawMethodRepository) GetDrawMethods(ctx context.Context, filter database.GetDrawMethodsFilter) ([]database.DrawMethod, error) { query := sq. - Select("DISTINCT ON (draw_method_id) dm.*"). - From("participation_methods"). - InnerJoin("participation_methods_draw_methods USING (participation_method_id)"). - InnerJoin("draw_methods dm USING (draw_method_id)"). - Where("game_id = ?", gameId) + Select("dm.*"). + From("draw_methods dm") + if filter.GameId != nil { + query = query.Where(sq.Eq{"dm.game_id": filter.GameId}) + } if filter.ParticipationMethodId != nil { - query = query.Where("participation_method_id = ?", filter.ParticipationMethodId) + query = query. + InnerJoin("participation_methods_draw_methods USING (draw_method_id)"). + Where(sq.Eq{"participation_method_id": filter.ParticipationMethodId}) } sql, args := query. diff --git a/internal/pkg/models/api/draw_method.go b/internal/pkg/models/api/draw_method.go new file mode 100644 index 0000000..846a80b --- /dev/null +++ b/internal/pkg/models/api/draw_method.go @@ -0,0 +1,49 @@ +package api + +import ( + dbModels "github.com/gapidobri/prizer/internal/pkg/models/database" + "github.com/gapidobri/prizer/internal/pkg/models/enums" +) + +// swagger:model DrawMethod +type DrawMethod struct { + // required: true + Id string `json:"draw_method_id"` + + // required: true + GameId string `json:"game_id"` + + // required: true + Name string `json:"name"` + + // required: true + Method enums.DrawMethod `json:"method"` + + // required: true + Data string `json:"data"` +} + +func DrawMethodFromDB(drawMethod dbModels.DrawMethod) DrawMethod { + return DrawMethod{ + Id: drawMethod.Id, + GameId: drawMethod.GameId, + Name: drawMethod.Name, + Method: drawMethod.Method, + Data: drawMethod.Data, + } +} + +type GetDrawMethodsFilter struct { + GameId *string `form:"gameId" binding:"omitnil,uuid"` + ParticipationId *string `form:"participationId" binding:"omitnil,uuid"` +} + +func (f GetDrawMethodsFilter) ToDB() dbModels.GetDrawMethodsFilter { + return dbModels.GetDrawMethodsFilter{ + GameId: f.GameId, + ParticipationMethodId: f.ParticipationId, + } +} + +// swagger:model GetDrawMethodsResponse +type GetDrawMethodsResponse []DrawMethod diff --git a/internal/pkg/models/api/won_prize.go b/internal/pkg/models/api/won_prize.go index 0f03052..d95ff58 100644 --- a/internal/pkg/models/api/won_prize.go +++ b/internal/pkg/models/api/won_prize.go @@ -23,9 +23,9 @@ func WonPrizeFromDB(wonPrize dbModels.WonPrize) WonPrize { } type GetWonPrizesFilter struct { - GameId *string `form:"gameId"` - UserId *string `form:"userId"` - PrizeId *string `form:"prizeId"` + GameId *string `form:"gameId" binding:"omitnil,uuid"` + UserId *string `form:"userId" binding:"omitnil,uuid"` + PrizeId *string `form:"prizeId" binding:"omitnil,uuid"` } func (f GetWonPrizesFilter) ToDB() dbModels.GetWonPrizesFilter { diff --git a/internal/pkg/models/database/draw_method.go b/internal/pkg/models/database/draw_method.go index eee04d2..d1e35ee 100644 --- a/internal/pkg/models/database/draw_method.go +++ b/internal/pkg/models/database/draw_method.go @@ -1,17 +1,13 @@ package database -type DrawMethodEnum string - -const ( - DrawMethodFirstN DrawMethodEnum = "first_n" - DrawMethodChance DrawMethodEnum = "chance" -) +import "github.com/gapidobri/prizer/internal/pkg/models/enums" type DrawMethod struct { - Id string `db:"draw_method_id"` - Name string `db:"name"` - Method DrawMethodEnum `db:"method"` - Data string `db:"data"` + Id string `db:"draw_method_id"` + GameId string `db:"game_id"` + Name string `db:"name"` + Method enums.DrawMethod `db:"method"` + Data string `db:"data"` } type DrawMethodChanceData struct { @@ -23,5 +19,6 @@ type DrawMethodFirstNData struct { } type GetDrawMethodsFilter struct { + GameId *string ParticipationMethodId *string } diff --git a/internal/pkg/models/enums/draw_method.go b/internal/pkg/models/enums/draw_method.go new file mode 100644 index 0000000..6e776e1 --- /dev/null +++ b/internal/pkg/models/enums/draw_method.go @@ -0,0 +1,9 @@ +package enums + +// swagger:enum DrawMethod +type DrawMethod string + +const ( + DrawMethodFirstN DrawMethod = "first_n" + DrawMethodChance DrawMethod = "chance" +) diff --git a/internal/service/draw_method.go b/internal/service/draw_method.go new file mode 100644 index 0000000..ddf7eb2 --- /dev/null +++ b/internal/service/draw_method.go @@ -0,0 +1,32 @@ +package service + +import ( + "context" + "github.com/gapidobri/prizer/internal/database" + "github.com/gapidobri/prizer/internal/pkg/models/api" + dbModels "github.com/gapidobri/prizer/internal/pkg/models/database" + "github.com/samber/lo" +) + +type DrawMethodService struct { + drawMethodRepository database.DrawMethodRepository +} + +func NewDrawMethodService(drawMethodRepository database.DrawMethodRepository) *DrawMethodService { + return &DrawMethodService{ + drawMethodRepository: drawMethodRepository, + } +} + +func (s *DrawMethodService) GetDrawMethods(ctx context.Context, filter api.GetDrawMethodsFilter) (api.GetDrawMethodsResponse, error) { + drawMethods, err := s.drawMethodRepository.GetDrawMethods(ctx, filter.ToDB()) + if err != nil { + return nil, err + } + + apiDrawMethods := lo.Map(drawMethods, func(drawMethod dbModels.DrawMethod, _ int) api.DrawMethod { + return api.DrawMethodFromDB(drawMethod) + }) + + return apiDrawMethods, nil +} diff --git a/internal/service/game.go b/internal/service/game.go index b8b56d6..7b8d047 100644 --- a/internal/service/game.go +++ b/internal/service/game.go @@ -263,7 +263,8 @@ func (s *GameService) Participate(ctx context.Context, participationMethodId str } // Participate in all draw methods - drawMethods, err := s.drawMethodRepository.GetDrawMethods(ctx, game.Id, dbModels.GetDrawMethodsFilter{ + drawMethods, err := s.drawMethodRepository.GetDrawMethods(ctx, dbModels.GetDrawMethodsFilter{ + GameId: &game.Id, ParticipationMethodId: &participationMethodId, }) if err != nil { @@ -290,10 +291,10 @@ func (s *GameService) Participate(ctx context.Context, participationMethodId str } switch drawMethod.Method { - case dbModels.DrawMethodFirstN: + case enums.DrawMethodFirstN: wonPrizes = append(wonPrizes, prizes[0]) - case dbModels.DrawMethodChance: + case enums.DrawMethodChance: var data dbModels.DrawMethodChanceData err = json.Unmarshal([]byte(drawMethod.Data), &data) if err != nil { diff --git a/schema.hcl b/schema.hcl index 54e1bbe..8857ebf 100644 --- a/schema.hcl +++ b/schema.hcl @@ -121,6 +121,9 @@ table "draw_methods" { type = uuid default = sql("gen_random_uuid()") } + column "game_id" { + type = uuid + } column "name" { type = varchar default = "Draw method" @@ -136,6 +139,12 @@ table "draw_methods" { column.draw_method_id ] } + foreign_key "game_fk" { + columns = [column.game_id] + ref_columns = [table.games.column.game_id] + on_delete = CASCADE + on_update = CASCADE + } } table "draw_methods_prizes" {