From f60cfba8b33faa574ab28ebd6a63f02c8a9fdefc Mon Sep 17 00:00:00 2001 From: ping-yee <611077101@mail.nknu.edu.tw> Date: Mon, 23 Oct 2023 20:17:54 +0800 Subject: [PATCH 01/89] feat: initial card and the player draw card. --- Backend/cmd/app/main.go | 3 +- Backend/cmd/migrate/migrate.go | 16 ++++++++ Backend/enums/Message.go | 39 +++++++++++++++++++ Backend/go.mod | 3 +- Backend/go.sum | 2 - .../service/delivery/http/v1/game_handler.go | 9 ++++- Backend/service/repository/cardRepository.go | 24 ++++++++++++ .../repository/mysql/mysqlCardRepository.go | 37 ++++++++++++++++++ .../repository/mysql/mysqlPlayerRepository.go | 25 ++++++++++++ .../service/repository/playerRepository.go | 12 ++++++ Backend/tests/acceptance/game_test.go | 3 +- Backend/tests/e2e/game_api_test.go | 4 +- README.md | 5 +++ playerCardsRepository.go | 22 +++++++++++ 14 files changed, 195 insertions(+), 9 deletions(-) create mode 100644 Backend/enums/Message.go create mode 100644 Backend/service/repository/cardRepository.go create mode 100644 Backend/service/repository/mysql/mysqlCardRepository.go create mode 100644 playerCardsRepository.go diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index 88bc5f3..cd0e4c4 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -16,8 +16,9 @@ func main() { gameRepo := mysqlRepo.NewGameRepository(db) playerRepo := mysqlRepo.NewPlayerRepository(db) + cardRepo := mysqlRepo.NewCardRepository(db) - http.NewGameHandler(engine, gameRepo, playerRepo) + http.NewGameHandler(engine, gameRepo, playerRepo, cardRepo) engine.Run(":8080") } diff --git a/Backend/cmd/migrate/migrate.go b/Backend/cmd/migrate/migrate.go index 05d4a31..4e73579 100644 --- a/Backend/cmd/migrate/migrate.go +++ b/Backend/cmd/migrate/migrate.go @@ -23,6 +23,14 @@ func MigrationMysql(db *gorm.DB) { if err != nil { return } + err = db.AutoMigrate(&repository.Card{}) + if err != nil { + return + } + err = db.AutoMigrate(&repository.PlayerCards{}) + if err != nil { + return + } } func Migration(db *gorm.DB) { @@ -34,4 +42,12 @@ func Migration(db *gorm.DB) { if err != nil { return } + err = db.AutoMigrate(&repository.Card{}) + if err != nil { + return + } + err = db.AutoMigrate(&repository.PlayerCards{}) + if err != nil { + return + } } diff --git a/Backend/enums/Message.go b/Backend/enums/Message.go new file mode 100644 index 0000000..0bdc592 --- /dev/null +++ b/Backend/enums/Message.go @@ -0,0 +1,39 @@ +package enums + +import ( + "github.com/Game-as-a-Service/The-Message/service/repository" +) + +func GetCards(gameId int) []*repository.Card { + + IsDrawed := 0 + + cards := []*repository.Card{} + cards = append(cards, &repository.Card{ + Id: 1, + Name: "Lock On", + Color: "Red", + IsDrawed: IsDrawed, + GameId: gameId, + }, &repository.Card{ + Id: 2, + Name: "Lock On", + Color: "Blue", + IsDrawed: IsDrawed, + GameId: gameId, + }, &repository.Card{ + Id: 3, + Name: "Lure Away", + Color: "Red", + IsDrawed: IsDrawed, + GameId: gameId, + }, &repository.Card{ + Id: 4, + Name: "Lure Away", + Color: "Red", + IsDrawed: IsDrawed, + GameId: gameId, + }) + + return cards +} diff --git a/Backend/go.mod b/Backend/go.mod index f1ad291..61572ce 100644 --- a/Backend/go.mod +++ b/Backend/go.mod @@ -3,8 +3,8 @@ module github.com/Game-as-a-Service/The-Message go 1.21.0 require ( - github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/gin-gonic/gin v1.9.1 + github.com/joho/godotenv v1.5.1 github.com/stretchr/testify v1.8.3 gorm.io/driver/mysql v1.5.1 gorm.io/gorm v1.25.4 @@ -24,7 +24,6 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/joho/godotenv v1.5.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/kr/pretty v0.3.1 // indirect diff --git a/Backend/go.sum b/Backend/go.sum index 049652b..d33d26e 100644 --- a/Backend/go.sum +++ b/Backend/go.sum @@ -1,5 +1,3 @@ -github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= -github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 04b67ad..7e0e59f 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -16,12 +16,14 @@ import ( type GameHandler struct { GameRepo repository.GameRepository PlayerRepo repository.PlayerRepository + CardRepo repository.CardRepository } -func NewGameHandler(engine *gin.Engine, gameRepo *mysqlRepo.GameRepository, playerRepo *mysqlRepo.PlayerRepository) *GameHandler { +func NewGameHandler(engine *gin.Engine, gameRepo *mysqlRepo.GameRepository, playerRepo *mysqlRepo.PlayerRepository, cardRepo *mysqlRepo.CardRepository) *GameHandler { handler := &GameHandler{ GameRepo: gameRepo, PlayerRepo: playerRepo, + CardRepo: cardRepo, } engine.POST("/api/v1/games", handler.StartGame) engine.Static("/swagger", "./web/swagger-ui") @@ -59,11 +61,13 @@ func (g *GameHandler) StartGame(c *gin.Context) { hashString := hex.EncodeToString(hash[:]) game.Token = hashString + // 建置遊戲 game, err := g.GameRepo.CreateGame(c, game) // 建立身份牌牌堆 identityCards := service.InitIdentityCards(len(req.Players)) + // 傳入玩家 for i, reqPlayer := range req.Players { player := new(repository.Player) player.Name = reqPlayer.Name @@ -72,6 +76,9 @@ func (g *GameHandler) StartGame(c *gin.Context) { _, err = g.PlayerRepo.CreatePlayer(c, player) } + // 情報(功能)排堆建立 + _, err = g.CardRepo.InitialCard(c, game.Id) + if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return diff --git a/Backend/service/repository/cardRepository.go b/Backend/service/repository/cardRepository.go new file mode 100644 index 0000000..26f18f7 --- /dev/null +++ b/Backend/service/repository/cardRepository.go @@ -0,0 +1,24 @@ +package repository + +import ( + "context" + "time" + + "gorm.io/gorm" +) + +type Card struct { + gorm.Model + Id int `gorm:"primaryKey;auto_increment"` + Name string + Color string + IsDrawed int + GameId int `gorm:"foreignKey:GameId;references:Id"` + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoCreateTime"` + DeletedAt gorm.DeletedAt +} + +type CardRepository interface { + InitialCard(ctx context.Context, gameId int) ([]*Card, error) +} diff --git a/Backend/service/repository/mysql/mysqlCardRepository.go b/Backend/service/repository/mysql/mysqlCardRepository.go new file mode 100644 index 0000000..6ba206a --- /dev/null +++ b/Backend/service/repository/mysql/mysqlCardRepository.go @@ -0,0 +1,37 @@ +package mysql + +import ( + "context" + "fmt" + + "github.com/Game-as-a-Service/The-Message/enums" + "github.com/Game-as-a-Service/The-Message/service/repository" + + "gorm.io/gorm" +) + +type CardRepository struct { + db *gorm.DB +} + +func NewCardRepository(db *gorm.DB) *CardRepository { + return &CardRepository{ + db: db, + } +} + +func (c *CardRepository) InitialCard(ctx context.Context, gameId int) ([]*repository.Card, error) { + + cards := enums.GetCards(gameId) + + var err error + + for _, card := range cards { + err := c.db.Table("cards").Create(&card).Error + if err != nil { + fmt.Println("Error creating card:", err) + } + } + + return cards, err +} diff --git a/Backend/service/repository/mysql/mysqlPlayerRepository.go b/Backend/service/repository/mysql/mysqlPlayerRepository.go index f62f643..f4ac9c1 100644 --- a/Backend/service/repository/mysql/mysqlPlayerRepository.go +++ b/Backend/service/repository/mysql/mysqlPlayerRepository.go @@ -21,3 +21,28 @@ func (p *PlayerRepository) CreatePlayer(ctx context.Context, player *repository. err := p.db.Table("players").Create(player).Error return player, err } + +func (p *PlayerRepository) PlayerDrawCard(ctx context.Context, playerId int) (*repository.Player, error) { + + // Need to split. + player := new(repository.Player) + + playerResult := p.db.Table("players").First(player, "id = ?", playerId) + + if playerResult.Error != nil { + return nil, playerResult.Error + } + + card := new(repository.Card) + + cardResult := p.db.Order("RAND()").Where(&repository.Card{GameId: player.GameId}).First(card) + + if cardResult.Error != nil { + return nil, cardResult.Error + } + + return player, cardResult.Error + + // err := p.db.Table("players").Create(player).Error + // return player, err +} diff --git a/Backend/service/repository/playerRepository.go b/Backend/service/repository/playerRepository.go index fef1a2b..39a39a5 100644 --- a/Backend/service/repository/playerRepository.go +++ b/Backend/service/repository/playerRepository.go @@ -18,6 +18,18 @@ type Player struct { DeletedAt gorm.DeletedAt } +type PlayerCards struct { + gorm.Model + Id int `gorm:"primaryKey;auto_increment"` + Type string + PlayerId int `gorm:"foreignKey:PlayerId;references:Id"` + CardId int `gorm:"foreignKey:CardId;references:Id"` + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoCreateTime"` + DeletedAt gorm.DeletedAt +} + type PlayerRepository interface { CreatePlayer(ctx context.Context, player *Player) (*Player, error) + PlayerDrawCard(ctx context.Context, player *Player) (*Player, error) } diff --git a/Backend/tests/acceptance/game_test.go b/Backend/tests/acceptance/game_test.go index 904e37d..604c3ce 100644 --- a/Backend/tests/acceptance/game_test.go +++ b/Backend/tests/acceptance/game_test.go @@ -27,8 +27,9 @@ func TestMain(m *testing.M) { gameRepo = mysqlRepo.NewGameRepository(testDB) playerRepo := mysqlRepo.NewPlayerRepository(testDB) + cardRepo := mysqlRepo.NewCardRepository(testDB) - handler.NewGameHandler(engine, gameRepo, playerRepo) + handler.NewGameHandler(engine, gameRepo, playerRepo, cardRepo) server := httptest.NewServer(engine) serverURL = server.URL diff --git a/Backend/tests/e2e/game_api_test.go b/Backend/tests/e2e/game_api_test.go index a2bdfd9..edd06a5 100644 --- a/Backend/tests/e2e/game_api_test.go +++ b/Backend/tests/e2e/game_api_test.go @@ -28,9 +28,9 @@ func TestMain(m *testing.M) { gameRepo = mysqlRepo.NewGameRepository(testDB) playerRepo := mysqlRepo.NewPlayerRepository(testDB) - // gameServ := service.NewGameService(gameRepo, playerRepo) + cardRepo := mysqlRepo.NewCardRepository(testDB) - handler.NewGameHandler(engine, gameRepo, playerRepo) + handler.NewGameHandler(engine, gameRepo, playerRepo, cardRepo) server := httptest.NewServer(engine) serverURL = server.URL diff --git a/README.md b/README.md index 8df9491..a832182 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,11 @@ go run cmd/app/main.go } ``` +查看 API 文件網址: +``` +http://localhost:8080/swagger/ +``` + ## Class Diagram ```mermaid diff --git a/playerCardsRepository.go b/playerCardsRepository.go new file mode 100644 index 0000000..9b96ab5 --- /dev/null +++ b/playerCardsRepository.go @@ -0,0 +1,22 @@ +package repository + +import ( + "time" + + "gorm.io/gorm" +) + +type playerCards struct { + gorm.Model + Player_id int `gorm:"primaryKey;auto_increment"` + Type string + PlayerId int `gorm:"foreignKey:PlayerId;references:Id"` + CardId int `gorm:"foreignKey:CardId;references:Id"` + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoCreateTime"` + DeletedAt gorm.DeletedAt +} + +type playerCardsRepository interface { + // InitialCard(ctx context.Context) (*Card, error) +} From 707d4666ab5c0b2d82d47bd3dcfdfcbb5a250a22 Mon Sep 17 00:00:00 2001 From: ping-yee <611077101@mail.nknu.edu.tw> Date: Tue, 24 Oct 2023 23:31:01 +0800 Subject: [PATCH 02/89] feat: Implement the player draw card feature. --- .../repository/mysql/mysqlPlayerRepository.go | 44 ++++++++++++++----- .../service/repository/playerRepository.go | 4 +- playerCardsRepository.go | 22 ---------- 3 files changed, 35 insertions(+), 35 deletions(-) delete mode 100644 playerCardsRepository.go diff --git a/Backend/service/repository/mysql/mysqlPlayerRepository.go b/Backend/service/repository/mysql/mysqlPlayerRepository.go index f4ac9c1..e90d046 100644 --- a/Backend/service/repository/mysql/mysqlPlayerRepository.go +++ b/Backend/service/repository/mysql/mysqlPlayerRepository.go @@ -22,27 +22,47 @@ func (p *PlayerRepository) CreatePlayer(ctx context.Context, player *repository. return player, err } -func (p *PlayerRepository) PlayerDrawCard(ctx context.Context, playerId int) (*repository.Player, error) { - - // Need to split. +func (p *PlayerRepository) GetPlayer(ctx context.Context, playerId int) (*repository.Player, error) { player := new(repository.Player) - playerResult := p.db.Table("players").First(player, "id = ?", playerId) + result := p.db.Table("players").First(player, "id = ?", playerId) - if playerResult.Error != nil { - return nil, playerResult.Error + if result.Error != nil { + return nil, result.Error } + return player, nil +} + +func (p *PlayerRepository) GetRandomCard(ctx context.Context, gameId int) (*repository.Card, error) { card := new(repository.Card) - cardResult := p.db.Order("RAND()").Where(&repository.Card{GameId: player.GameId}).First(card) + result := p.db.Order("RAND()").Where(&repository.Card{GameId: gameId}).First(card) + + if result.Error != nil { + return nil, result.Error + } + + return card, nil +} + +func (p *PlayerRepository) PlayerDrawCard(ctx context.Context, playerId int) (*repository.PlayerCards, error) { + + player, _ := p.GetPlayer(ctx, playerId) - if cardResult.Error != nil { - return nil, cardResult.Error + card, _ := p.GetRandomCard(ctx, player.GameId) + + playerCard := &repository.PlayerCards{ + PlayerId: player.Id, + CardId: card.Id, + Type: "hands", } - return player, cardResult.Error + result := p.db.Table("PlayerCards").Create(&playerCard) + + if result.Error != nil { + return nil, result.Error + } - // err := p.db.Table("players").Create(player).Error - // return player, err + return playerCard, nil } diff --git a/Backend/service/repository/playerRepository.go b/Backend/service/repository/playerRepository.go index 39a39a5..492f9f5 100644 --- a/Backend/service/repository/playerRepository.go +++ b/Backend/service/repository/playerRepository.go @@ -31,5 +31,7 @@ type PlayerCards struct { type PlayerRepository interface { CreatePlayer(ctx context.Context, player *Player) (*Player, error) - PlayerDrawCard(ctx context.Context, player *Player) (*Player, error) + GetPlayer(ctx context.Context, playerId int) (*Player, error) + GetRandomCard(ctx context.Context, gameId int) (*Card, error) + PlayerDrawCard(ctx context.Context, playerId int) (*PlayerCards, error) } diff --git a/playerCardsRepository.go b/playerCardsRepository.go deleted file mode 100644 index 9b96ab5..0000000 --- a/playerCardsRepository.go +++ /dev/null @@ -1,22 +0,0 @@ -package repository - -import ( - "time" - - "gorm.io/gorm" -) - -type playerCards struct { - gorm.Model - Player_id int `gorm:"primaryKey;auto_increment"` - Type string - PlayerId int `gorm:"foreignKey:PlayerId;references:Id"` - CardId int `gorm:"foreignKey:CardId;references:Id"` - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoCreateTime"` - DeletedAt gorm.DeletedAt -} - -type playerCardsRepository interface { - // InitialCard(ctx context.Context) (*Card, error) -} From 03afee2057b7ab7675b1a152c33ae3cb82929d82 Mon Sep 17 00:00:00 2001 From: KenLin Date: Sat, 28 Oct 2023 13:50:48 +0800 Subject: [PATCH 03/89] refactor: Unify file name naming rules --- .../service/repository/{cardRepository.go => card_repository.go} | 0 .../service/repository/{gameRepository.go => game_repository.go} | 0 .../mysql/{mysqlCardRepository.go => card_repository.go} | 0 .../mysql/{mysqlGameRepository.go => game_repository.go} | 0 .../mysql/{mysqlPlayerRepository.go => player_repository.go} | 0 .../repository/{playerRepository.go => player_repository.go} | 0 .../request/{CreateGameRequest.go => create_game_request.go} | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename Backend/service/repository/{cardRepository.go => card_repository.go} (100%) rename Backend/service/repository/{gameRepository.go => game_repository.go} (100%) rename Backend/service/repository/mysql/{mysqlCardRepository.go => card_repository.go} (100%) rename Backend/service/repository/mysql/{mysqlGameRepository.go => game_repository.go} (100%) rename Backend/service/repository/mysql/{mysqlPlayerRepository.go => player_repository.go} (100%) rename Backend/service/repository/{playerRepository.go => player_repository.go} (100%) rename Backend/service/request/{CreateGameRequest.go => create_game_request.go} (100%) diff --git a/Backend/service/repository/cardRepository.go b/Backend/service/repository/card_repository.go similarity index 100% rename from Backend/service/repository/cardRepository.go rename to Backend/service/repository/card_repository.go diff --git a/Backend/service/repository/gameRepository.go b/Backend/service/repository/game_repository.go similarity index 100% rename from Backend/service/repository/gameRepository.go rename to Backend/service/repository/game_repository.go diff --git a/Backend/service/repository/mysql/mysqlCardRepository.go b/Backend/service/repository/mysql/card_repository.go similarity index 100% rename from Backend/service/repository/mysql/mysqlCardRepository.go rename to Backend/service/repository/mysql/card_repository.go diff --git a/Backend/service/repository/mysql/mysqlGameRepository.go b/Backend/service/repository/mysql/game_repository.go similarity index 100% rename from Backend/service/repository/mysql/mysqlGameRepository.go rename to Backend/service/repository/mysql/game_repository.go diff --git a/Backend/service/repository/mysql/mysqlPlayerRepository.go b/Backend/service/repository/mysql/player_repository.go similarity index 100% rename from Backend/service/repository/mysql/mysqlPlayerRepository.go rename to Backend/service/repository/mysql/player_repository.go diff --git a/Backend/service/repository/playerRepository.go b/Backend/service/repository/player_repository.go similarity index 100% rename from Backend/service/repository/playerRepository.go rename to Backend/service/repository/player_repository.go diff --git a/Backend/service/request/CreateGameRequest.go b/Backend/service/request/create_game_request.go similarity index 100% rename from Backend/service/request/CreateGameRequest.go rename to Backend/service/request/create_game_request.go From a0b11830d0355e95ce50d7aa61c7efbb2801bc67 Mon Sep 17 00:00:00 2001 From: KenLin Date: Sat, 28 Oct 2023 14:51:57 +0800 Subject: [PATCH 04/89] feat: Add game cards seeder --- Backend/cmd/migrate/game_card_seeder.go | 43 +++++++++++++ Backend/enums/Message.go | 39 ----------- Backend/enums/game_cards.go | 13 ++++ .../{indentity.go => indentity_cards.go} | 0 .../service/delivery/http/v1/game_handler.go | 2 +- Backend/service/repository/card_repository.go | 13 ++-- .../repository/mysql/card_repository.go | 29 +++++---- .../repository/mysql/player_repository.go | 64 +++++++++---------- .../service/repository/player_repository.go | 4 +- 9 files changed, 114 insertions(+), 93 deletions(-) create mode 100644 Backend/cmd/migrate/game_card_seeder.go delete mode 100644 Backend/enums/Message.go create mode 100644 Backend/enums/game_cards.go rename Backend/enums/{indentity.go => indentity_cards.go} (100%) diff --git a/Backend/cmd/migrate/game_card_seeder.go b/Backend/cmd/migrate/game_card_seeder.go new file mode 100644 index 0000000..ffe228c --- /dev/null +++ b/Backend/cmd/migrate/game_card_seeder.go @@ -0,0 +1,43 @@ +//go:build migrate + +package main + +import ( + "context" + "github.com/Game-as-a-Service/The-Message/config" + "github.com/Game-as-a-Service/The-Message/enums" + "github.com/Game-as-a-Service/The-Message/service/repository" + "github.com/Game-as-a-Service/The-Message/service/repository/mysql" + _ "github.com/joho/godotenv/autoload" + "gorm.io/gorm" +) + +var actionColors = map[string]map[string]int{ + enums.LockOn: {"紅": 5, "藍": 5, "黑": 4}, + enums.LureAway: {"紅": 2, "藍": 2, "黑": 4}, + enums.Intercept: {"紅": 1, "藍": 1, "黑": 6}, + enums.Diversion: {"紅": 2, "藍": 2, "黑": 2}, + enums.Decipher: {"紅": 3, "藍": 3, "黑": 1}, + enums.Burn: {"紅": 1, "藍": 1, "黑": 4}, + enums.SeeThrough: {"紅": 3, "藍": 3, "黑": 6}, + enums.Probe: {"紅": 6, "藍": 6, "黑": 6}, + enums.BlurOfTruth: {"紅": 1, "藍": 1}, +} + +func main() { + db := config.InitDB() + SeederCards(db) +} + +func SeederCards(db *gorm.DB) { + for actionType, colors := range actionColors { + for color, count := range colors { + for i := 0; i < count; i++ { + mysql.NewCardRepository(db).CreateCard(context.TODO(), &repository.Card{ + Name: actionType, + Color: color, + }) + } + } + } +} diff --git a/Backend/enums/Message.go b/Backend/enums/Message.go deleted file mode 100644 index 0bdc592..0000000 --- a/Backend/enums/Message.go +++ /dev/null @@ -1,39 +0,0 @@ -package enums - -import ( - "github.com/Game-as-a-Service/The-Message/service/repository" -) - -func GetCards(gameId int) []*repository.Card { - - IsDrawed := 0 - - cards := []*repository.Card{} - cards = append(cards, &repository.Card{ - Id: 1, - Name: "Lock On", - Color: "Red", - IsDrawed: IsDrawed, - GameId: gameId, - }, &repository.Card{ - Id: 2, - Name: "Lock On", - Color: "Blue", - IsDrawed: IsDrawed, - GameId: gameId, - }, &repository.Card{ - Id: 3, - Name: "Lure Away", - Color: "Red", - IsDrawed: IsDrawed, - GameId: gameId, - }, &repository.Card{ - Id: 4, - Name: "Lure Away", - Color: "Red", - IsDrawed: IsDrawed, - GameId: gameId, - }) - - return cards -} diff --git a/Backend/enums/game_cards.go b/Backend/enums/game_cards.go new file mode 100644 index 0000000..d1edb18 --- /dev/null +++ b/Backend/enums/game_cards.go @@ -0,0 +1,13 @@ +package enums + +const ( + LockOn = "鎖定" + LureAway = "調虎離山" + Probe = "試探" + Intercept = "截獲" + Decipher = "破譯" + Diversion = "轉移" + Burn = "燒毀" + BlurOfTruth = "真偽莫辯" + SeeThrough = "識破" +) diff --git a/Backend/enums/indentity.go b/Backend/enums/indentity_cards.go similarity index 100% rename from Backend/enums/indentity.go rename to Backend/enums/indentity_cards.go diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 7e0e59f..1166562 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -77,7 +77,7 @@ func (g *GameHandler) StartGame(c *gin.Context) { } // 情報(功能)排堆建立 - _, err = g.CardRepo.InitialCard(c, game.Id) + //_, err = g.CardRepo.InitialCard(c, game.Id) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) diff --git a/Backend/service/repository/card_repository.go b/Backend/service/repository/card_repository.go index 26f18f7..efc5b26 100644 --- a/Backend/service/repository/card_repository.go +++ b/Backend/service/repository/card_repository.go @@ -9,16 +9,17 @@ import ( type Card struct { gorm.Model - Id int `gorm:"primaryKey;auto_increment"` - Name string - Color string - IsDrawed int - GameId int `gorm:"foreignKey:GameId;references:Id"` + Id int `gorm:"primaryKey;auto_increment"` + Name string + Color string + //IsDrawn int + //GameId int `gorm:"foreignKey:GameId;references:Id"` CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoCreateTime"` DeletedAt gorm.DeletedAt } type CardRepository interface { - InitialCard(ctx context.Context, gameId int) ([]*Card, error) + GetCardById(ctx context.Context, id int) (*Card, error) + CreateCard(ctx context.Context, card *Card) (*Card, error) } diff --git a/Backend/service/repository/mysql/card_repository.go b/Backend/service/repository/mysql/card_repository.go index 6ba206a..4307fdc 100644 --- a/Backend/service/repository/mysql/card_repository.go +++ b/Backend/service/repository/mysql/card_repository.go @@ -2,11 +2,7 @@ package mysql import ( "context" - "fmt" - - "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" - "gorm.io/gorm" ) @@ -20,18 +16,25 @@ func NewCardRepository(db *gorm.DB) *CardRepository { } } -func (c *CardRepository) InitialCard(ctx context.Context, gameId int) ([]*repository.Card, error) { +func (c *CardRepository) GetCardById(ctx context.Context, id int) (*repository.Card, error) { + card := new(repository.Card) + + result := c.db.Table("cards").First(card, "id = ?", id) + + if result.Error != nil { + return nil, result.Error + } + + return card, nil +} - cards := enums.GetCards(gameId) +func (c *CardRepository) CreateCard(ctx context.Context, card *repository.Card) (*repository.Card, error) { - var err error + result := c.db.Table("cards").Create(card) - for _, card := range cards { - err := c.db.Table("cards").Create(&card).Error - if err != nil { - fmt.Println("Error creating card:", err) - } + if result.Error != nil { + return nil, result.Error } - return cards, err + return card, nil } diff --git a/Backend/service/repository/mysql/player_repository.go b/Backend/service/repository/mysql/player_repository.go index e90d046..f3acb73 100644 --- a/Backend/service/repository/mysql/player_repository.go +++ b/Backend/service/repository/mysql/player_repository.go @@ -34,35 +34,35 @@ func (p *PlayerRepository) GetPlayer(ctx context.Context, playerId int) (*reposi return player, nil } -func (p *PlayerRepository) GetRandomCard(ctx context.Context, gameId int) (*repository.Card, error) { - card := new(repository.Card) - - result := p.db.Order("RAND()").Where(&repository.Card{GameId: gameId}).First(card) - - if result.Error != nil { - return nil, result.Error - } - - return card, nil -} - -func (p *PlayerRepository) PlayerDrawCard(ctx context.Context, playerId int) (*repository.PlayerCards, error) { - - player, _ := p.GetPlayer(ctx, playerId) - - card, _ := p.GetRandomCard(ctx, player.GameId) - - playerCard := &repository.PlayerCards{ - PlayerId: player.Id, - CardId: card.Id, - Type: "hands", - } - - result := p.db.Table("PlayerCards").Create(&playerCard) - - if result.Error != nil { - return nil, result.Error - } - - return playerCard, nil -} +//func (p *PlayerRepository) GetRandomCard(ctx context.Context, gameId int) (*repository.Card, error) { +// card := new(repository.Card) +// +// result := p.db.Order("RAND()").Where(&repository.Card{GameId: gameId}).First(card) +// +// if result.Error != nil { +// return nil, result.Error +// } +// +// return card, nil +//} + +//func (p *PlayerRepository) PlayerDrawCard(ctx context.Context, playerId int) (*repository.PlayerCards, error) { +// +// player, _ := p.GetPlayer(ctx, playerId) +// +// card, _ := p.GetRandomCard(ctx, player.GameId) +// +// playerCard := &repository.PlayerCards{ +// PlayerId: player.Id, +// CardId: card.Id, +// Type: "hands", +// } +// +// result := p.db.Table("PlayerCards").Create(&playerCard) +// +// if result.Error != nil { +// return nil, result.Error +// } +// +// return playerCard, nil +//} diff --git a/Backend/service/repository/player_repository.go b/Backend/service/repository/player_repository.go index 492f9f5..a6d15cd 100644 --- a/Backend/service/repository/player_repository.go +++ b/Backend/service/repository/player_repository.go @@ -32,6 +32,6 @@ type PlayerCards struct { type PlayerRepository interface { CreatePlayer(ctx context.Context, player *Player) (*Player, error) GetPlayer(ctx context.Context, playerId int) (*Player, error) - GetRandomCard(ctx context.Context, gameId int) (*Card, error) - PlayerDrawCard(ctx context.Context, playerId int) (*PlayerCards, error) + //GetRandomCard(ctx context.Context, gameId int) (*Card, error) + //PlayerDrawCard(ctx context.Context, playerId int) (*PlayerCards, error) } From d07b5861f67e55c4caf4d0d5b0dd656a069363ff Mon Sep 17 00:00:00 2001 From: KenLin Date: Sat, 28 Oct 2023 15:18:36 +0800 Subject: [PATCH 05/89] refactor: Unify receiver name of game repository --- Backend/service/repository/mysql/game_repository.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Backend/service/repository/mysql/game_repository.go b/Backend/service/repository/mysql/game_repository.go index d01a073..40ed294 100644 --- a/Backend/service/repository/mysql/game_repository.go +++ b/Backend/service/repository/mysql/game_repository.go @@ -17,10 +17,10 @@ func NewGameRepository(db *gorm.DB) *GameRepository { } } -func (p *GameRepository) GetGameById(ctx context.Context, id int) (*repository.Game, error) { +func (g *GameRepository) GetGameById(ctx context.Context, id int) (*repository.Game, error) { game := new(repository.Game) - result := p.db.Table("games").First(game, "id = ?", id) + result := g.db.Table("games").First(game, "id = ?", id) if result.Error != nil { return nil, result.Error @@ -29,9 +29,9 @@ func (p *GameRepository) GetGameById(ctx context.Context, id int) (*repository.G return game, nil } -func (p *GameRepository) CreateGame(ctx context.Context, game *repository.Game) (*repository.Game, error) { +func (g *GameRepository) CreateGame(ctx context.Context, game *repository.Game) (*repository.Game, error) { - result := p.db.Table("games").Create(game) + result := g.db.Table("games").Create(game) if result.Error != nil { return nil, result.Error @@ -40,10 +40,10 @@ func (p *GameRepository) CreateGame(ctx context.Context, game *repository.Game) return game, nil } -func (p *GameRepository) DeleteGame(ctx context.Context, id int) error { +func (g *GameRepository) DeleteGame(ctx context.Context, id int) error { game := new(repository.Game) - result := p.db.Table("games").Delete(game, "id = ?", id) + result := g.db.Table("games").Delete(game, "id = ?", id) if result.Error != nil { return result.Error From 45d3a24e8943af556537abb4fbd36cc794970001 Mon Sep 17 00:00:00 2001 From: KenLin Date: Sat, 28 Oct 2023 18:03:43 +0800 Subject: [PATCH 06/89] feat: Add implementation to initialize the deck --- Backend/cmd/app/main.go | 3 +- Backend/cmd/migrate/migrate.go | 8 +++ Backend/enums/indentity_cards.go | 2 +- .../service/delivery/http/v1/game_handler.go | 26 +++++---- Backend/service/repository/card_repository.go | 9 ++- Backend/service/repository/deck_repository.go | 23 ++++++++ .../repository/mysql/card_repository.go | 12 ++++ .../repository/mysql/deck_repository.go | 51 +++++++++++++++++ .../repository/mysql/player_repository.go | 49 ++++++----------- .../service/repository/player_repository.go | 4 +- Backend/service/service/gameService.go | 33 ----------- Backend/service/service/game_service.go | 55 +++++++++++++++++++ Backend/tests/acceptance/game_test.go | 3 +- Backend/tests/e2e/game_api_test.go | 3 +- 14 files changed, 195 insertions(+), 86 deletions(-) create mode 100644 Backend/service/repository/deck_repository.go create mode 100644 Backend/service/repository/mysql/deck_repository.go delete mode 100644 Backend/service/service/gameService.go create mode 100644 Backend/service/service/game_service.go diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index cd0e4c4..1f45ef1 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -17,8 +17,9 @@ func main() { gameRepo := mysqlRepo.NewGameRepository(db) playerRepo := mysqlRepo.NewPlayerRepository(db) cardRepo := mysqlRepo.NewCardRepository(db) + deckRepo := mysqlRepo.NewDeckRepository(db) - http.NewGameHandler(engine, gameRepo, playerRepo, cardRepo) + http.NewGameHandler(engine, gameRepo, playerRepo, cardRepo, deckRepo) engine.Run(":8080") } diff --git a/Backend/cmd/migrate/migrate.go b/Backend/cmd/migrate/migrate.go index 4e73579..6b9e8a9 100644 --- a/Backend/cmd/migrate/migrate.go +++ b/Backend/cmd/migrate/migrate.go @@ -27,6 +27,10 @@ func MigrationMysql(db *gorm.DB) { if err != nil { return } + err = db.AutoMigrate(&repository.Deck{}) + if err != nil { + return + } err = db.AutoMigrate(&repository.PlayerCards{}) if err != nil { return @@ -46,6 +50,10 @@ func Migration(db *gorm.DB) { if err != nil { return } + err = db.AutoMigrate(&repository.Deck{}) + if err != nil { + return + } err = db.AutoMigrate(&repository.PlayerCards{}) if err != nil { return diff --git a/Backend/enums/indentity_cards.go b/Backend/enums/indentity_cards.go index 991c87c..db2a3d6 100644 --- a/Backend/enums/indentity_cards.go +++ b/Backend/enums/indentity_cards.go @@ -2,6 +2,6 @@ package enums const ( UndercoverFront = "潛伏戰線" - MilitaryIntel = "軍情" + MilitaryAgency = "軍情處" Bystander = "打醬油" ) diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 1166562..a1f9618 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -17,13 +17,21 @@ type GameHandler struct { GameRepo repository.GameRepository PlayerRepo repository.PlayerRepository CardRepo repository.CardRepository + DeckRepo repository.DeckRepository } -func NewGameHandler(engine *gin.Engine, gameRepo *mysqlRepo.GameRepository, playerRepo *mysqlRepo.PlayerRepository, cardRepo *mysqlRepo.CardRepository) *GameHandler { +func NewGameHandler( + engine *gin.Engine, + gameRepo *mysqlRepo.GameRepository, + playerRepo *mysqlRepo.PlayerRepository, + cardRepo *mysqlRepo.CardRepository, + deckRepo *mysqlRepo.DeckRepository, +) *GameHandler { handler := &GameHandler{ GameRepo: gameRepo, PlayerRepo: playerRepo, CardRepo: cardRepo, + DeckRepo: deckRepo, } engine.POST("/api/v1/games", handler.StartGame) engine.Static("/swagger", "./web/swagger-ui") @@ -61,28 +69,24 @@ func (g *GameHandler) StartGame(c *gin.Context) { hashString := hex.EncodeToString(hash[:]) game.Token = hashString - // 建置遊戲 game, err := g.GameRepo.CreateGame(c, game) - // 建立身份牌牌堆 identityCards := service.InitIdentityCards(len(req.Players)) - // 傳入玩家 for i, reqPlayer := range req.Players { player := new(repository.Player) player.Name = reqPlayer.Name player.GameId = game.Id player.IdentityCard = identityCards[i] _, err = g.PlayerRepo.CreatePlayer(c, player) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } } - // 情報(功能)排堆建立 - //_, err = g.CardRepo.InitialCard(c, game.Id) - - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } + cards, err := g.CardRepo.Get(c) + service.InitialDeck(game.Id, cards, g.DeckRepo) c.JSON(http.StatusOK, gin.H{ "Id": game.Id, diff --git a/Backend/service/repository/card_repository.go b/Backend/service/repository/card_repository.go index efc5b26..679e454 100644 --- a/Backend/service/repository/card_repository.go +++ b/Backend/service/repository/card_repository.go @@ -9,11 +9,9 @@ import ( type Card struct { gorm.Model - Id int `gorm:"primaryKey;auto_increment"` - Name string - Color string - //IsDrawn int - //GameId int `gorm:"foreignKey:GameId;references:Id"` + Id int `gorm:"primaryKey;auto_increment"` + Name string + Color string CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoCreateTime"` DeletedAt gorm.DeletedAt @@ -22,4 +20,5 @@ type Card struct { type CardRepository interface { GetCardById(ctx context.Context, id int) (*Card, error) CreateCard(ctx context.Context, card *Card) (*Card, error) + Get(ctx context.Context) ([]*Card, error) } diff --git a/Backend/service/repository/deck_repository.go b/Backend/service/repository/deck_repository.go new file mode 100644 index 0000000..5e57d23 --- /dev/null +++ b/Backend/service/repository/deck_repository.go @@ -0,0 +1,23 @@ +package repository + +import ( + "context" + "gorm.io/gorm" + "time" +) + +type Deck struct { + gorm.Model + Id int `gorm:"primaryKey;auto_increment"` + GameId int + CardId int + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoCreateTime"` + DeletedAt gorm.DeletedAt +} + +type DeckRepository interface { + GetDeckById(ctx context.Context, id int) (*Deck, error) + Get(ctx context.Context) ([]*Deck, error) + CreateDeck(ctx context.Context, deck *Deck) (*Deck, error) +} diff --git a/Backend/service/repository/mysql/card_repository.go b/Backend/service/repository/mysql/card_repository.go index 4307fdc..ae04303 100644 --- a/Backend/service/repository/mysql/card_repository.go +++ b/Backend/service/repository/mysql/card_repository.go @@ -38,3 +38,15 @@ func (c *CardRepository) CreateCard(ctx context.Context, card *repository.Card) return card, nil } + +func (c *CardRepository) Get(ctx context.Context) ([]*repository.Card, error) { + var cards []*repository.Card + + result := c.db.Table("cards").Find(&cards) + + if result.Error != nil { + return nil, result.Error + } + + return cards, nil +} diff --git a/Backend/service/repository/mysql/deck_repository.go b/Backend/service/repository/mysql/deck_repository.go new file mode 100644 index 0000000..838a3c6 --- /dev/null +++ b/Backend/service/repository/mysql/deck_repository.go @@ -0,0 +1,51 @@ +package mysql + +import ( + "context" + "github.com/Game-as-a-Service/The-Message/service/repository" + "gorm.io/gorm" +) + +type DeckRepository struct { + db *gorm.DB +} + +func NewDeckRepository(db *gorm.DB) *DeckRepository { + return &DeckRepository{ + db: db, + } +} + +func (d *DeckRepository) GetDeckById(ctx context.Context, id int) (*repository.Deck, error) { + deck := new(repository.Deck) + + result := d.db.Table("decks").First(deck, "id = ?", id) + + if result.Error != nil { + return nil, result.Error + } + + return deck, nil +} + +func (d *DeckRepository) Get(ctx context.Context) ([]*repository.Deck, error) { + var decks []*repository.Deck + + result := d.db.Table("decks").Find(&decks) + + if result.Error != nil { + return nil, result.Error + } + + return decks, nil +} + +func (d *DeckRepository) CreateDeck(ctx context.Context, deck *repository.Deck) (*repository.Deck, error) { + result := d.db.Table("decks").Create(deck) + + if result.Error != nil { + return nil, result.Error + } + + return deck, nil +} diff --git a/Backend/service/repository/mysql/player_repository.go b/Backend/service/repository/mysql/player_repository.go index f3acb73..f50f912 100644 --- a/Backend/service/repository/mysql/player_repository.go +++ b/Backend/service/repository/mysql/player_repository.go @@ -34,35 +34,22 @@ func (p *PlayerRepository) GetPlayer(ctx context.Context, playerId int) (*reposi return player, nil } -//func (p *PlayerRepository) GetRandomCard(ctx context.Context, gameId int) (*repository.Card, error) { -// card := new(repository.Card) -// -// result := p.db.Order("RAND()").Where(&repository.Card{GameId: gameId}).First(card) -// -// if result.Error != nil { -// return nil, result.Error -// } -// -// return card, nil -//} +func (p *PlayerRepository) GetPlayerById(ctx context.Context, id int) (*repository.Player, error) { + player := new(repository.Player) + + result := p.db.Table("players").First(player, "id = ?", id) + + if result.Error != nil { + return nil, result.Error + } -//func (p *PlayerRepository) PlayerDrawCard(ctx context.Context, playerId int) (*repository.PlayerCards, error) { -// -// player, _ := p.GetPlayer(ctx, playerId) -// -// card, _ := p.GetRandomCard(ctx, player.GameId) -// -// playerCard := &repository.PlayerCards{ -// PlayerId: player.Id, -// CardId: card.Id, -// Type: "hands", -// } -// -// result := p.db.Table("PlayerCards").Create(&playerCard) -// -// if result.Error != nil { -// return nil, result.Error -// } -// -// return playerCard, nil -//} + return player, nil +} + +func (p *PlayerRepository) GetPlayerWithCardById(ctx context.Context, id int) (*repository.Player, error) { + var player repository.Player + if err := p.db.Preload("Cards").First(&player, id).Error; err != nil { + return nil, err + } + return &player, nil +} diff --git a/Backend/service/repository/player_repository.go b/Backend/service/repository/player_repository.go index a6d15cd..43df130 100644 --- a/Backend/service/repository/player_repository.go +++ b/Backend/service/repository/player_repository.go @@ -32,6 +32,6 @@ type PlayerCards struct { type PlayerRepository interface { CreatePlayer(ctx context.Context, player *Player) (*Player, error) GetPlayer(ctx context.Context, playerId int) (*Player, error) - //GetRandomCard(ctx context.Context, gameId int) (*Card, error) - //PlayerDrawCard(ctx context.Context, playerId int) (*PlayerCards, error) + // GetRandomCard(ctx context.Context, gameId int) (*Card, error) + // PlayerDrawCard(ctx context.Context, playerId int) (*PlayerCards, error) } diff --git a/Backend/service/service/gameService.go b/Backend/service/service/gameService.go deleted file mode 100644 index 07b705f..0000000 --- a/Backend/service/service/gameService.go +++ /dev/null @@ -1,33 +0,0 @@ -package service - -import ( - "math/rand" - - "github.com/Game-as-a-Service/The-Message/enums" - "github.com/Game-as-a-Service/The-Message/service/repository" -) - -type GameService struct { - GameRepo repository.GameRepository - PlayerRepo repository.PlayerRepository -} - -func InitIdentityCards(count int) []string { - identityCards := make([]string, count) - // if count == 3 had 1 UndercoverFront, 1 MilitaryIntel, 1 Bystander - if count == 3 { - identityCards[0] = enums.UndercoverFront - identityCards[1] = enums.MilitaryIntel - identityCards[2] = enums.Bystander - } - identityCards = shuffle(identityCards) - return identityCards -} - -func shuffle(cards []string) []string { - shuffledCards := make([]string, len(cards)) - for i, j := range rand.Perm(len(cards)) { - shuffledCards[i] = cards[j] - } - return shuffledCards -} diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go new file mode 100644 index 0000000..ceb7408 --- /dev/null +++ b/Backend/service/service/game_service.go @@ -0,0 +1,55 @@ +package service + +import ( + "context" + "fmt" + "github.com/Game-as-a-Service/The-Message/enums" + "github.com/Game-as-a-Service/The-Message/service/repository" + "math/rand" + "time" +) + +func InitialDeck(gameId int, cards []*repository.Card, deckRepo repository.DeckRepository) { + // 初始化隨機數生成器 + rand.New(rand.NewSource(time.Now().UnixNano())) + + // 隨機排序 + rand.Shuffle(len(cards), func(i, j int) { + cards[i], cards[j] = cards[j], cards[i] + }) + dd(cards) + + for _, card := range cards { + deck := new(repository.Deck) + deck.GameId = gameId + deck.CardId = card.Id + _, err := deckRepo.CreateDeck(context.TODO(), deck) + if err != nil { + return + } + } +} + +func dd(x interface{}) { + fmt.Printf("%+v\n", x) +} + +func InitIdentityCards(count int) []string { + identityCards := make([]string, count) + + if count == 3 { + identityCards[0] = enums.UndercoverFront + identityCards[1] = enums.MilitaryAgency + identityCards[2] = enums.Bystander + } + identityCards = shuffle(identityCards) + return identityCards +} + +func shuffle(cards []string) []string { + shuffledCards := make([]string, len(cards)) + for i, j := range rand.Perm(len(cards)) { + shuffledCards[i] = cards[j] + } + return shuffledCards +} diff --git a/Backend/tests/acceptance/game_test.go b/Backend/tests/acceptance/game_test.go index 604c3ce..decc66f 100644 --- a/Backend/tests/acceptance/game_test.go +++ b/Backend/tests/acceptance/game_test.go @@ -28,8 +28,9 @@ func TestMain(m *testing.M) { gameRepo = mysqlRepo.NewGameRepository(testDB) playerRepo := mysqlRepo.NewPlayerRepository(testDB) cardRepo := mysqlRepo.NewCardRepository(testDB) + deckRepo := mysqlRepo.NewDeckRepository(testDB) - handler.NewGameHandler(engine, gameRepo, playerRepo, cardRepo) + handler.NewGameHandler(engine, gameRepo, playerRepo, cardRepo, deckRepo) server := httptest.NewServer(engine) serverURL = server.URL diff --git a/Backend/tests/e2e/game_api_test.go b/Backend/tests/e2e/game_api_test.go index edd06a5..281a053 100644 --- a/Backend/tests/e2e/game_api_test.go +++ b/Backend/tests/e2e/game_api_test.go @@ -29,8 +29,9 @@ func TestMain(m *testing.M) { gameRepo = mysqlRepo.NewGameRepository(testDB) playerRepo := mysqlRepo.NewPlayerRepository(testDB) cardRepo := mysqlRepo.NewCardRepository(testDB) + deckRepo := mysqlRepo.NewDeckRepository(testDB) - handler.NewGameHandler(engine, gameRepo, playerRepo, cardRepo) + handler.NewGameHandler(engine, gameRepo, playerRepo, cardRepo, deckRepo) server := httptest.NewServer(engine) serverURL = server.URL From 5906fc9ddb4b223522304f57f2ddc308c3a48edc Mon Sep 17 00:00:00 2001 From: KenLin Date: Sat, 28 Oct 2023 21:07:00 +0800 Subject: [PATCH 07/89] feat: Add deal card function after game initialization --- Backend/cmd/app/main.go | 8 ++- Backend/cmd/migrate/migrate.go | 4 +- .../service/delivery/http/v1/game_handler.go | 52 ++++++++++++--- Backend/service/repository/deck_repository.go | 2 + .../repository/mysql/deck_repository.go | 24 +++++++ .../mysql/player_card_repository.go | 64 +++++++++++++++++++ .../repository/mysql/player_repository.go | 12 ++-- .../repository/player_card_repository.go | 28 ++++++++ .../service/repository/player_repository.go | 16 +---- Backend/service/service/game_service.go | 22 +------ Backend/tests/acceptance/game_test.go | 3 +- Backend/tests/e2e/game_api_test.go | 11 +++- 12 files changed, 190 insertions(+), 56 deletions(-) create mode 100644 Backend/service/repository/mysql/player_card_repository.go create mode 100644 Backend/service/repository/player_card_repository.go diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index 1f45ef1..7335d9a 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -18,8 +18,12 @@ func main() { playerRepo := mysqlRepo.NewPlayerRepository(db) cardRepo := mysqlRepo.NewCardRepository(db) deckRepo := mysqlRepo.NewDeckRepository(db) + playerCardRepo := mysqlRepo.NewPlayerCardRepository(db) - http.NewGameHandler(engine, gameRepo, playerRepo, cardRepo, deckRepo) + http.NewGameHandler(engine, gameRepo, playerRepo, cardRepo, deckRepo, playerCardRepo) - engine.Run(":8080") + err := engine.Run(":8080") + if err != nil { + return + } } diff --git a/Backend/cmd/migrate/migrate.go b/Backend/cmd/migrate/migrate.go index 6b9e8a9..d76980b 100644 --- a/Backend/cmd/migrate/migrate.go +++ b/Backend/cmd/migrate/migrate.go @@ -31,7 +31,7 @@ func MigrationMysql(db *gorm.DB) { if err != nil { return } - err = db.AutoMigrate(&repository.PlayerCards{}) + err = db.AutoMigrate(&repository.PlayerCard{}) if err != nil { return } @@ -54,7 +54,7 @@ func Migration(db *gorm.DB) { if err != nil { return } - err = db.AutoMigrate(&repository.PlayerCards{}) + err = db.AutoMigrate(&repository.PlayerCard{}) if err != nil { return } diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index a1f9618..4d10b41 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -14,10 +14,11 @@ import ( ) type GameHandler struct { - GameRepo repository.GameRepository - PlayerRepo repository.PlayerRepository - CardRepo repository.CardRepository - DeckRepo repository.DeckRepository + GameRepo repository.GameRepository + PlayerRepo repository.PlayerRepository + CardRepo repository.CardRepository + DeckRepo repository.DeckRepository + PlayerCardRepo repository.PlayerCardRepository } func NewGameHandler( @@ -26,12 +27,14 @@ func NewGameHandler( playerRepo *mysqlRepo.PlayerRepository, cardRepo *mysqlRepo.CardRepository, deckRepo *mysqlRepo.DeckRepository, + playerCardRepo *mysqlRepo.PlayerCardRepository, ) *GameHandler { handler := &GameHandler{ - GameRepo: gameRepo, - PlayerRepo: playerRepo, - CardRepo: cardRepo, - DeckRepo: deckRepo, + GameRepo: gameRepo, + PlayerRepo: playerRepo, + CardRepo: cardRepo, + DeckRepo: deckRepo, + PlayerCardRepo: playerCardRepo, } engine.POST("/api/v1/games", handler.StartGame) engine.Static("/swagger", "./web/swagger-ui") @@ -72,7 +75,6 @@ func (g *GameHandler) StartGame(c *gin.Context) { game, err := g.GameRepo.CreateGame(c, game) identityCards := service.InitIdentityCards(len(req.Players)) - for i, reqPlayer := range req.Players { player := new(repository.Player) player.Name = reqPlayer.Name @@ -86,7 +88,37 @@ func (g *GameHandler) StartGame(c *gin.Context) { } cards, err := g.CardRepo.Get(c) - service.InitialDeck(game.Id, cards, g.DeckRepo) + cards = service.InitialDeck(game.Id, cards) + for _, card := range cards { + deck := new(repository.Deck) + deck.GameId = game.Id + deck.CardId = card.Id + _, err := g.DeckRepo.CreateDeck(c, deck) + if err != nil { + return + } + } + + players, err := g.PlayerRepo.GetPlayersByGameId(c, game.Id) + for _, player := range players { + drawCards, _ := g.DeckRepo.GetDecksByGameId(c, game.Id) + for i := 0; i < 3; i++ { + playerCards := new(repository.PlayerCard) + playerCards.GameId = game.Id + playerCards.PlayerId = player.Id + playerCards.CardId = drawCards[i].CardId + _, err := g.PlayerCardRepo.CreatePlayerCard(c, playerCards) + if err != nil { + return + } + // delete deck + err = g.DeckRepo.DeleteDeck(c, drawCards[i].Id) + if err != nil { + return + } + } + + } c.JSON(http.StatusOK, gin.H{ "Id": game.Id, diff --git a/Backend/service/repository/deck_repository.go b/Backend/service/repository/deck_repository.go index 5e57d23..9053949 100644 --- a/Backend/service/repository/deck_repository.go +++ b/Backend/service/repository/deck_repository.go @@ -20,4 +20,6 @@ type DeckRepository interface { GetDeckById(ctx context.Context, id int) (*Deck, error) Get(ctx context.Context) ([]*Deck, error) CreateDeck(ctx context.Context, deck *Deck) (*Deck, error) + GetDecksByGameId(ctx context.Context, id int) ([]*Deck, error) + DeleteDeck(ctx context.Context, id int) error } diff --git a/Backend/service/repository/mysql/deck_repository.go b/Backend/service/repository/mysql/deck_repository.go index 838a3c6..7e38b04 100644 --- a/Backend/service/repository/mysql/deck_repository.go +++ b/Backend/service/repository/mysql/deck_repository.go @@ -49,3 +49,27 @@ func (d *DeckRepository) CreateDeck(ctx context.Context, deck *repository.Deck) return deck, nil } + +func (d *DeckRepository) GetDecksByGameId(ctx context.Context, id int) ([]*repository.Deck, error) { + var decks []*repository.Deck + + result := d.db.Table("decks").Find(&decks, "game_id = ?", id) + + if result.Error != nil { + return nil, result.Error + } + + return decks, nil +} + +func (d *DeckRepository) DeleteDeck(ctx context.Context, id int) error { + deck := new(repository.Deck) + + result := d.db.Table("decks").Delete(deck, "id = ?", id) + + if result.Error != nil { + return result.Error + } + + return nil +} diff --git a/Backend/service/repository/mysql/player_card_repository.go b/Backend/service/repository/mysql/player_card_repository.go new file mode 100644 index 0000000..f698ec9 --- /dev/null +++ b/Backend/service/repository/mysql/player_card_repository.go @@ -0,0 +1,64 @@ +package mysql + +import ( + "context" + "github.com/Game-as-a-Service/The-Message/service/repository" + "gorm.io/gorm" +) + +type PlayerCardRepository struct { + db *gorm.DB +} + +func NewPlayerCardRepository(db *gorm.DB) *PlayerCardRepository { + return &PlayerCardRepository{ + db: db, + } +} + +func (p PlayerCardRepository) GetPlayerCardById(ctx context.Context, id int) (*repository.PlayerCard, error) { + card := new(repository.PlayerCard) + + result := p.db.Table("player_cards").First(card, "id = ?", id) + + if result.Error != nil { + return nil, result.Error + } + + return card, nil + +} + +func (p PlayerCardRepository) GetPlayerCardsByGameId(ctx context.Context, id int) ([]*repository.PlayerCard, error) { + var cards []*repository.PlayerCard + + result := p.db.Table("player_cards").Find(&cards, "game_id = ?", id) + + if result.Error != nil { + return nil, result.Error + } + + return cards, nil +} + +func (p PlayerCardRepository) CreatePlayerCard(ctx context.Context, card *repository.PlayerCard) (*repository.PlayerCard, error) { + result := p.db.Table("player_cards").Create(card) + + if result.Error != nil { + return nil, result.Error + } + + return card, nil +} + +func (p PlayerCardRepository) DeletePlayerCard(ctx context.Context, id int) error { + card := new(repository.PlayerCard) + + result := p.db.Table("player_cards").Delete(card, "id = ?", id) + + if result.Error != nil { + return result.Error + } + + return nil +} diff --git a/Backend/service/repository/mysql/player_repository.go b/Backend/service/repository/mysql/player_repository.go index f50f912..13adc3a 100644 --- a/Backend/service/repository/mysql/player_repository.go +++ b/Backend/service/repository/mysql/player_repository.go @@ -34,21 +34,21 @@ func (p *PlayerRepository) GetPlayer(ctx context.Context, playerId int) (*reposi return player, nil } -func (p *PlayerRepository) GetPlayerById(ctx context.Context, id int) (*repository.Player, error) { - player := new(repository.Player) +func (p *PlayerRepository) GetPlayersByGameId(ctx context.Context, id int) ([]*repository.Player, error) { + var players []*repository.Player - result := p.db.Table("players").First(player, "id = ?", id) + result := p.db.Table("players").Find(&players, "game_id = ?", id) if result.Error != nil { return nil, result.Error } - return player, nil + return players, nil } -func (p *PlayerRepository) GetPlayerWithCardById(ctx context.Context, id int) (*repository.Player, error) { +func (p *PlayerRepository) GetPlayerWithPlayerCards(ctx context.Context, playerId int) (*repository.Player, error) { var player repository.Player - if err := p.db.Preload("Cards").First(&player, id).Error; err != nil { + if err := p.db.Debug().Preload("PlayerCards").Preload("PlayerCards.Card").First(&player, playerId).Error; err != nil { return nil, err } return &player, nil diff --git a/Backend/service/repository/player_card_repository.go b/Backend/service/repository/player_card_repository.go new file mode 100644 index 0000000..04c7b0b --- /dev/null +++ b/Backend/service/repository/player_card_repository.go @@ -0,0 +1,28 @@ +package repository + +import ( + "context" + "gorm.io/gorm" + "time" +) + +type PlayerCard struct { + gorm.Model + Id int `gorm:"primaryKey;auto_increment"` + PlayerId int + GameId int + CardId int + Type string + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoCreateTime"` + DeletedAt gorm.DeletedAt + Card Card + Player Player +} + +type PlayerCardRepository interface { + GetPlayerCardById(ctx context.Context, id int) (*PlayerCard, error) + GetPlayerCardsByGameId(ctx context.Context, id int) ([]*PlayerCard, error) + CreatePlayerCard(ctx context.Context, card *PlayerCard) (*PlayerCard, error) + DeletePlayerCard(ctx context.Context, id int) error +} diff --git a/Backend/service/repository/player_repository.go b/Backend/service/repository/player_repository.go index 43df130..d5f963d 100644 --- a/Backend/service/repository/player_repository.go +++ b/Backend/service/repository/player_repository.go @@ -13,25 +13,15 @@ type Player struct { Name string GameId int `gorm:"foreignKey:GameId;references:Id"` IdentityCard string + PlayerCards []PlayerCard CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoCreateTime"` DeletedAt gorm.DeletedAt } -type PlayerCards struct { - gorm.Model - Id int `gorm:"primaryKey;auto_increment"` - Type string - PlayerId int `gorm:"foreignKey:PlayerId;references:Id"` - CardId int `gorm:"foreignKey:CardId;references:Id"` - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoCreateTime"` - DeletedAt gorm.DeletedAt -} - type PlayerRepository interface { CreatePlayer(ctx context.Context, player *Player) (*Player, error) GetPlayer(ctx context.Context, playerId int) (*Player, error) - // GetRandomCard(ctx context.Context, gameId int) (*Card, error) - // PlayerDrawCard(ctx context.Context, playerId int) (*PlayerCards, error) + GetPlayersByGameId(ctx context.Context, id int) ([]*Player, error) + GetPlayerWithPlayerCards(ctx context.Context, playerId int) (*Player, error) } diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index ceb7408..bd49fd2 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -1,37 +1,19 @@ package service import ( - "context" - "fmt" "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" "math/rand" "time" ) -func InitialDeck(gameId int, cards []*repository.Card, deckRepo repository.DeckRepository) { - // 初始化隨機數生成器 +func InitialDeck(gameId int, cards []*repository.Card) []*repository.Card { rand.New(rand.NewSource(time.Now().UnixNano())) - // 隨機排序 rand.Shuffle(len(cards), func(i, j int) { cards[i], cards[j] = cards[j], cards[i] }) - dd(cards) - - for _, card := range cards { - deck := new(repository.Deck) - deck.GameId = gameId - deck.CardId = card.Id - _, err := deckRepo.CreateDeck(context.TODO(), deck) - if err != nil { - return - } - } -} - -func dd(x interface{}) { - fmt.Printf("%+v\n", x) + return cards } func InitIdentityCards(count int) []string { diff --git a/Backend/tests/acceptance/game_test.go b/Backend/tests/acceptance/game_test.go index decc66f..c5f1e07 100644 --- a/Backend/tests/acceptance/game_test.go +++ b/Backend/tests/acceptance/game_test.go @@ -29,8 +29,9 @@ func TestMain(m *testing.M) { playerRepo := mysqlRepo.NewPlayerRepository(testDB) cardRepo := mysqlRepo.NewCardRepository(testDB) deckRepo := mysqlRepo.NewDeckRepository(testDB) + playerCardRepo := mysqlRepo.NewPlayerCardRepository(testDB) - handler.NewGameHandler(engine, gameRepo, playerRepo, cardRepo, deckRepo) + handler.NewGameHandler(engine, gameRepo, playerRepo, cardRepo, deckRepo, playerCardRepo) server := httptest.NewServer(engine) serverURL = server.URL diff --git a/Backend/tests/e2e/game_api_test.go b/Backend/tests/e2e/game_api_test.go index 281a053..8b68dd8 100644 --- a/Backend/tests/e2e/game_api_test.go +++ b/Backend/tests/e2e/game_api_test.go @@ -20,6 +20,7 @@ import ( var serverURL string var gameRepo *mysqlRepo.GameRepository +var playerRepo *mysqlRepo.PlayerRepository func TestMain(m *testing.M) { testDB := config.InitTestDB() @@ -27,11 +28,12 @@ func TestMain(m *testing.M) { engine := gin.Default() gameRepo = mysqlRepo.NewGameRepository(testDB) - playerRepo := mysqlRepo.NewPlayerRepository(testDB) + playerRepo = mysqlRepo.NewPlayerRepository(testDB) cardRepo := mysqlRepo.NewCardRepository(testDB) deckRepo := mysqlRepo.NewDeckRepository(testDB) + playerCardRepo := mysqlRepo.NewPlayerCardRepository(testDB) - handler.NewGameHandler(engine, gameRepo, playerRepo, cardRepo, deckRepo) + handler.NewGameHandler(engine, gameRepo, playerRepo, cardRepo, deckRepo, playerCardRepo) server := httptest.NewServer(engine) serverURL = server.URL @@ -70,6 +72,11 @@ func TestStartGameE2E(t *testing.T) { assert.NotEmpty(t, game.Players[0].IdentityCard) assert.NotEmpty(t, game.Players[1].IdentityCard) assert.NotEmpty(t, game.Players[2].IdentityCard) + + for _, player := range game.Players { + playerCards, _ := playerRepo.GetPlayerWithPlayerCards(context.TODO(), player.Id) + assert.NotEmpty(t, playerCards.PlayerCards) + } } // Helper functions From 79df523ab3e7289ee79bb7d87e89b2778cee8ee4 Mon Sep 17 00:00:00 2001 From: KenLin Date: Sat, 28 Oct 2023 21:11:27 +0800 Subject: [PATCH 08/89] chore: Update step to Run in Backend --- README.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a832182..50fee44 100644 --- a/README.md +++ b/README.md @@ -3,38 +3,43 @@ ## Step to Run in Backend -1. 進入 Backend 資料夾 +- 進入 Backend 資料夾 ```bash cd Backend ``` -2. docker 建置出 MySQL 環境 +- docker 建置出 MySQL 環境 ```bash docker-compose up -d ``` -3. 進行 DB 資料表清空與重新建置 +- 進行 DB 資料表清空與重新建置 ```bash go run ./cmd/migrate/fresh.go ``` -4. 進行 DB Migration +- 進行 DB Migration ```bash go run ./cmd/migrate/migrate.go ``` -5. 開啟 Go Web Server +- 進行 DB Seeder +```bash +go run ./cmd/migrate/game_card_seeder.go +``` + +- 開啟 Go Web Server ```bash go run cmd/app/main.go ``` -6. 進行第一個 Request 呼叫 +- 進行第一個 Request 呼叫 - 有兩種方式可以進行 Request 呼叫,如以下所示: - 1. 將 `The-Message.postman_collection.json` 檔匯入至 Postman 中,並執行 Collection 當中的 API。 + - 將 `The-Message.postman_collection.json` 檔匯入至 Postman 中,並執行 Collection 當中的 API。 - 2. 撰寫一個 HTTP Method 為 POST 的 Request,路徑為 `localhost:8080/api/v1/games`,並帶入以下玩家 JSON 資料。 + - 撰寫一個 HTTP Method 為 POST 的 Request,路徑為 `localhost:8080/api/v1/games`,並帶入以下玩家 JSON 資料。 ```json { "players": [{ From 9cfa5714ccd348486cb69a6f13a51c8ff915c8be Mon Sep 17 00:00:00 2001 From: KenLin Date: Sat, 28 Oct 2023 21:19:40 +0800 Subject: [PATCH 09/89] ci: fix the error caused by no data when executing acceptance test in pipeline --- .github/workflows/test-go-unit.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-go-unit.yml b/.github/workflows/test-go-unit.yml index c2e8666..7b093a1 100644 --- a/.github/workflows/test-go-unit.yml +++ b/.github/workflows/test-go-unit.yml @@ -85,7 +85,10 @@ jobs: - name: Migration working-directory: ./Backend - run: go run ./cmd/migrate/migrate.go + run: | + go run ./cmd/migrate/migrate.go + go run ./cmd/migrate/game_card_seeder.go + - name: 🎯 Acceptance test working-directory: ./Backend run: go test ./... -v -count=1 -coverprofile=coverage.out From c361f6e898bee951db3ee392bed11d6279b9e5d3 Mon Sep 17 00:00:00 2001 From: KenLin Date: Sat, 28 Oct 2023 22:54:16 +0800 Subject: [PATCH 10/89] Refactor: the incorrect function name and remove the unused function --- .../service/delivery/http/v1/game_handler.go | 3 ++- Backend/service/repository/card_repository.go | 2 +- Backend/service/repository/deck_repository.go | 2 -- .../repository/mysql/card_repository.go | 2 +- .../repository/mysql/deck_repository.go | 24 ------------------- 5 files changed, 4 insertions(+), 29 deletions(-) diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 4d10b41..06fdb92 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -87,7 +87,7 @@ func (g *GameHandler) StartGame(c *gin.Context) { } } - cards, err := g.CardRepo.Get(c) + cards, err := g.CardRepo.GetCards(c) cards = service.InitialDeck(game.Id, cards) for _, card := range cards { deck := new(repository.Deck) @@ -107,6 +107,7 @@ func (g *GameHandler) StartGame(c *gin.Context) { playerCards.GameId = game.Id playerCards.PlayerId = player.Id playerCards.CardId = drawCards[i].CardId + playerCards.Type = "hand" _, err := g.PlayerCardRepo.CreatePlayerCard(c, playerCards) if err != nil { return diff --git a/Backend/service/repository/card_repository.go b/Backend/service/repository/card_repository.go index 679e454..f4a92ba 100644 --- a/Backend/service/repository/card_repository.go +++ b/Backend/service/repository/card_repository.go @@ -20,5 +20,5 @@ type Card struct { type CardRepository interface { GetCardById(ctx context.Context, id int) (*Card, error) CreateCard(ctx context.Context, card *Card) (*Card, error) - Get(ctx context.Context) ([]*Card, error) + GetCards(ctx context.Context) ([]*Card, error) } diff --git a/Backend/service/repository/deck_repository.go b/Backend/service/repository/deck_repository.go index 9053949..b1a67f0 100644 --- a/Backend/service/repository/deck_repository.go +++ b/Backend/service/repository/deck_repository.go @@ -17,8 +17,6 @@ type Deck struct { } type DeckRepository interface { - GetDeckById(ctx context.Context, id int) (*Deck, error) - Get(ctx context.Context) ([]*Deck, error) CreateDeck(ctx context.Context, deck *Deck) (*Deck, error) GetDecksByGameId(ctx context.Context, id int) ([]*Deck, error) DeleteDeck(ctx context.Context, id int) error diff --git a/Backend/service/repository/mysql/card_repository.go b/Backend/service/repository/mysql/card_repository.go index ae04303..c88024b 100644 --- a/Backend/service/repository/mysql/card_repository.go +++ b/Backend/service/repository/mysql/card_repository.go @@ -39,7 +39,7 @@ func (c *CardRepository) CreateCard(ctx context.Context, card *repository.Card) return card, nil } -func (c *CardRepository) Get(ctx context.Context) ([]*repository.Card, error) { +func (c *CardRepository) GetCards(ctx context.Context) ([]*repository.Card, error) { var cards []*repository.Card result := c.db.Table("cards").Find(&cards) diff --git a/Backend/service/repository/mysql/deck_repository.go b/Backend/service/repository/mysql/deck_repository.go index 7e38b04..4c029a0 100644 --- a/Backend/service/repository/mysql/deck_repository.go +++ b/Backend/service/repository/mysql/deck_repository.go @@ -16,30 +16,6 @@ func NewDeckRepository(db *gorm.DB) *DeckRepository { } } -func (d *DeckRepository) GetDeckById(ctx context.Context, id int) (*repository.Deck, error) { - deck := new(repository.Deck) - - result := d.db.Table("decks").First(deck, "id = ?", id) - - if result.Error != nil { - return nil, result.Error - } - - return deck, nil -} - -func (d *DeckRepository) Get(ctx context.Context) ([]*repository.Deck, error) { - var decks []*repository.Deck - - result := d.db.Table("decks").Find(&decks) - - if result.Error != nil { - return nil, result.Error - } - - return decks, nil -} - func (d *DeckRepository) CreateDeck(ctx context.Context, deck *repository.Deck) (*repository.Deck, error) { result := d.db.Table("decks").Create(deck) From 9fd64c9a204163a02eab6f5bc869c8663dda2d2e Mon Sep 17 00:00:00 2001 From: KenLin Date: Mon, 30 Oct 2023 23:30:19 +0800 Subject: [PATCH 11/89] refactor: Using Dependency Injection to inject the service layer into the HTTP Handler, which has more flexibility to perform unit testing --- Backend/cmd/app/main.go | 18 ++- .../service/delivery/http/v1/game_handler.go | 60 ++++----- Backend/service/service/game_service.go | 118 +++++++++++++++++- Backend/tests/acceptance/game_test.go | 20 ++- Backend/tests/e2e/game_api_test.go | 21 +++- 5 files changed, 192 insertions(+), 45 deletions(-) diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index 7335d9a..4439f20 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -2,6 +2,7 @@ package main import ( "github.com/Game-as-a-Service/The-Message/config" + "github.com/Game-as-a-Service/The-Message/service/service" "github.com/gin-gonic/gin" http "github.com/Game-as-a-Service/The-Message/service/delivery/http/v1" @@ -20,7 +21,22 @@ func main() { deckRepo := mysqlRepo.NewDeckRepository(db) playerCardRepo := mysqlRepo.NewPlayerCardRepository(db) - http.NewGameHandler(engine, gameRepo, playerRepo, cardRepo, deckRepo, playerCardRepo) + gameService := service.NewGameService( + &service.GameServiceOptions{ + GameRepo: gameRepo, + PlayerRepo: playerRepo, + CardRepo: cardRepo, + DeckRepo: deckRepo, + PlayerCardRepo: playerCardRepo, + }, + ) + + http.RegisterGameHandler( + &http.GameHandlerOptions{ + Engine: engine, + Service: gameService, + }, + ) err := engine.Run(":8080") if err != nil { diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 06fdb92..d75b0c3 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -7,44 +7,33 @@ import ( "strconv" "github.com/Game-as-a-Service/The-Message/service/repository" - mysqlRepo "github.com/Game-as-a-Service/The-Message/service/repository/mysql" "github.com/Game-as-a-Service/The-Message/service/request" - service "github.com/Game-as-a-Service/The-Message/service/service" + "github.com/Game-as-a-Service/The-Message/service/service" "github.com/gin-gonic/gin" ) type GameHandler struct { - GameRepo repository.GameRepository - PlayerRepo repository.PlayerRepository - CardRepo repository.CardRepository - DeckRepo repository.DeckRepository - PlayerCardRepo repository.PlayerCardRepository + gameService service.GameService } -func NewGameHandler( - engine *gin.Engine, - gameRepo *mysqlRepo.GameRepository, - playerRepo *mysqlRepo.PlayerRepository, - cardRepo *mysqlRepo.CardRepository, - deckRepo *mysqlRepo.DeckRepository, - playerCardRepo *mysqlRepo.PlayerCardRepository, -) *GameHandler { +type GameHandlerOptions struct { + Engine *gin.Engine + Service service.GameService +} + +func RegisterGameHandler(opts *GameHandlerOptions) { handler := &GameHandler{ - GameRepo: gameRepo, - PlayerRepo: playerRepo, - CardRepo: cardRepo, - DeckRepo: deckRepo, - PlayerCardRepo: playerCardRepo, + gameService: opts.Service, } - engine.POST("/api/v1/games", handler.StartGame) - engine.Static("/swagger", "./web/swagger-ui") - return handler + + opts.Engine.POST("/api/v1/games", handler.StartGame) + opts.Engine.Static("/swagger", "./web/swagger-ui") } func (g *GameHandler) GetGame(c *gin.Context) { gameId, _ := strconv.Atoi(c.Param("gameId")) - game, err := g.GameRepo.GetGameById(c, gameId) + game, err := g.gameService.GetGameById(c, gameId) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) @@ -72,48 +61,49 @@ func (g *GameHandler) StartGame(c *gin.Context) { hashString := hex.EncodeToString(hash[:]) game.Token = hashString - game, err := g.GameRepo.CreateGame(c, game) + game, err := g.gameService.CreateGame(c, game) - identityCards := service.InitIdentityCards(len(req.Players)) + identityCards := g.gameService.InitIdentityCards(len(req.Players)) for i, reqPlayer := range req.Players { player := new(repository.Player) player.Name = reqPlayer.Name player.GameId = game.Id player.IdentityCard = identityCards[i] - _, err = g.PlayerRepo.CreatePlayer(c, player) + _, err = g.gameService.CreatePlayer(c, player) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } + } - cards, err := g.CardRepo.GetCards(c) - cards = service.InitialDeck(game.Id, cards) + cards, err := g.gameService.GetCards(c) + cards = g.gameService.InitialDeck(game.Id, cards) for _, card := range cards { deck := new(repository.Deck) deck.GameId = game.Id deck.CardId = card.Id - _, err := g.DeckRepo.CreateDeck(c, deck) + _, err := g.gameService.CreateDeck(c, deck) if err != nil { return } } - players, err := g.PlayerRepo.GetPlayersByGameId(c, game.Id) + players, err := g.gameService.GetPlayersByGameId(c, game.Id) for _, player := range players { - drawCards, _ := g.DeckRepo.GetDecksByGameId(c, game.Id) + drawCards, _ := g.gameService.GetDecksByGameId(c, game.Id) for i := 0; i < 3; i++ { playerCards := new(repository.PlayerCard) playerCards.GameId = game.Id playerCards.PlayerId = player.Id playerCards.CardId = drawCards[i].CardId playerCards.Type = "hand" - _, err := g.PlayerCardRepo.CreatePlayerCard(c, playerCards) + _, err := g.gameService.CreatePlayerCard(c, playerCards) if err != nil { return } // delete deck - err = g.DeckRepo.DeleteDeck(c, drawCards[i].Id) + err = g.gameService.DeleteDeck(c, drawCards[i].Id) if err != nil { return } @@ -130,7 +120,7 @@ func (g *GameHandler) StartGame(c *gin.Context) { func (g *GameHandler) DeleteGame(c *gin.Context) { gameId, _ := strconv.Atoi(c.Param("gameId")) - err := g.GameRepo.DeleteGame(c, gameId) + err := g.gameService.DeleteGame(c, gameId) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index bd49fd2..b862e18 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -3,11 +3,38 @@ package service import ( "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" + "github.com/gin-gonic/gin" "math/rand" "time" ) -func InitialDeck(gameId int, cards []*repository.Card) []*repository.Card { +type GameService struct { + GameRepo repository.GameRepository + PlayerRepo repository.PlayerRepository + CardRepo repository.CardRepository + DeckRepo repository.DeckRepository + PlayerCardRepo repository.PlayerCardRepository +} + +type GameServiceOptions struct { + GameRepo repository.GameRepository + PlayerRepo repository.PlayerRepository + CardRepo repository.CardRepository + DeckRepo repository.DeckRepository + PlayerCardRepo repository.PlayerCardRepository +} + +func NewGameService(opts *GameServiceOptions) GameService { + return GameService{ + GameRepo: opts.GameRepo, + PlayerRepo: opts.PlayerRepo, + CardRepo: opts.CardRepo, + DeckRepo: opts.DeckRepo, + PlayerCardRepo: opts.PlayerCardRepo, + } +} + +func (g *GameService) InitialDeck(gameId int, cards []*repository.Card) []*repository.Card { rand.New(rand.NewSource(time.Now().UnixNano())) rand.Shuffle(len(cards), func(i, j int) { @@ -16,7 +43,7 @@ func InitialDeck(gameId int, cards []*repository.Card) []*repository.Card { return cards } -func InitIdentityCards(count int) []string { +func (g *GameService) InitIdentityCards(count int) []string { identityCards := make([]string, count) if count == 3 { @@ -24,14 +51,97 @@ func InitIdentityCards(count int) []string { identityCards[1] = enums.MilitaryAgency identityCards[2] = enums.Bystander } - identityCards = shuffle(identityCards) + identityCards = g.shuffle(identityCards) return identityCards } -func shuffle(cards []string) []string { +func (g *GameService) shuffle(cards []string) []string { shuffledCards := make([]string, len(cards)) for i, j := range rand.Perm(len(cards)) { shuffledCards[i] = cards[j] } return shuffledCards } + +func (g *GameService) GetGameById(c *gin.Context, id int) (*repository.Game, error) { + game, err := g.GameRepo.GetGameById(c, id) + if err != nil { + return nil, err + } + return game, nil +} + +func (g *GameService) CreateGame(c *gin.Context, game *repository.Game) (*repository.Game, error) { + game, err := g.GameRepo.CreateGame(c, game) + if err != nil { + return nil, err + } + return game, nil +} + +func (g *GameService) CreatePlayer(c *gin.Context, player *repository.Player) (*repository.Player, error) { + player, err := g.PlayerRepo.CreatePlayer(c, player) + if err != nil { + return nil, err + } + return player, nil +} + +func (g *GameService) GetCards(c *gin.Context) ([]*repository.Card, error) { + cards, err := g.CardRepo.GetCards(c) + if err != nil { + return nil, err + } + return cards, nil +} + +func (g *GameService) CreateDeck(c *gin.Context, deck *repository.Deck) (*repository.Deck, error) { + deck, err := g.DeckRepo.CreateDeck(c, deck) + if err != nil { + return nil, err + } + return deck, nil +} + +func (g *GameService) GetPlayersByGameId(c *gin.Context, id int) ([]*repository.Player, error) { + players, err := g.PlayerRepo.GetPlayersByGameId(c, id) + if err != nil { + return nil, err + } + return players, nil +} + +func (g *GameService) GetDecksByGameId(c *gin.Context, id int) ([]*repository.Deck, error) { + decks, err := g.DeckRepo.GetDecksByGameId(c, id) + if err != nil { + return nil, err + } + return decks, nil + +} + +func (g *GameService) CreatePlayerCard(c *gin.Context, cards *repository.PlayerCard) (*repository.PlayerCard, error) { + cards, err := g.PlayerCardRepo.CreatePlayerCard(c, cards) + if err != nil { + return nil, err + } + return cards, nil + +} + +func (g *GameService) DeleteDeck(c *gin.Context, id int) error { + err := g.DeckRepo.DeleteDeck(c, id) + if err != nil { + return err + } + return nil +} + +func (g *GameService) DeleteGame(c *gin.Context, id int) error { + err := g.GameRepo.DeleteGame(c, id) + if err != nil { + return err + } + return nil + +} diff --git a/Backend/tests/acceptance/game_test.go b/Backend/tests/acceptance/game_test.go index c5f1e07..aa9ac37 100644 --- a/Backend/tests/acceptance/game_test.go +++ b/Backend/tests/acceptance/game_test.go @@ -4,6 +4,8 @@ import ( "bytes" "context" "encoding/json" + v1 "github.com/Game-as-a-Service/The-Message/service/delivery/http/v1" + "github.com/Game-as-a-Service/The-Message/service/service" "log" "net/http" "net/http/httptest" @@ -11,7 +13,6 @@ import ( "testing" "github.com/Game-as-a-Service/The-Message/config" - handler "github.com/Game-as-a-Service/The-Message/service/delivery/http/v1" mysqlRepo "github.com/Game-as-a-Service/The-Message/service/repository/mysql" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" @@ -31,7 +32,22 @@ func TestMain(m *testing.M) { deckRepo := mysqlRepo.NewDeckRepository(testDB) playerCardRepo := mysqlRepo.NewPlayerCardRepository(testDB) - handler.NewGameHandler(engine, gameRepo, playerRepo, cardRepo, deckRepo, playerCardRepo) + gameService := service.NewGameService( + &service.GameServiceOptions{ + GameRepo: gameRepo, + PlayerRepo: playerRepo, + CardRepo: cardRepo, + DeckRepo: deckRepo, + PlayerCardRepo: playerCardRepo, + }, + ) + + v1.RegisterGameHandler( + &v1.GameHandlerOptions{ + Engine: engine, + Service: gameService, + }, + ) server := httptest.NewServer(engine) serverURL = server.URL diff --git a/Backend/tests/e2e/game_api_test.go b/Backend/tests/e2e/game_api_test.go index 8b68dd8..19d589b 100644 --- a/Backend/tests/e2e/game_api_test.go +++ b/Backend/tests/e2e/game_api_test.go @@ -4,6 +4,8 @@ import ( "bytes" "context" "encoding/json" + v1 "github.com/Game-as-a-Service/The-Message/service/delivery/http/v1" + "github.com/Game-as-a-Service/The-Message/service/service" "log" "net/http" "net/http/httptest" @@ -11,8 +13,6 @@ import ( "testing" "github.com/Game-as-a-Service/The-Message/config" - handler "github.com/Game-as-a-Service/The-Message/service/delivery/http/v1" - mysqlRepo "github.com/Game-as-a-Service/The-Message/service/repository/mysql" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" @@ -33,7 +33,22 @@ func TestMain(m *testing.M) { deckRepo := mysqlRepo.NewDeckRepository(testDB) playerCardRepo := mysqlRepo.NewPlayerCardRepository(testDB) - handler.NewGameHandler(engine, gameRepo, playerRepo, cardRepo, deckRepo, playerCardRepo) + gameService := service.NewGameService( + &service.GameServiceOptions{ + GameRepo: gameRepo, + PlayerRepo: playerRepo, + CardRepo: cardRepo, + DeckRepo: deckRepo, + PlayerCardRepo: playerCardRepo, + }, + ) + + v1.RegisterGameHandler( + &v1.GameHandlerOptions{ + Engine: engine, + Service: gameService, + }, + ) server := httptest.NewServer(engine) serverURL = server.URL From 5821406df402aba7a4c5068858afaeaf07658cd5 Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 31 Oct 2023 00:56:16 +0800 Subject: [PATCH 12/89] refactor: The StartGame method and decompose its responsibilities into GameService --- .../service/delivery/http/v1/game_handler.go | 67 +++------- Backend/service/service/game_service.go | 116 ++++++++++++++++-- 2 files changed, 119 insertions(+), 64 deletions(-) diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index d75b0c3..5e559c3 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -1,12 +1,9 @@ package http import ( - "crypto/sha256" - "encoding/hex" "net/http" "strconv" - "github.com/Game-as-a-Service/The-Message/service/repository" "github.com/Game-as-a-Service/The-Message/service/request" "github.com/Game-as-a-Service/The-Message/service/service" "github.com/gin-gonic/gin" @@ -54,61 +51,25 @@ func (g *GameHandler) StartGame(c *gin.Context) { return } - game := new(repository.Game) - jwtToken := "the-message" // 先亂寫Token - jwtBytes := []byte(jwtToken) - hash := sha256.Sum256(jwtBytes) - hashString := hex.EncodeToString(hash[:]) - game.Token = hashString - - game, err := g.gameService.CreateGame(c, game) - - identityCards := g.gameService.InitIdentityCards(len(req.Players)) - for i, reqPlayer := range req.Players { - player := new(repository.Player) - player.Name = reqPlayer.Name - player.GameId = game.Id - player.IdentityCard = identityCards[i] - _, err = g.gameService.CreatePlayer(c, player) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - + game, err := g.gameService.InitGame(c) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return } - cards, err := g.gameService.GetCards(c) - cards = g.gameService.InitialDeck(game.Id, cards) - for _, card := range cards { - deck := new(repository.Deck) - deck.GameId = game.Id - deck.CardId = card.Id - _, err := g.gameService.CreateDeck(c, deck) - if err != nil { - return - } + if err := g.gameService.InitPlayers(c, game, req); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return } - players, err := g.gameService.GetPlayersByGameId(c, game.Id) - for _, player := range players { - drawCards, _ := g.gameService.GetDecksByGameId(c, game.Id) - for i := 0; i < 3; i++ { - playerCards := new(repository.PlayerCard) - playerCards.GameId = game.Id - playerCards.PlayerId = player.Id - playerCards.CardId = drawCards[i].CardId - playerCards.Type = "hand" - _, err := g.gameService.CreatePlayerCard(c, playerCards) - if err != nil { - return - } - // delete deck - err = g.gameService.DeleteDeck(c, drawCards[i].Id) - if err != nil { - return - } - } + if err := g.gameService.InitDeck(c, game); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + if err := g.gameService.DrawCardsForPlayers(c, game); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return } c.JSON(http.StatusOK, gin.H{ diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index b862e18..3df14c7 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -1,8 +1,12 @@ package service import ( + "context" + "crypto/sha256" + "encoding/hex" "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" + "github.com/Game-as-a-Service/The-Message/service/request" "github.com/gin-gonic/gin" "math/rand" "time" @@ -34,7 +38,7 @@ func NewGameService(opts *GameServiceOptions) GameService { } } -func (g *GameService) InitialDeck(gameId int, cards []*repository.Card) []*repository.Card { +func (g *GameService) ShuffleDeck(cards []*repository.Card) []*repository.Card { rand.New(rand.NewSource(time.Now().UnixNano())) rand.Shuffle(len(cards), func(i, j int) { @@ -63,7 +67,7 @@ func (g *GameService) shuffle(cards []string) []string { return shuffledCards } -func (g *GameService) GetGameById(c *gin.Context, id int) (*repository.Game, error) { +func (g *GameService) GetGameById(c context.Context, id int) (*repository.Game, error) { game, err := g.GameRepo.GetGameById(c, id) if err != nil { return nil, err @@ -71,7 +75,7 @@ func (g *GameService) GetGameById(c *gin.Context, id int) (*repository.Game, err return game, nil } -func (g *GameService) CreateGame(c *gin.Context, game *repository.Game) (*repository.Game, error) { +func (g *GameService) CreateGame(c context.Context, game *repository.Game) (*repository.Game, error) { game, err := g.GameRepo.CreateGame(c, game) if err != nil { return nil, err @@ -79,7 +83,7 @@ func (g *GameService) CreateGame(c *gin.Context, game *repository.Game) (*reposi return game, nil } -func (g *GameService) CreatePlayer(c *gin.Context, player *repository.Player) (*repository.Player, error) { +func (g *GameService) CreatePlayer(c context.Context, player *repository.Player) (*repository.Player, error) { player, err := g.PlayerRepo.CreatePlayer(c, player) if err != nil { return nil, err @@ -87,7 +91,7 @@ func (g *GameService) CreatePlayer(c *gin.Context, player *repository.Player) (* return player, nil } -func (g *GameService) GetCards(c *gin.Context) ([]*repository.Card, error) { +func (g *GameService) GetCards(c context.Context) ([]*repository.Card, error) { cards, err := g.CardRepo.GetCards(c) if err != nil { return nil, err @@ -95,7 +99,7 @@ func (g *GameService) GetCards(c *gin.Context) ([]*repository.Card, error) { return cards, nil } -func (g *GameService) CreateDeck(c *gin.Context, deck *repository.Deck) (*repository.Deck, error) { +func (g *GameService) CreateDeck(c context.Context, deck *repository.Deck) (*repository.Deck, error) { deck, err := g.DeckRepo.CreateDeck(c, deck) if err != nil { return nil, err @@ -103,7 +107,7 @@ func (g *GameService) CreateDeck(c *gin.Context, deck *repository.Deck) (*reposi return deck, nil } -func (g *GameService) GetPlayersByGameId(c *gin.Context, id int) ([]*repository.Player, error) { +func (g *GameService) GetPlayersByGameId(c context.Context, id int) ([]*repository.Player, error) { players, err := g.PlayerRepo.GetPlayersByGameId(c, id) if err != nil { return nil, err @@ -111,7 +115,7 @@ func (g *GameService) GetPlayersByGameId(c *gin.Context, id int) ([]*repository. return players, nil } -func (g *GameService) GetDecksByGameId(c *gin.Context, id int) ([]*repository.Deck, error) { +func (g *GameService) GetDecksByGameId(c context.Context, id int) ([]*repository.Deck, error) { decks, err := g.DeckRepo.GetDecksByGameId(c, id) if err != nil { return nil, err @@ -120,7 +124,7 @@ func (g *GameService) GetDecksByGameId(c *gin.Context, id int) ([]*repository.De } -func (g *GameService) CreatePlayerCard(c *gin.Context, cards *repository.PlayerCard) (*repository.PlayerCard, error) { +func (g *GameService) CreatePlayerCard(c context.Context, cards *repository.PlayerCard) (*repository.PlayerCard, error) { cards, err := g.PlayerCardRepo.CreatePlayerCard(c, cards) if err != nil { return nil, err @@ -129,7 +133,7 @@ func (g *GameService) CreatePlayerCard(c *gin.Context, cards *repository.PlayerC } -func (g *GameService) DeleteDeck(c *gin.Context, id int) error { +func (g *GameService) DeleteDeck(c context.Context, id int) error { err := g.DeckRepo.DeleteDeck(c, id) if err != nil { return err @@ -137,7 +141,7 @@ func (g *GameService) DeleteDeck(c *gin.Context, id int) error { return nil } -func (g *GameService) DeleteGame(c *gin.Context, id int) error { +func (g *GameService) DeleteGame(c context.Context, id int) error { err := g.GameRepo.DeleteGame(c, id) if err != nil { return err @@ -145,3 +149,93 @@ func (g *GameService) DeleteGame(c *gin.Context, id int) error { return nil } + +func (g *GameService) InitGame(c *gin.Context) (*repository.Game, error) { + game := new(repository.Game) + jwtToken := "the-message" // 先亂寫Token + jwtBytes := []byte(jwtToken) + hash := sha256.Sum256(jwtBytes) + hashString := hex.EncodeToString(hash[:]) + game.Token = hashString + + game, err := g.CreateGame(c, game) + if err != nil { + return nil, err + } + return game, nil +} + +func (g *GameService) InitPlayers(c *gin.Context, game *repository.Game, req request.CreateGameRequest) error { + identityCards := g.InitIdentityCards(len(req.Players)) + for i, reqPlayer := range req.Players { + player := new(repository.Player) + player.Name = reqPlayer.Name + player.GameId = game.Id + player.IdentityCard = identityCards[i] + _, err := g.CreatePlayer(c, player) + if err != nil { + return err + } + } + return nil +} + +func (g *GameService) InitDeck(c *gin.Context, game *repository.Game) error { + cards, err := g.GetCards(c) + if err != nil { + return err + } + + cards = g.ShuffleDeck(cards) + + var deck []*repository.Deck + + for _, card := range cards { + card, err := g.DeckRepo.CreateDeck(c, &repository.Deck{ + GameId: game.Id, + CardId: card.Id, + }) + if err != nil { + return err + } + deck = append(deck, card) + } + + return nil +} + +func (g *GameService) DrawCard(c *gin.Context, game *repository.Game, player *repository.Player, drawCards []*repository.Deck, count int) error { + for i := 0; i < count; i++ { + playerCards := new(repository.PlayerCard) + playerCards.GameId = game.Id + playerCards.PlayerId = player.Id + playerCards.CardId = drawCards[i].CardId + playerCards.Type = "hand" + _, err := g.CreatePlayerCard(c, playerCards) + if err != nil { + return err + } + // delete deck + err = g.DeleteDeck(c, drawCards[i].Id) + if err != nil { + return err + } + } + return nil +} + +func (g *GameService) DrawCardsForPlayers(c *gin.Context, game *repository.Game) error { + players, err := g.GetPlayersByGameId(c, game.Id) + if err != nil { + return err + } + for _, player := range players { + drawCards, _ := g.GetDecksByGameId(c, game.Id) + err2 := g.DrawCard(c, game, player, drawCards, 3) + if err2 != nil { + return err2 + } + + } + return nil +} From 0431f91e026cb5b97e5972ebfd0d5eda51360b58 Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 31 Oct 2023 11:05:02 +0800 Subject: [PATCH 13/89] refactor: The methods in GameService and delegate some of the responsibilities of player methods to other PlayerService --- Backend/cmd/app/main.go | 19 ++-- .../service/delivery/http/v1/game_handler.go | 2 +- .../mysql/player_card_repository.go | 1 - Backend/service/service/card_service.go | 1 + Backend/service/service/game_service.go | 105 +++++------------- Backend/service/service/player_service.go | 85 ++++++++++++++ Backend/tests/acceptance/game_test.go | 14 ++- Backend/tests/e2e/game_api_test.go | 14 ++- Backend/utills/response_writer.go | 1 - Backend/utils/response_writer.go | 1 + 10 files changed, 144 insertions(+), 99 deletions(-) create mode 100644 Backend/service/service/card_service.go create mode 100644 Backend/service/service/player_service.go delete mode 100644 Backend/utills/response_writer.go create mode 100644 Backend/utils/response_writer.go diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index 4439f20..e6d3335 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -2,11 +2,10 @@ package main import ( "github.com/Game-as-a-Service/The-Message/config" - "github.com/Game-as-a-Service/The-Message/service/service" - "github.com/gin-gonic/gin" - http "github.com/Game-as-a-Service/The-Message/service/delivery/http/v1" mysqlRepo "github.com/Game-as-a-Service/The-Message/service/repository/mysql" + "github.com/Game-as-a-Service/The-Message/service/service" + "github.com/gin-gonic/gin" _ "github.com/joho/godotenv/autoload" ) @@ -21,13 +20,17 @@ func main() { deckRepo := mysqlRepo.NewDeckRepository(db) playerCardRepo := mysqlRepo.NewPlayerCardRepository(db) + playerService := service.NewPlayerService(&service.PlayerServiceOptions{ + PlayerRepo: playerRepo, + PlayerCardRepo: playerCardRepo, + }) + gameService := service.NewGameService( &service.GameServiceOptions{ - GameRepo: gameRepo, - PlayerRepo: playerRepo, - CardRepo: cardRepo, - DeckRepo: deckRepo, - PlayerCardRepo: playerCardRepo, + GameRepo: gameRepo, + PlayerService: playerService, + CardRepo: cardRepo, + DeckRepo: deckRepo, }, ) diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 5e559c3..0b4058a 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -57,7 +57,7 @@ func (g *GameHandler) StartGame(c *gin.Context) { return } - if err := g.gameService.InitPlayers(c, game, req); err != nil { + if err := g.gameService.PlayerService.InitPlayers(c, game, req); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } diff --git a/Backend/service/repository/mysql/player_card_repository.go b/Backend/service/repository/mysql/player_card_repository.go index f698ec9..2d68a0f 100644 --- a/Backend/service/repository/mysql/player_card_repository.go +++ b/Backend/service/repository/mysql/player_card_repository.go @@ -26,7 +26,6 @@ func (p PlayerCardRepository) GetPlayerCardById(ctx context.Context, id int) (*r } return card, nil - } func (p PlayerCardRepository) GetPlayerCardsByGameId(ctx context.Context, id int) ([]*repository.PlayerCard, error) { diff --git a/Backend/service/service/card_service.go b/Backend/service/service/card_service.go new file mode 100644 index 0000000..6d43c33 --- /dev/null +++ b/Backend/service/service/card_service.go @@ -0,0 +1 @@ +package service diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index 3df14c7..99006f2 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -4,37 +4,32 @@ import ( "context" "crypto/sha256" "encoding/hex" - "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" - "github.com/Game-as-a-Service/The-Message/service/request" "github.com/gin-gonic/gin" "math/rand" "time" ) type GameService struct { - GameRepo repository.GameRepository - PlayerRepo repository.PlayerRepository - CardRepo repository.CardRepository - DeckRepo repository.DeckRepository - PlayerCardRepo repository.PlayerCardRepository + GameRepo repository.GameRepository + PlayerService PlayerService + CardRepo repository.CardRepository + DeckRepo repository.DeckRepository } type GameServiceOptions struct { - GameRepo repository.GameRepository - PlayerRepo repository.PlayerRepository - CardRepo repository.CardRepository - DeckRepo repository.DeckRepository - PlayerCardRepo repository.PlayerCardRepository + GameRepo repository.GameRepository + PlayerService PlayerService + CardRepo repository.CardRepository + DeckRepo repository.DeckRepository } func NewGameService(opts *GameServiceOptions) GameService { return GameService{ - GameRepo: opts.GameRepo, - PlayerRepo: opts.PlayerRepo, - CardRepo: opts.CardRepo, - DeckRepo: opts.DeckRepo, - PlayerCardRepo: opts.PlayerCardRepo, + GameRepo: opts.GameRepo, + PlayerService: opts.PlayerService, + CardRepo: opts.CardRepo, + DeckRepo: opts.DeckRepo, } } @@ -47,18 +42,6 @@ func (g *GameService) ShuffleDeck(cards []*repository.Card) []*repository.Card { return cards } -func (g *GameService) InitIdentityCards(count int) []string { - identityCards := make([]string, count) - - if count == 3 { - identityCards[0] = enums.UndercoverFront - identityCards[1] = enums.MilitaryAgency - identityCards[2] = enums.Bystander - } - identityCards = g.shuffle(identityCards) - return identityCards -} - func (g *GameService) shuffle(cards []string) []string { shuffledCards := make([]string, len(cards)) for i, j := range rand.Perm(len(cards)) { @@ -83,14 +66,6 @@ func (g *GameService) CreateGame(c context.Context, game *repository.Game) (*rep return game, nil } -func (g *GameService) CreatePlayer(c context.Context, player *repository.Player) (*repository.Player, error) { - player, err := g.PlayerRepo.CreatePlayer(c, player) - if err != nil { - return nil, err - } - return player, nil -} - func (g *GameService) GetCards(c context.Context) ([]*repository.Card, error) { cards, err := g.CardRepo.GetCards(c) if err != nil { @@ -107,14 +82,6 @@ func (g *GameService) CreateDeck(c context.Context, deck *repository.Deck) (*rep return deck, nil } -func (g *GameService) GetPlayersByGameId(c context.Context, id int) ([]*repository.Player, error) { - players, err := g.PlayerRepo.GetPlayersByGameId(c, id) - if err != nil { - return nil, err - } - return players, nil -} - func (g *GameService) GetDecksByGameId(c context.Context, id int) ([]*repository.Deck, error) { decks, err := g.DeckRepo.GetDecksByGameId(c, id) if err != nil { @@ -124,16 +91,7 @@ func (g *GameService) GetDecksByGameId(c context.Context, id int) ([]*repository } -func (g *GameService) CreatePlayerCard(c context.Context, cards *repository.PlayerCard) (*repository.PlayerCard, error) { - cards, err := g.PlayerCardRepo.CreatePlayerCard(c, cards) - if err != nil { - return nil, err - } - return cards, nil - -} - -func (g *GameService) DeleteDeck(c context.Context, id int) error { +func (g *GameService) DeleteDeckFromGame(c context.Context, id int) error { err := g.DeckRepo.DeleteDeck(c, id) if err != nil { return err @@ -165,21 +123,6 @@ func (g *GameService) InitGame(c *gin.Context) (*repository.Game, error) { return game, nil } -func (g *GameService) InitPlayers(c *gin.Context, game *repository.Game, req request.CreateGameRequest) error { - identityCards := g.InitIdentityCards(len(req.Players)) - for i, reqPlayer := range req.Players { - player := new(repository.Player) - player.Name = reqPlayer.Name - player.GameId = game.Id - player.IdentityCard = identityCards[i] - _, err := g.CreatePlayer(c, player) - if err != nil { - return err - } - } - return nil -} - func (g *GameService) InitDeck(c *gin.Context, game *repository.Game) error { cards, err := g.GetCards(c) if err != nil { @@ -206,17 +149,23 @@ func (g *GameService) InitDeck(c *gin.Context, game *repository.Game) error { func (g *GameService) DrawCard(c *gin.Context, game *repository.Game, player *repository.Player, drawCards []*repository.Deck, count int) error { for i := 0; i < count; i++ { - playerCards := new(repository.PlayerCard) - playerCards.GameId = game.Id - playerCards.PlayerId = player.Id - playerCards.CardId = drawCards[i].CardId - playerCards.Type = "hand" - _, err := g.CreatePlayerCard(c, playerCards) + //card := new(repository.PlayerCard) + //card.GameId = game.Id + //card.PlayerId = player.Id + //card.CardId = drawCards[i].CardId + //card.Type = "hand" + card := &repository.PlayerCard{ + GameId: game.Id, + PlayerId: player.Id, + CardId: drawCards[i].CardId, + Type: "hand", + } + err := g.PlayerService.CreatePlayerCard(c, card) if err != nil { return err } // delete deck - err = g.DeleteDeck(c, drawCards[i].Id) + err = g.DeleteDeckFromGame(c, drawCards[i].Id) if err != nil { return err } @@ -225,7 +174,7 @@ func (g *GameService) DrawCard(c *gin.Context, game *repository.Game, player *re } func (g *GameService) DrawCardsForPlayers(c *gin.Context, game *repository.Game) error { - players, err := g.GetPlayersByGameId(c, game.Id) + players, err := g.PlayerService.GetPlayersByGameId(c, game.Id) if err != nil { return err } diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go new file mode 100644 index 0000000..3040882 --- /dev/null +++ b/Backend/service/service/player_service.go @@ -0,0 +1,85 @@ +package service + +import ( + "context" + "github.com/Game-as-a-Service/The-Message/enums" + "github.com/Game-as-a-Service/The-Message/service/repository" + "github.com/Game-as-a-Service/The-Message/service/request" + "math/rand" +) + +type PlayerService struct { + PlayerRepo repository.PlayerRepository + PlayerCardRepo repository.PlayerCardRepository +} + +type PlayerServiceOptions struct { + PlayerRepo repository.PlayerRepository + PlayerCardRepo repository.PlayerCardRepository +} + +func NewPlayerService(opts *PlayerServiceOptions) PlayerService { + return PlayerService{ + PlayerRepo: opts.PlayerRepo, + PlayerCardRepo: opts.PlayerCardRepo, + } +} + +func (p *PlayerService) InitPlayers(c context.Context, game *repository.Game, req request.CreateGameRequest) error { + identityCards := p.InitIdentityCards(len(req.Players)) + for i, reqPlayer := range req.Players { + player := new(repository.Player) + player.Name = reqPlayer.Name + player.GameId = game.Id + player.IdentityCard = identityCards[i] + _, err := p.CreatePlayer(c, player) + if err != nil { + return err + } + } + return nil +} + +func (p *PlayerService) InitIdentityCards(playersCount int) []string { + identityCards := make([]string, playersCount) + + if playersCount == 3 { + identityCards[0] = enums.UndercoverFront + identityCards[1] = enums.MilitaryAgency + identityCards[2] = enums.Bystander + } + identityCards = p.ShuffleIdentityCards(identityCards) + return identityCards +} + +func (p *PlayerService) ShuffleIdentityCards(cards []string) []string { + shuffledCards := make([]string, len(cards)) + for i, j := range rand.Perm(len(cards)) { + shuffledCards[i] = cards[j] + } + return shuffledCards +} + +func (p *PlayerService) CreatePlayer(c context.Context, player *repository.Player) (*repository.Player, error) { + player, err := p.PlayerRepo.CreatePlayer(c, player) + if err != nil { + return nil, err + } + return player, nil +} + +func (p *PlayerService) GetPlayersByGameId(c context.Context, id int) ([]*repository.Player, error) { + players, err := p.PlayerRepo.GetPlayersByGameId(c, id) + if err != nil { + return nil, err + } + return players, nil +} + +func (p *PlayerService) CreatePlayerCard(c context.Context, card *repository.PlayerCard) error { + _, err := p.PlayerCardRepo.CreatePlayerCard(c, card) + if err != nil { + return err + } + return nil +} diff --git a/Backend/tests/acceptance/game_test.go b/Backend/tests/acceptance/game_test.go index aa9ac37..9e00120 100644 --- a/Backend/tests/acceptance/game_test.go +++ b/Backend/tests/acceptance/game_test.go @@ -32,13 +32,17 @@ func TestMain(m *testing.M) { deckRepo := mysqlRepo.NewDeckRepository(testDB) playerCardRepo := mysqlRepo.NewPlayerCardRepository(testDB) + playerService := service.NewPlayerService(&service.PlayerServiceOptions{ + PlayerRepo: playerRepo, + PlayerCardRepo: playerCardRepo, + }) + gameService := service.NewGameService( &service.GameServiceOptions{ - GameRepo: gameRepo, - PlayerRepo: playerRepo, - CardRepo: cardRepo, - DeckRepo: deckRepo, - PlayerCardRepo: playerCardRepo, + GameRepo: gameRepo, + PlayerService: playerService, + CardRepo: cardRepo, + DeckRepo: deckRepo, }, ) diff --git a/Backend/tests/e2e/game_api_test.go b/Backend/tests/e2e/game_api_test.go index 19d589b..b32e3d5 100644 --- a/Backend/tests/e2e/game_api_test.go +++ b/Backend/tests/e2e/game_api_test.go @@ -33,13 +33,17 @@ func TestMain(m *testing.M) { deckRepo := mysqlRepo.NewDeckRepository(testDB) playerCardRepo := mysqlRepo.NewPlayerCardRepository(testDB) + playerService := service.NewPlayerService(&service.PlayerServiceOptions{ + PlayerRepo: playerRepo, + PlayerCardRepo: playerCardRepo, + }) + gameService := service.NewGameService( &service.GameServiceOptions{ - GameRepo: gameRepo, - PlayerRepo: playerRepo, - CardRepo: cardRepo, - DeckRepo: deckRepo, - PlayerCardRepo: playerCardRepo, + GameRepo: gameRepo, + PlayerService: playerService, + CardRepo: cardRepo, + DeckRepo: deckRepo, }, ) diff --git a/Backend/utills/response_writer.go b/Backend/utills/response_writer.go deleted file mode 100644 index 7cbcaff..0000000 --- a/Backend/utills/response_writer.go +++ /dev/null @@ -1 +0,0 @@ -package utills diff --git a/Backend/utils/response_writer.go b/Backend/utils/response_writer.go new file mode 100644 index 0000000..d4b585b --- /dev/null +++ b/Backend/utils/response_writer.go @@ -0,0 +1 @@ +package utils From e2c191b7a9ccee614c5b441bfb52cada67fdb087 Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 31 Oct 2023 11:14:19 +0800 Subject: [PATCH 14/89] refactor: The methods in GameService and delegate some of the responsibilities of card methods to other CardService --- Backend/cmd/app/main.go | 6 +++++- Backend/service/service/card_service.go | 27 +++++++++++++++++++++++++ Backend/service/service/game_service.go | 24 ++++------------------ Backend/tests/acceptance/game_test.go | 6 +++++- Backend/tests/e2e/game_api_test.go | 6 +++++- 5 files changed, 46 insertions(+), 23 deletions(-) diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index e6d3335..a312607 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -20,6 +20,10 @@ func main() { deckRepo := mysqlRepo.NewDeckRepository(db) playerCardRepo := mysqlRepo.NewPlayerCardRepository(db) + cardService := service.NewCardService(&service.CardServiceOptions{ + CardRepo: cardRepo, + }) + playerService := service.NewPlayerService(&service.PlayerServiceOptions{ PlayerRepo: playerRepo, PlayerCardRepo: playerCardRepo, @@ -29,8 +33,8 @@ func main() { &service.GameServiceOptions{ GameRepo: gameRepo, PlayerService: playerService, - CardRepo: cardRepo, DeckRepo: deckRepo, + CardService: cardService, }, ) diff --git a/Backend/service/service/card_service.go b/Backend/service/service/card_service.go index 6d43c33..ac12776 100644 --- a/Backend/service/service/card_service.go +++ b/Backend/service/service/card_service.go @@ -1 +1,28 @@ package service + +import ( + "context" + "github.com/Game-as-a-Service/The-Message/service/repository" +) + +type CardService struct { + CardRepo repository.CardRepository +} + +type CardServiceOptions struct { + CardRepo repository.CardRepository +} + +func NewCardService(opts *CardServiceOptions) CardService { + return CardService{ + CardRepo: opts.CardRepo, + } +} + +func (c *CardService) GetCards(ctx context.Context) ([]*repository.Card, error) { + cards, err := c.CardRepo.GetCards(ctx) + if err != nil { + return nil, err + } + return cards, nil +} diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index 99006f2..0106c71 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -13,23 +13,23 @@ import ( type GameService struct { GameRepo repository.GameRepository PlayerService PlayerService - CardRepo repository.CardRepository DeckRepo repository.DeckRepository + CardService CardService } type GameServiceOptions struct { GameRepo repository.GameRepository PlayerService PlayerService - CardRepo repository.CardRepository DeckRepo repository.DeckRepository + CardService CardService } func NewGameService(opts *GameServiceOptions) GameService { return GameService{ GameRepo: opts.GameRepo, PlayerService: opts.PlayerService, - CardRepo: opts.CardRepo, DeckRepo: opts.DeckRepo, + CardService: opts.CardService, } } @@ -42,14 +42,6 @@ func (g *GameService) ShuffleDeck(cards []*repository.Card) []*repository.Card { return cards } -func (g *GameService) shuffle(cards []string) []string { - shuffledCards := make([]string, len(cards)) - for i, j := range rand.Perm(len(cards)) { - shuffledCards[i] = cards[j] - } - return shuffledCards -} - func (g *GameService) GetGameById(c context.Context, id int) (*repository.Game, error) { game, err := g.GameRepo.GetGameById(c, id) if err != nil { @@ -66,14 +58,6 @@ func (g *GameService) CreateGame(c context.Context, game *repository.Game) (*rep return game, nil } -func (g *GameService) GetCards(c context.Context) ([]*repository.Card, error) { - cards, err := g.CardRepo.GetCards(c) - if err != nil { - return nil, err - } - return cards, nil -} - func (g *GameService) CreateDeck(c context.Context, deck *repository.Deck) (*repository.Deck, error) { deck, err := g.DeckRepo.CreateDeck(c, deck) if err != nil { @@ -124,7 +108,7 @@ func (g *GameService) InitGame(c *gin.Context) (*repository.Game, error) { } func (g *GameService) InitDeck(c *gin.Context, game *repository.Game) error { - cards, err := g.GetCards(c) + cards, err := g.CardService.GetCards(c) if err != nil { return err } diff --git a/Backend/tests/acceptance/game_test.go b/Backend/tests/acceptance/game_test.go index 9e00120..c174b68 100644 --- a/Backend/tests/acceptance/game_test.go +++ b/Backend/tests/acceptance/game_test.go @@ -32,6 +32,10 @@ func TestMain(m *testing.M) { deckRepo := mysqlRepo.NewDeckRepository(testDB) playerCardRepo := mysqlRepo.NewPlayerCardRepository(testDB) + cardService := service.NewCardService(&service.CardServiceOptions{ + CardRepo: cardRepo, + }) + playerService := service.NewPlayerService(&service.PlayerServiceOptions{ PlayerRepo: playerRepo, PlayerCardRepo: playerCardRepo, @@ -41,8 +45,8 @@ func TestMain(m *testing.M) { &service.GameServiceOptions{ GameRepo: gameRepo, PlayerService: playerService, - CardRepo: cardRepo, DeckRepo: deckRepo, + CardService: cardService, }, ) diff --git a/Backend/tests/e2e/game_api_test.go b/Backend/tests/e2e/game_api_test.go index b32e3d5..4c2a26c 100644 --- a/Backend/tests/e2e/game_api_test.go +++ b/Backend/tests/e2e/game_api_test.go @@ -33,6 +33,10 @@ func TestMain(m *testing.M) { deckRepo := mysqlRepo.NewDeckRepository(testDB) playerCardRepo := mysqlRepo.NewPlayerCardRepository(testDB) + cardService := service.NewCardService(&service.CardServiceOptions{ + CardRepo: cardRepo, + }) + playerService := service.NewPlayerService(&service.PlayerServiceOptions{ PlayerRepo: playerRepo, PlayerCardRepo: playerCardRepo, @@ -42,8 +46,8 @@ func TestMain(m *testing.M) { &service.GameServiceOptions{ GameRepo: gameRepo, PlayerService: playerService, - CardRepo: cardRepo, DeckRepo: deckRepo, + CardService: cardService, }, ) From 182e3abb86e4a7d0c123e1f8be4eb7a1f7c8ed77 Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 31 Oct 2023 11:31:48 +0800 Subject: [PATCH 15/89] refactor: The methods in GameService and delegate some of the responsibilities of deck methods to other DeckService --- Backend/cmd/app/main.go | 7 +- Backend/service/service/deck_service.go | 83 ++++++++++++++++++++++ Backend/service/service/game_service.go | 91 +++++-------------------- Backend/tests/acceptance/game_test.go | 7 +- Backend/tests/e2e/game_api_test.go | 7 +- 5 files changed, 117 insertions(+), 78 deletions(-) create mode 100644 Backend/service/service/deck_service.go diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index a312607..ba63a76 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -24,6 +24,11 @@ func main() { CardRepo: cardRepo, }) + deckService := service.NewDeckService(&service.DeckServiceOptions{ + DeckRepo: deckRepo, + CardService: cardService, + }) + playerService := service.NewPlayerService(&service.PlayerServiceOptions{ PlayerRepo: playerRepo, PlayerCardRepo: playerCardRepo, @@ -33,8 +38,8 @@ func main() { &service.GameServiceOptions{ GameRepo: gameRepo, PlayerService: playerService, - DeckRepo: deckRepo, CardService: cardService, + DeckService: deckService, }, ) diff --git a/Backend/service/service/deck_service.go b/Backend/service/service/deck_service.go new file mode 100644 index 0000000..86389f3 --- /dev/null +++ b/Backend/service/service/deck_service.go @@ -0,0 +1,83 @@ +package service + +import ( + "context" + "github.com/Game-as-a-Service/The-Message/service/repository" + "math/rand" + "time" +) + +type DeckService struct { + CardService CardService + DeckRepo repository.DeckRepository +} + +type DeckServiceOptions struct { + CardService CardService + DeckRepo repository.DeckRepository +} + +func NewDeckService(opts *DeckServiceOptions) DeckService { + return DeckService{ + CardService: opts.CardService, + DeckRepo: opts.DeckRepo, + } +} + +func (d *DeckService) ShuffleDeck(cards []*repository.Card) []*repository.Card { + rand.New(rand.NewSource(time.Now().UnixNano())) + + rand.Shuffle(len(cards), func(i, j int) { + cards[i], cards[j] = cards[j], cards[i] + }) + return cards +} + +func (d *DeckService) CreateDeck(c context.Context, deck *repository.Deck) (*repository.Deck, error) { + deck, err := d.DeckRepo.CreateDeck(c, deck) + if err != nil { + return nil, err + } + return deck, nil +} + +func (d *DeckService) GetDecksByGameId(c context.Context, id int) ([]*repository.Deck, error) { + decks, err := d.DeckRepo.GetDecksByGameId(c, id) + if err != nil { + return nil, err + } + return decks, nil + +} + +func (d *DeckService) DeleteDeckFromGame(c context.Context, id int) error { + err := d.DeckRepo.DeleteDeck(c, id) + if err != nil { + return err + } + return nil +} + +func (d *DeckService) InitDeck(c context.Context, game *repository.Game) error { + cards, err := d.CardService.GetCards(c) + if err != nil { + return err + } + + cards = d.ShuffleDeck(cards) + + var deck []*repository.Deck + + for _, card := range cards { + card, err := d.DeckRepo.CreateDeck(c, &repository.Deck{ + GameId: game.Id, + CardId: card.Id, + }) + if err != nil { + return err + } + deck = append(deck, card) + } + + return nil +} diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index 0106c71..9d5cf0a 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -5,43 +5,31 @@ import ( "crypto/sha256" "encoding/hex" "github.com/Game-as-a-Service/The-Message/service/repository" - "github.com/gin-gonic/gin" - "math/rand" - "time" ) type GameService struct { GameRepo repository.GameRepository PlayerService PlayerService - DeckRepo repository.DeckRepository CardService CardService + DeckService DeckService } type GameServiceOptions struct { GameRepo repository.GameRepository PlayerService PlayerService - DeckRepo repository.DeckRepository CardService CardService + DeckService DeckService } func NewGameService(opts *GameServiceOptions) GameService { return GameService{ GameRepo: opts.GameRepo, PlayerService: opts.PlayerService, - DeckRepo: opts.DeckRepo, CardService: opts.CardService, + DeckService: opts.DeckService, } } -func (g *GameService) ShuffleDeck(cards []*repository.Card) []*repository.Card { - rand.New(rand.NewSource(time.Now().UnixNano())) - - rand.Shuffle(len(cards), func(i, j int) { - cards[i], cards[j] = cards[j], cards[i] - }) - return cards -} - func (g *GameService) GetGameById(c context.Context, id int) (*repository.Game, error) { game, err := g.GameRepo.GetGameById(c, id) if err != nil { @@ -58,31 +46,6 @@ func (g *GameService) CreateGame(c context.Context, game *repository.Game) (*rep return game, nil } -func (g *GameService) CreateDeck(c context.Context, deck *repository.Deck) (*repository.Deck, error) { - deck, err := g.DeckRepo.CreateDeck(c, deck) - if err != nil { - return nil, err - } - return deck, nil -} - -func (g *GameService) GetDecksByGameId(c context.Context, id int) ([]*repository.Deck, error) { - decks, err := g.DeckRepo.GetDecksByGameId(c, id) - if err != nil { - return nil, err - } - return decks, nil - -} - -func (g *GameService) DeleteDeckFromGame(c context.Context, id int) error { - err := g.DeckRepo.DeleteDeck(c, id) - if err != nil { - return err - } - return nil -} - func (g *GameService) DeleteGame(c context.Context, id int) error { err := g.GameRepo.DeleteGame(c, id) if err != nil { @@ -92,7 +55,7 @@ func (g *GameService) DeleteGame(c context.Context, id int) error { } -func (g *GameService) InitGame(c *gin.Context) (*repository.Game, error) { +func (g *GameService) InitGame(c context.Context) (*repository.Game, error) { game := new(repository.Game) jwtToken := "the-message" // 先亂寫Token jwtBytes := []byte(jwtToken) @@ -107,37 +70,8 @@ func (g *GameService) InitGame(c *gin.Context) (*repository.Game, error) { return game, nil } -func (g *GameService) InitDeck(c *gin.Context, game *repository.Game) error { - cards, err := g.CardService.GetCards(c) - if err != nil { - return err - } - - cards = g.ShuffleDeck(cards) - - var deck []*repository.Deck - - for _, card := range cards { - card, err := g.DeckRepo.CreateDeck(c, &repository.Deck{ - GameId: game.Id, - CardId: card.Id, - }) - if err != nil { - return err - } - deck = append(deck, card) - } - - return nil -} - -func (g *GameService) DrawCard(c *gin.Context, game *repository.Game, player *repository.Player, drawCards []*repository.Deck, count int) error { +func (g *GameService) DrawCard(c context.Context, game *repository.Game, player *repository.Player, drawCards []*repository.Deck, count int) error { for i := 0; i < count; i++ { - //card := new(repository.PlayerCard) - //card.GameId = game.Id - //card.PlayerId = player.Id - //card.CardId = drawCards[i].CardId - //card.Type = "hand" card := &repository.PlayerCard{ GameId: game.Id, PlayerId: player.Id, @@ -148,8 +82,7 @@ func (g *GameService) DrawCard(c *gin.Context, game *repository.Game, player *re if err != nil { return err } - // delete deck - err = g.DeleteDeckFromGame(c, drawCards[i].Id) + err = g.DeckService.DeleteDeckFromGame(c, drawCards[i].Id) if err != nil { return err } @@ -157,13 +90,13 @@ func (g *GameService) DrawCard(c *gin.Context, game *repository.Game, player *re return nil } -func (g *GameService) DrawCardsForPlayers(c *gin.Context, game *repository.Game) error { +func (g *GameService) DrawCardsForPlayers(c context.Context, game *repository.Game) error { players, err := g.PlayerService.GetPlayersByGameId(c, game.Id) if err != nil { return err } for _, player := range players { - drawCards, _ := g.GetDecksByGameId(c, game.Id) + drawCards, _ := g.DeckService.GetDecksByGameId(c, game.Id) err2 := g.DrawCard(c, game, player, drawCards, 3) if err2 != nil { return err2 @@ -172,3 +105,11 @@ func (g *GameService) DrawCardsForPlayers(c *gin.Context, game *repository.Game) } return nil } + +func (g *GameService) InitDeck(c context.Context, game *repository.Game) error { + err := g.DeckService.InitDeck(c, game) + if err != nil { + return err + } + return nil +} diff --git a/Backend/tests/acceptance/game_test.go b/Backend/tests/acceptance/game_test.go index c174b68..b6e20a7 100644 --- a/Backend/tests/acceptance/game_test.go +++ b/Backend/tests/acceptance/game_test.go @@ -36,6 +36,11 @@ func TestMain(m *testing.M) { CardRepo: cardRepo, }) + deckService := service.NewDeckService(&service.DeckServiceOptions{ + DeckRepo: deckRepo, + CardService: cardService, + }) + playerService := service.NewPlayerService(&service.PlayerServiceOptions{ PlayerRepo: playerRepo, PlayerCardRepo: playerCardRepo, @@ -45,8 +50,8 @@ func TestMain(m *testing.M) { &service.GameServiceOptions{ GameRepo: gameRepo, PlayerService: playerService, - DeckRepo: deckRepo, CardService: cardService, + DeckService: deckService, }, ) diff --git a/Backend/tests/e2e/game_api_test.go b/Backend/tests/e2e/game_api_test.go index 4c2a26c..10c19b4 100644 --- a/Backend/tests/e2e/game_api_test.go +++ b/Backend/tests/e2e/game_api_test.go @@ -37,6 +37,11 @@ func TestMain(m *testing.M) { CardRepo: cardRepo, }) + deckService := service.NewDeckService(&service.DeckServiceOptions{ + DeckRepo: deckRepo, + CardService: cardService, + }) + playerService := service.NewPlayerService(&service.PlayerServiceOptions{ PlayerRepo: playerRepo, PlayerCardRepo: playerCardRepo, @@ -46,8 +51,8 @@ func TestMain(m *testing.M) { &service.GameServiceOptions{ GameRepo: gameRepo, PlayerService: playerService, - DeckRepo: deckRepo, CardService: cardService, + DeckService: deckService, }, ) From 77e3a97499c7fd966d28c5b5042a8c48e4aeeb01 Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 31 Oct 2023 11:46:03 +0800 Subject: [PATCH 16/89] rewrite: Game initialization to get Token method --- Backend/service/service/game_service.go | 24 +++++++++++++++-------- Backend/service/service/player_service.go | 10 +++++----- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index 9d5cf0a..3ad3b74 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -2,7 +2,7 @@ package service import ( "context" - "crypto/sha256" + "crypto/rand" "encoding/hex" "github.com/Game-as-a-Service/The-Message/service/repository" ) @@ -56,14 +56,14 @@ func (g *GameService) DeleteGame(c context.Context, id int) error { } func (g *GameService) InitGame(c context.Context) (*repository.Game, error) { - game := new(repository.Game) - jwtToken := "the-message" // 先亂寫Token - jwtBytes := []byte(jwtToken) - hash := sha256.Sum256(jwtBytes) - hashString := hex.EncodeToString(hash[:]) - game.Token = hashString + token, err := g.GenerateSecureToken(256) + if err != nil { + return nil, err + } - game, err := g.CreateGame(c, game) + game, err := g.CreateGame(c, &repository.Game{ + Token: token, + }) if err != nil { return nil, err } @@ -113,3 +113,11 @@ func (g *GameService) InitDeck(c context.Context, game *repository.Game) error { } return nil } + +func (g *GameService) GenerateSecureToken(n int) (string, error) { + bytes := make([]byte, n) + if _, err := rand.Read(bytes); err != nil { + return "", err + } + return hex.EncodeToString(bytes), nil +} diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 3040882..cbb8ee6 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -28,11 +28,11 @@ func NewPlayerService(opts *PlayerServiceOptions) PlayerService { func (p *PlayerService) InitPlayers(c context.Context, game *repository.Game, req request.CreateGameRequest) error { identityCards := p.InitIdentityCards(len(req.Players)) for i, reqPlayer := range req.Players { - player := new(repository.Player) - player.Name = reqPlayer.Name - player.GameId = game.Id - player.IdentityCard = identityCards[i] - _, err := p.CreatePlayer(c, player) + _, err := p.CreatePlayer(c, &repository.Player{ + Name: reqPlayer.Name, + GameId: game.Id, + IdentityCard: identityCards[i], + }) if err != nil { return err } From 5e3c82aa340352e73c807ddb182b93da35ebd2eb Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 31 Oct 2023 11:54:34 +0800 Subject: [PATCH 17/89] refactor: Clearly name the game as Player Draw Card --- Backend/service/delivery/http/v1/game_handler.go | 2 +- Backend/service/service/game_service.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 0b4058a..076ac02 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -67,7 +67,7 @@ func (g *GameHandler) StartGame(c *gin.Context) { return } - if err := g.gameService.DrawCardsForPlayers(c, game); err != nil { + if err := g.gameService.DrawCardsForAllPlayers(c, game); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index 3ad3b74..f2df746 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -90,7 +90,7 @@ func (g *GameService) DrawCard(c context.Context, game *repository.Game, player return nil } -func (g *GameService) DrawCardsForPlayers(c context.Context, game *repository.Game) error { +func (g *GameService) DrawCardsForAllPlayers(c context.Context, game *repository.Game) error { players, err := g.PlayerService.GetPlayersByGameId(c, game.Id) if err != nil { return err From e7e05a7e33d85af1ad0900d5529c2c91e6fb9633 Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 31 Oct 2023 14:28:51 +0800 Subject: [PATCH 18/89] refactor: Adjust the order of functions --- Backend/service/service/deck_service.go | 48 +++++++++---------- Backend/service/service/game_service.go | 64 ++++++++++++------------- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/Backend/service/service/deck_service.go b/Backend/service/service/deck_service.go index 86389f3..f5ee452 100644 --- a/Backend/service/service/deck_service.go +++ b/Backend/service/service/deck_service.go @@ -24,6 +24,30 @@ func NewDeckService(opts *DeckServiceOptions) DeckService { } } +func (d *DeckService) InitDeck(c context.Context, game *repository.Game) error { + cards, err := d.CardService.GetCards(c) + if err != nil { + return err + } + + cards = d.ShuffleDeck(cards) + + var deck []*repository.Deck + + for _, card := range cards { + card, err := d.DeckRepo.CreateDeck(c, &repository.Deck{ + GameId: game.Id, + CardId: card.Id, + }) + if err != nil { + return err + } + deck = append(deck, card) + } + + return nil +} + func (d *DeckService) ShuffleDeck(cards []*repository.Card) []*repository.Card { rand.New(rand.NewSource(time.Now().UnixNano())) @@ -57,27 +81,3 @@ func (d *DeckService) DeleteDeckFromGame(c context.Context, id int) error { } return nil } - -func (d *DeckService) InitDeck(c context.Context, game *repository.Game) error { - cards, err := d.CardService.GetCards(c) - if err != nil { - return err - } - - cards = d.ShuffleDeck(cards) - - var deck []*repository.Deck - - for _, card := range cards { - card, err := d.DeckRepo.CreateDeck(c, &repository.Deck{ - GameId: game.Id, - CardId: card.Id, - }) - if err != nil { - return err - } - deck = append(deck, card) - } - - return nil -} diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index f2df746..d01f9d3 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -30,44 +30,27 @@ func NewGameService(opts *GameServiceOptions) GameService { } } -func (g *GameService) GetGameById(c context.Context, id int) (*repository.Game, error) { - game, err := g.GameRepo.GetGameById(c, id) +func (g *GameService) InitGame(c context.Context) (*repository.Game, error) { + token, err := g.GenerateSecureToken(256) if err != nil { return nil, err } - return game, nil -} -func (g *GameService) CreateGame(c context.Context, game *repository.Game) (*repository.Game, error) { - game, err := g.GameRepo.CreateGame(c, game) + game, err := g.CreateGame(c, &repository.Game{ + Token: token, + }) if err != nil { return nil, err } return game, nil } -func (g *GameService) DeleteGame(c context.Context, id int) error { - err := g.GameRepo.DeleteGame(c, id) +func (g *GameService) InitDeck(c context.Context, game *repository.Game) error { + err := g.DeckService.InitDeck(c, game) if err != nil { return err } return nil - -} - -func (g *GameService) InitGame(c context.Context) (*repository.Game, error) { - token, err := g.GenerateSecureToken(256) - if err != nil { - return nil, err - } - - game, err := g.CreateGame(c, &repository.Game{ - Token: token, - }) - if err != nil { - return nil, err - } - return game, nil } func (g *GameService) DrawCard(c context.Context, game *repository.Game, player *repository.Player, drawCards []*repository.Deck, count int) error { @@ -106,14 +89,6 @@ func (g *GameService) DrawCardsForAllPlayers(c context.Context, game *repository return nil } -func (g *GameService) InitDeck(c context.Context, game *repository.Game) error { - err := g.DeckService.InitDeck(c, game) - if err != nil { - return err - } - return nil -} - func (g *GameService) GenerateSecureToken(n int) (string, error) { bytes := make([]byte, n) if _, err := rand.Read(bytes); err != nil { @@ -121,3 +96,28 @@ func (g *GameService) GenerateSecureToken(n int) (string, error) { } return hex.EncodeToString(bytes), nil } + +func (g *GameService) CreateGame(c context.Context, game *repository.Game) (*repository.Game, error) { + game, err := g.GameRepo.CreateGame(c, game) + if err != nil { + return nil, err + } + return game, nil +} + +func (g *GameService) GetGameById(c context.Context, id int) (*repository.Game, error) { + game, err := g.GameRepo.GetGameById(c, id) + if err != nil { + return nil, err + } + return game, nil +} + +func (g *GameService) DeleteGame(c context.Context, id int) error { + err := g.GameRepo.DeleteGame(c, id) + if err != nil { + return err + } + return nil + +} From 6d2bf61f4a9cf406a48051b73be565d84b438054 Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 31 Oct 2023 17:10:31 +0800 Subject: [PATCH 19/89] infra: Generate basic Swagger API file --- Backend/cmd/app/main.go | 7 ++ Backend/go.mod | 26 ++++- Backend/go.sum | 104 +++++++++++++++--- .../service/delivery/http/v1/game_handler.go | 42 ++++--- .../service/request/create_game_request.go | 5 + README.md | 5 + 6 files changed, 151 insertions(+), 38 deletions(-) diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index ba63a76..b6003b9 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -1,14 +1,20 @@ package main import ( + _ "github.com/Game-as-a-Service/The-Message/cmd/app/docs" "github.com/Game-as-a-Service/The-Message/config" http "github.com/Game-as-a-Service/The-Message/service/delivery/http/v1" mysqlRepo "github.com/Game-as-a-Service/The-Message/service/repository/mysql" "github.com/Game-as-a-Service/The-Message/service/service" "github.com/gin-gonic/gin" _ "github.com/joho/godotenv/autoload" + swaggerFiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" ) +// @title The Message API +// @description This is an online version of the "The Message" board game backend API +// @host 127.0.0.1:8080 func main() { db := config.InitDB() @@ -49,6 +55,7 @@ func main() { Service: gameService, }, ) + engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) err := engine.Run(":8080") if err != nil { diff --git a/Backend/go.mod b/Backend/go.mod index 61572ce..512795c 100644 --- a/Backend/go.mod +++ b/Backend/go.mod @@ -6,28 +6,41 @@ require ( github.com/gin-gonic/gin v1.9.1 github.com/joho/godotenv v1.5.1 github.com/stretchr/testify v1.8.3 + github.com/swaggo/files v1.0.1 + github.com/swaggo/gin-swagger v1.6.0 + github.com/swaggo/swag v1.16.2 + github.com/swaggo/swag/example/celler v0.0.0-20231012023037-2b5852a16877 gorm.io/driver/mysql v1.5.1 gorm.io/gorm v1.25.4 ) require ( + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/bytedance/sonic v1.9.1 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/spec v0.20.4 // indirect + github.com/go-openapi/swag v0.19.15 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/gofrs/uuid v4.2.0+incompatible // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/kr/pretty v0.3.1 // indirect github.com/leodido/go-urn v1.2.4 // indirect + github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -36,11 +49,12 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.9.0 // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + golang.org/x/tools v0.7.0 // indirect google.golang.org/protobuf v1.30.0 // indirect - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/Backend/go.sum b/Backend/go.sum index d33d26e..2d6804d 100644 --- a/Backend/go.sum +++ b/Backend/go.sum @@ -1,3 +1,9 @@ +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= @@ -10,10 +16,22 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= +github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -27,6 +45,8 @@ github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrt github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= +github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= @@ -38,17 +58,24 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -56,17 +83,17 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -74,31 +101,78 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= +github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= +github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= +github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= +github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04= +github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E= +github.com/swaggo/swag/example/celler v0.0.0-20231012023037-2b5852a16877 h1:GLD+cKjytGsoUokQnFxV88eBtbIyKmoTNP6O40KvjHk= +github.com/swaggo/swag/example/celler v0.0.0-20231012023037-2b5852a16877/go.mod h1:AKDmkSEqXqsZMqOXNXs53EZsD9D/SlZYxxNq2JZkoAw= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw= diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 076ac02..29e3fa7 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -24,25 +24,17 @@ func RegisterGameHandler(opts *GameHandlerOptions) { } opts.Engine.POST("/api/v1/games", handler.StartGame) - opts.Engine.Static("/swagger", "./web/swagger-ui") -} - -func (g *GameHandler) GetGame(c *gin.Context) { - gameId, _ := strconv.Atoi(c.Param("gameId")) - - game, err := g.gameService.GetGameById(c, gameId) - - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "Id": game.Id, - "Token": game.Token, - }) } +// StartGame godoc +// @Summary Start a new game +// @Description Start a new game +// @Tags games +// @Accept json +// @Produce json +// @Param players body request.CreateGameRequest true "Players" +// @Success 200 {object} request.CreateGameResponse +// @Router /api/v1/games [post] func (g *GameHandler) StartGame(c *gin.Context) { var req request.CreateGameRequest @@ -78,6 +70,22 @@ func (g *GameHandler) StartGame(c *gin.Context) { }) } +func (g *GameHandler) GetGame(c *gin.Context) { + gameId, _ := strconv.Atoi(c.Param("gameId")) + + game, err := g.gameService.GetGameById(c, gameId) + + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{ + "Id": game.Id, + "Token": game.Token, + }) +} + func (g *GameHandler) DeleteGame(c *gin.Context) { gameId, _ := strconv.Atoi(c.Param("gameId")) diff --git a/Backend/service/request/create_game_request.go b/Backend/service/request/create_game_request.go index 1a30722..3cae100 100644 --- a/Backend/service/request/create_game_request.go +++ b/Backend/service/request/create_game_request.go @@ -8,3 +8,8 @@ type PlayerInfo struct { ID string `json:"id"` Name string `json:"name"` } + +type CreateGameResponse struct { + ID string `json:"id"` + Token string `json:"token"` +} diff --git a/README.md b/README.md index 50fee44..c0b1e43 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,11 @@ go run ./cmd/migrate/migrate.go go run ./cmd/migrate/game_card_seeder.go ``` +- 自動產生 Swagger API 文件 +```bash +swag init -g ./cmd/app/main.go -output ./cmd/app/docs +``` + - 開啟 Go Web Server ```bash go run cmd/app/main.go From f258bd4b9e7f9eaf59c197ec445bd8d827fadecb Mon Sep 17 00:00:00 2001 From: KenLin Date: Thu, 2 Nov 2023 20:41:52 +0800 Subject: [PATCH 20/89] chore: Add docs.go for swaggo --- .github/workflows/test-go-unit.yml | 9 ++- .gitignore | 1 + Backend/cmd/app/docs/docs.go | 106 +++++++++++++++++++++++++++++ Backend/go.mod | 2 - Backend/go.sum | 4 -- README.md | 4 +- 6 files changed, 117 insertions(+), 9 deletions(-) create mode 100644 Backend/cmd/app/docs/docs.go diff --git a/.github/workflows/test-go-unit.yml b/.github/workflows/test-go-unit.yml index 7b093a1..c403626 100644 --- a/.github/workflows/test-go-unit.yml +++ b/.github/workflows/test-go-unit.yml @@ -30,6 +30,13 @@ jobs: working-directory: ./Backend run: cp env .env + - name: Install dependencies + working-directory: ./Backend + run: | + go mod tidy + go mod download + go mod vendor + - name: Build working-directory: ./Backend run: go build -v ./... @@ -69,7 +76,7 @@ jobs: MYSQL_PASSWORD: ${{ secrets.DB_PASSWORD }} ports: - "3306:3306" - + runs-on: ubuntu-22.04 steps: - name: Checkout code diff --git a/.gitignore b/.gitignore index 2195e91..0f961ed 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ Backend/.env /.idea/* +vendor/ diff --git a/Backend/cmd/app/docs/docs.go b/Backend/cmd/app/docs/docs.go new file mode 100644 index 0000000..60a9938 --- /dev/null +++ b/Backend/cmd/app/docs/docs.go @@ -0,0 +1,106 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/api/v1/games": { + "post": { + "description": "Start a new game", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "games" + ], + "summary": "Start a new game", + "parameters": [ + { + "description": "Players", + "name": "players", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateGameRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/request.CreateGameResponse" + } + } + } + } + } + }, + "definitions": { + "request.CreateGameRequest": { + "type": "object", + "properties": { + "players": { + "type": "array", + "items": { + "$ref": "#/definitions/request.PlayerInfo" + } + } + } + }, + "request.CreateGameResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "token": { + "type": "string" + } + } + }, + "request.PlayerInfo": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "", + Host: "127.0.0.1:8080", + BasePath: "", + Schemes: []string{}, + Title: "The Message API", + Description: "This is an online version of the \"The Message\" board game backend API", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/Backend/go.mod b/Backend/go.mod index 512795c..0d464b6 100644 --- a/Backend/go.mod +++ b/Backend/go.mod @@ -9,7 +9,6 @@ require ( github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/swag v1.16.2 - github.com/swaggo/swag/example/celler v0.0.0-20231012023037-2b5852a16877 gorm.io/driver/mysql v1.5.1 gorm.io/gorm v1.25.4 ) @@ -32,7 +31,6 @@ require ( github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/gofrs/uuid v4.2.0+incompatible // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect diff --git a/Backend/go.sum b/Backend/go.sum index 2d6804d..8b474dc 100644 --- a/Backend/go.sum +++ b/Backend/go.sum @@ -45,8 +45,6 @@ github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrt github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= -github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= @@ -107,8 +105,6 @@ github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+z github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04= github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E= -github.com/swaggo/swag/example/celler v0.0.0-20231012023037-2b5852a16877 h1:GLD+cKjytGsoUokQnFxV88eBtbIyKmoTNP6O40KvjHk= -github.com/swaggo/swag/example/celler v0.0.0-20231012023037-2b5852a16877/go.mod h1:AKDmkSEqXqsZMqOXNXs53EZsD9D/SlZYxxNq2JZkoAw= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= diff --git a/README.md b/README.md index c0b1e43..de74098 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ go run cmd/app/main.go 查看 API 文件網址: ``` -http://localhost:8080/swagger/ +http://127.0.0.1:8080/swagger/index.html ``` ## Class Diagram @@ -111,4 +111,4 @@ classDiagram + getCardWeight(firstCard : MissionCard, secondCard : MissionCard) : MissionCard[] + getCanShowCard(heads : MissionCard[]) : MissionCard[] } -``` \ No newline at end of file +``` From e76cbd327f6f9ef20d5ccce74fc6c33965dc2a00 Mon Sep 17 00:00:00 2001 From: KenLin Date: Fri, 17 Nov 2023 16:35:07 +0800 Subject: [PATCH 21/89] refactor: Using the official way provided by gorm to use ORM --- Backend/service/repository/mysql/card_repository.go | 6 +++--- Backend/service/repository/mysql/deck_repository.go | 6 +++--- Backend/service/repository/mysql/game_repository.go | 6 +++--- .../service/repository/mysql/player_card_repository.go | 8 ++++---- Backend/service/repository/mysql/player_repository.go | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Backend/service/repository/mysql/card_repository.go b/Backend/service/repository/mysql/card_repository.go index c88024b..8ab096c 100644 --- a/Backend/service/repository/mysql/card_repository.go +++ b/Backend/service/repository/mysql/card_repository.go @@ -19,7 +19,7 @@ func NewCardRepository(db *gorm.DB) *CardRepository { func (c *CardRepository) GetCardById(ctx context.Context, id int) (*repository.Card, error) { card := new(repository.Card) - result := c.db.Table("cards").First(card, "id = ?", id) + result := c.db.First(&card, "id = ?", id) if result.Error != nil { return nil, result.Error @@ -30,7 +30,7 @@ func (c *CardRepository) GetCardById(ctx context.Context, id int) (*repository.C func (c *CardRepository) CreateCard(ctx context.Context, card *repository.Card) (*repository.Card, error) { - result := c.db.Table("cards").Create(card) + result := c.db.Create(&card) if result.Error != nil { return nil, result.Error @@ -42,7 +42,7 @@ func (c *CardRepository) CreateCard(ctx context.Context, card *repository.Card) func (c *CardRepository) GetCards(ctx context.Context) ([]*repository.Card, error) { var cards []*repository.Card - result := c.db.Table("cards").Find(&cards) + result := c.db.Find(&cards) if result.Error != nil { return nil, result.Error diff --git a/Backend/service/repository/mysql/deck_repository.go b/Backend/service/repository/mysql/deck_repository.go index 4c029a0..1f6e011 100644 --- a/Backend/service/repository/mysql/deck_repository.go +++ b/Backend/service/repository/mysql/deck_repository.go @@ -17,7 +17,7 @@ func NewDeckRepository(db *gorm.DB) *DeckRepository { } func (d *DeckRepository) CreateDeck(ctx context.Context, deck *repository.Deck) (*repository.Deck, error) { - result := d.db.Table("decks").Create(deck) + result := d.db.Create(&deck) if result.Error != nil { return nil, result.Error @@ -29,7 +29,7 @@ func (d *DeckRepository) CreateDeck(ctx context.Context, deck *repository.Deck) func (d *DeckRepository) GetDecksByGameId(ctx context.Context, id int) ([]*repository.Deck, error) { var decks []*repository.Deck - result := d.db.Table("decks").Find(&decks, "game_id = ?", id) + result := d.db.Find(&decks, "game_id = ?", id) if result.Error != nil { return nil, result.Error @@ -41,7 +41,7 @@ func (d *DeckRepository) GetDecksByGameId(ctx context.Context, id int) ([]*repos func (d *DeckRepository) DeleteDeck(ctx context.Context, id int) error { deck := new(repository.Deck) - result := d.db.Table("decks").Delete(deck, "id = ?", id) + result := d.db.Delete(&deck, "id = ?", id) if result.Error != nil { return result.Error diff --git a/Backend/service/repository/mysql/game_repository.go b/Backend/service/repository/mysql/game_repository.go index 40ed294..8c0e143 100644 --- a/Backend/service/repository/mysql/game_repository.go +++ b/Backend/service/repository/mysql/game_repository.go @@ -20,7 +20,7 @@ func NewGameRepository(db *gorm.DB) *GameRepository { func (g *GameRepository) GetGameById(ctx context.Context, id int) (*repository.Game, error) { game := new(repository.Game) - result := g.db.Table("games").First(game, "id = ?", id) + result := g.db.First(&game, "id = ?", id) if result.Error != nil { return nil, result.Error @@ -31,7 +31,7 @@ func (g *GameRepository) GetGameById(ctx context.Context, id int) (*repository.G func (g *GameRepository) CreateGame(ctx context.Context, game *repository.Game) (*repository.Game, error) { - result := g.db.Table("games").Create(game) + result := g.db.Create(&game) if result.Error != nil { return nil, result.Error @@ -43,7 +43,7 @@ func (g *GameRepository) CreateGame(ctx context.Context, game *repository.Game) func (g *GameRepository) DeleteGame(ctx context.Context, id int) error { game := new(repository.Game) - result := g.db.Table("games").Delete(game, "id = ?", id) + result := g.db.Delete(&game, "id = ?", id) if result.Error != nil { return result.Error diff --git a/Backend/service/repository/mysql/player_card_repository.go b/Backend/service/repository/mysql/player_card_repository.go index 2d68a0f..cadd32a 100644 --- a/Backend/service/repository/mysql/player_card_repository.go +++ b/Backend/service/repository/mysql/player_card_repository.go @@ -19,7 +19,7 @@ func NewPlayerCardRepository(db *gorm.DB) *PlayerCardRepository { func (p PlayerCardRepository) GetPlayerCardById(ctx context.Context, id int) (*repository.PlayerCard, error) { card := new(repository.PlayerCard) - result := p.db.Table("player_cards").First(card, "id = ?", id) + result := p.db.First(&card, "id = ?", id) if result.Error != nil { return nil, result.Error @@ -31,7 +31,7 @@ func (p PlayerCardRepository) GetPlayerCardById(ctx context.Context, id int) (*r func (p PlayerCardRepository) GetPlayerCardsByGameId(ctx context.Context, id int) ([]*repository.PlayerCard, error) { var cards []*repository.PlayerCard - result := p.db.Table("player_cards").Find(&cards, "game_id = ?", id) + result := p.db.Find(&cards, "game_id = ?", id) if result.Error != nil { return nil, result.Error @@ -41,7 +41,7 @@ func (p PlayerCardRepository) GetPlayerCardsByGameId(ctx context.Context, id int } func (p PlayerCardRepository) CreatePlayerCard(ctx context.Context, card *repository.PlayerCard) (*repository.PlayerCard, error) { - result := p.db.Table("player_cards").Create(card) + result := p.db.Create(&card) if result.Error != nil { return nil, result.Error @@ -53,7 +53,7 @@ func (p PlayerCardRepository) CreatePlayerCard(ctx context.Context, card *reposi func (p PlayerCardRepository) DeletePlayerCard(ctx context.Context, id int) error { card := new(repository.PlayerCard) - result := p.db.Table("player_cards").Delete(card, "id = ?", id) + result := p.db.Delete(&card, "id = ?", id) if result.Error != nil { return result.Error diff --git a/Backend/service/repository/mysql/player_repository.go b/Backend/service/repository/mysql/player_repository.go index 13adc3a..b7ac24e 100644 --- a/Backend/service/repository/mysql/player_repository.go +++ b/Backend/service/repository/mysql/player_repository.go @@ -18,14 +18,14 @@ func NewPlayerRepository(db *gorm.DB) *PlayerRepository { } func (p *PlayerRepository) CreatePlayer(ctx context.Context, player *repository.Player) (*repository.Player, error) { - err := p.db.Table("players").Create(player).Error + err := p.db.Create(&player).Error return player, err } func (p *PlayerRepository) GetPlayer(ctx context.Context, playerId int) (*repository.Player, error) { player := new(repository.Player) - result := p.db.Table("players").First(player, "id = ?", playerId) + result := p.db.First(&player, "id = ?", playerId) if result.Error != nil { return nil, result.Error @@ -37,7 +37,7 @@ func (p *PlayerRepository) GetPlayer(ctx context.Context, playerId int) (*reposi func (p *PlayerRepository) GetPlayersByGameId(ctx context.Context, id int) ([]*repository.Player, error) { var players []*repository.Player - result := p.db.Table("players").Find(&players, "game_id = ?", id) + result := p.db.Find(&players, "game_id = ?", id) if result.Error != nil { return nil, result.Error From 230f9c29a5080c2c467b5ca2d406957c3cb58b32 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Mon, 4 Dec 2023 15:26:04 +0800 Subject: [PATCH 22/89] Feat: Added heartbeat check api --- Backend/cmd/app/main.go | 6 ++++ .../delivery/http/v1/heartbeat_handler.go | 28 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 Backend/service/delivery/http/v1/heartbeat_handler.go diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index b6003b9..2475003 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -55,6 +55,12 @@ func main() { Service: gameService, }, ) + + http.RegisterHeartbeatHandler( + &http.HeartbeatHandler{ + Engine: engine, + }) + engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) err := engine.Run(":8080") diff --git a/Backend/service/delivery/http/v1/heartbeat_handler.go b/Backend/service/delivery/http/v1/heartbeat_handler.go new file mode 100644 index 0000000..9f9529a --- /dev/null +++ b/Backend/service/delivery/http/v1/heartbeat_handler.go @@ -0,0 +1,28 @@ +package http + +import ( + "github.com/gin-gonic/gin" + "net/http" +) + +type HeartbeatHandler struct { + Engine *gin.Engine +} + +func RegisterHeartbeatHandler(opts *HeartbeatHandler) { + handler := &HeartbeatHandler{} + + opts.Engine.GET("/api/v1/heartbeat", handler.Heartbeat) +} + +// Heartbeat godoc +// @Summary Check if the server is alive +// @Description Check if the server is alive +// @Tags heartbeat +// @Accept json +// @Produce json +// @Success 204 +// @Router /api/v1/heartbeat [get] +func (g *HeartbeatHandler) Heartbeat(c *gin.Context) { + c.JSON(http.StatusNoContent, http.NoBody) +} From 731ca3799cf1c313484c88f4066434dfff42fa0c Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Mon, 4 Dec 2023 15:48:10 +0800 Subject: [PATCH 23/89] Test: Added Heartbeat e2e test --- Backend/cmd/app/main.go | 1 + .../delivery/http/v1/heartbeat_handler.go | 2 +- Backend/tests/e2e/hearbeat_api_test.go | 40 +++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 Backend/tests/e2e/hearbeat_api_test.go diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index 2475003..5e38127 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -56,6 +56,7 @@ func main() { }, ) + // Register the heartbeat handler http.RegisterHeartbeatHandler( &http.HeartbeatHandler{ Engine: engine, diff --git a/Backend/service/delivery/http/v1/heartbeat_handler.go b/Backend/service/delivery/http/v1/heartbeat_handler.go index 9f9529a..faa2d29 100644 --- a/Backend/service/delivery/http/v1/heartbeat_handler.go +++ b/Backend/service/delivery/http/v1/heartbeat_handler.go @@ -22,7 +22,7 @@ func RegisterHeartbeatHandler(opts *HeartbeatHandler) { // @Accept json // @Produce json // @Success 204 -// @Router /api/v1/heartbeat [get] +// @Router /api/v1/heartbeat [GET] func (g *HeartbeatHandler) Heartbeat(c *gin.Context) { c.JSON(http.StatusNoContent, http.NoBody) } diff --git a/Backend/tests/e2e/hearbeat_api_test.go b/Backend/tests/e2e/hearbeat_api_test.go new file mode 100644 index 0000000..a797671 --- /dev/null +++ b/Backend/tests/e2e/hearbeat_api_test.go @@ -0,0 +1,40 @@ +package e2e + +import ( + "github.com/stretchr/testify/assert" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" +) + +func TestHeartbeatEndpoint(t *testing.T) { + // Initiate a new gin router + gin.SetMode(gin.TestMode) + router := gin.New() + + // Set up the heartbeat endpoint + router.GET("/api/v1/heartbeat", func(c *gin.Context) { + c.Status(http.StatusNoContent) + }) + + // Prepare a new HTTP request + req, err := http.NewRequest("GET", "/api/v1/heartbeat", nil) + if err != nil { + t.Fatalf("Couldn't create request: %v", err) + } + + // Create a response recorder + response := httptest.NewRecorder() + router.ServeHTTP(response, req) + + // Check if the status code is 204 + if response.Code != http.StatusNoContent { + t.Errorf("Expected status code %d but got %d", http.StatusNoContent, response.Code) + } + + // Assert that the response body is empty + assert.Equal(t, http.StatusNoContent, response.Code) + assert.Empty(t, response.Body.String()) +} From 20fa01bf07b29a3e94626350f384038c19e94c60 Mon Sep 17 00:00:00 2001 From: KenLin Date: Wed, 29 Nov 2023 14:16:39 +0800 Subject: [PATCH 24/89] feat: Add migration sql file --- .../000001_create_games_table.down.sql | 5 +++++ .../000001_create_games_table.up.sql | 12 ++++++++++++ .../000002_create_players_table.down.sql | 5 +++++ .../000002_create_players_table.up.sql | 15 +++++++++++++++ .../000003_create_cards_table.down.sql | 5 +++++ .../000003_create_cards_table.up.sql | 13 +++++++++++++ .../000004_create_decks_table.down.sql | 5 +++++ .../000004_create_decks_table.up.sql | 13 +++++++++++++ .../000005_create_player_cards_table.down.sql | 5 +++++ .../000005_create_player_cards_table.up.sql | 18 ++++++++++++++++++ 10 files changed, 96 insertions(+) create mode 100644 Backend/database/migrations/000001_create_games_table.down.sql create mode 100644 Backend/database/migrations/000001_create_games_table.up.sql create mode 100644 Backend/database/migrations/000002_create_players_table.down.sql create mode 100644 Backend/database/migrations/000002_create_players_table.up.sql create mode 100644 Backend/database/migrations/000003_create_cards_table.down.sql create mode 100644 Backend/database/migrations/000003_create_cards_table.up.sql create mode 100644 Backend/database/migrations/000004_create_decks_table.down.sql create mode 100644 Backend/database/migrations/000004_create_decks_table.up.sql create mode 100644 Backend/database/migrations/000005_create_player_cards_table.down.sql create mode 100644 Backend/database/migrations/000005_create_player_cards_table.up.sql diff --git a/Backend/database/migrations/000001_create_games_table.down.sql b/Backend/database/migrations/000001_create_games_table.down.sql new file mode 100644 index 0000000..1007ef2 --- /dev/null +++ b/Backend/database/migrations/000001_create_games_table.down.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP TABLE IF EXISTS games; + +COMMIT; diff --git a/Backend/database/migrations/000001_create_games_table.up.sql b/Backend/database/migrations/000001_create_games_table.up.sql new file mode 100644 index 0000000..e3b9a27 --- /dev/null +++ b/Backend/database/migrations/000001_create_games_table.up.sql @@ -0,0 +1,12 @@ +BEGIN; + +CREATE TABLE games +( + id INT AUTO_INCREMENT PRIMARY KEY, + token LONGTEXT NOT NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME +) ENGINE=InnoDB; + +COMMIT; diff --git a/Backend/database/migrations/000002_create_players_table.down.sql b/Backend/database/migrations/000002_create_players_table.down.sql new file mode 100644 index 0000000..5a8fc29 --- /dev/null +++ b/Backend/database/migrations/000002_create_players_table.down.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP TABLE IF EXISTS players; + +COMMIT; diff --git a/Backend/database/migrations/000002_create_players_table.up.sql b/Backend/database/migrations/000002_create_players_table.up.sql new file mode 100644 index 0000000..bbbf9db --- /dev/null +++ b/Backend/database/migrations/000002_create_players_table.up.sql @@ -0,0 +1,15 @@ +BEGIN; + +CREATE TABLE players +( + id INT AUTO_INCREMENT PRIMARY KEY, + game_id INT NOT NULL, + name VARCHAR(255) NOT NULL, + identity_card VARCHAR(255) NOT NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME, + FOREIGN KEY (game_id) REFERENCES games (id) +) ENGINE=InnoDB; + +COMMIT; diff --git a/Backend/database/migrations/000003_create_cards_table.down.sql b/Backend/database/migrations/000003_create_cards_table.down.sql new file mode 100644 index 0000000..6d07f2a --- /dev/null +++ b/Backend/database/migrations/000003_create_cards_table.down.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP TABLE IF EXISTS cards; + +COMMIT; diff --git a/Backend/database/migrations/000003_create_cards_table.up.sql b/Backend/database/migrations/000003_create_cards_table.up.sql new file mode 100644 index 0000000..4f1411b --- /dev/null +++ b/Backend/database/migrations/000003_create_cards_table.up.sql @@ -0,0 +1,13 @@ +BEGIN; + +CREATE TABLE cards +( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + color VARCHAR(255) NOT NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME +) ENGINE=InnoDB; + +COMMIT; diff --git a/Backend/database/migrations/000004_create_decks_table.down.sql b/Backend/database/migrations/000004_create_decks_table.down.sql new file mode 100644 index 0000000..bd69e7b --- /dev/null +++ b/Backend/database/migrations/000004_create_decks_table.down.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP TABLE IF EXISTS decks; + +COMMIT; diff --git a/Backend/database/migrations/000004_create_decks_table.up.sql b/Backend/database/migrations/000004_create_decks_table.up.sql new file mode 100644 index 0000000..9af2164 --- /dev/null +++ b/Backend/database/migrations/000004_create_decks_table.up.sql @@ -0,0 +1,13 @@ +BEGIN; + +CREATE TABLE decks +( + id INT AUTO_INCREMENT PRIMARY KEY, + game_id INT NOT NULL, + card_id INT NOT NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME +) ENGINE=InnoDB; + +COMMIT; diff --git a/Backend/database/migrations/000005_create_player_cards_table.down.sql b/Backend/database/migrations/000005_create_player_cards_table.down.sql new file mode 100644 index 0000000..5ab9b1a --- /dev/null +++ b/Backend/database/migrations/000005_create_player_cards_table.down.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP TABLE IF EXISTS player_cards; + +COMMIT; diff --git a/Backend/database/migrations/000005_create_player_cards_table.up.sql b/Backend/database/migrations/000005_create_player_cards_table.up.sql new file mode 100644 index 0000000..54fd5f8 --- /dev/null +++ b/Backend/database/migrations/000005_create_player_cards_table.up.sql @@ -0,0 +1,18 @@ +BEGIN; + +CREATE TABLE player_cards +( + id INT AUTO_INCREMENT PRIMARY KEY, + player_id INT NOT NULL, + game_id INT NOT NULL, + card_id INT NOT NULL, + type VARCHAR(255) NOT NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME, + FOREIGN KEY (player_id) REFERENCES players (id), + FOREIGN KEY (game_id) REFERENCES games (id), + FOREIGN KEY (card_id) REFERENCES cards (id) +) ENGINE=InnoDB; + +COMMIT; From 59233d3a9f29e918e47d164c48ec0454dc944f6b Mon Sep 17 00:00:00 2001 From: KenLin Date: Thu, 30 Nov 2023 16:05:01 +0800 Subject: [PATCH 25/89] feat: Integrate golang-migrate for database management This commit introduces the golang-migrate package to our project, replacing the manual gorm migration process. This change simplifies and automates database schema migrations, ensuring more reliable and consistent database updates. It also aligns our migration process with industry best practices for database management. --- Backend/cmd/app/main.go | 2 +- Backend/cmd/migrate/fresh.go | 47 ------------- Backend/cmd/migrate/game_card_seeder.go | 35 +--------- Backend/cmd/migrate/migrate.go | 17 ++++- Backend/cmd/migrate/refresh.go | 13 ++++ Backend/cmd/migrate/rollback.go | 23 +++++++ Backend/config/database.go | 36 +++++++--- Backend/config/migration.go | 72 ++++++++++++++++++++ Backend/config/test_database.go | 2 +- Backend/database/seeders/database_seeder.go | 10 +++ Backend/database/seeders/game_card_seeder.go | 34 +++++++++ Backend/go.mod | 10 ++- Backend/go.sum | 47 +++++++++++-- Backend/tests/e2e/game_api_test.go | 4 +- README.md | 9 ++- 15 files changed, 254 insertions(+), 107 deletions(-) delete mode 100644 Backend/cmd/migrate/fresh.go create mode 100644 Backend/cmd/migrate/refresh.go create mode 100644 Backend/cmd/migrate/rollback.go create mode 100644 Backend/config/migration.go create mode 100644 Backend/database/seeders/database_seeder.go create mode 100644 Backend/database/seeders/game_card_seeder.go diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index b6003b9..14a36c7 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -16,7 +16,7 @@ import ( // @description This is an online version of the "The Message" board game backend API // @host 127.0.0.1:8080 func main() { - db := config.InitDB() + db := config.NewDatabase() engine := gin.Default() diff --git a/Backend/cmd/migrate/fresh.go b/Backend/cmd/migrate/fresh.go deleted file mode 100644 index d6ee041..0000000 --- a/Backend/cmd/migrate/fresh.go +++ /dev/null @@ -1,47 +0,0 @@ -//go:build migrate - -package main - -import ( - "database/sql" - "fmt" - "github.com/Game-as-a-Service/The-Message/config" - _ "github.com/joho/godotenv/autoload" - "gorm.io/gorm" -) - -func main() { - db := config.InitDB() - tableNames := GetTableNames(db) - TruncateTables(db, tableNames) -} - -func GetTableNames(db *gorm.DB) []string { - var tableNames []string - var rows *sql.Rows - var err error - - rows, err = db.Raw("SHOW TABLES").Rows() - if err != nil { - fmt.Println("Error fetching table names:", err) - return tableNames - } - defer rows.Close() - - for rows.Next() { - var tableName string - rows.Scan(&tableName) - tableNames = append(tableNames, tableName) - } - - return tableNames -} - -func TruncateTables(db *gorm.DB, tableNames []string) { - for _, tableName := range tableNames { - db.Exec(fmt.Sprintf("SET FOREIGN_KEY_CHECKS = 0")) - db.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)) - db.Exec(fmt.Sprintf("SET FOREIGN_KEY_CHECKS = 1")) - } - fmt.Println("All specified tables truncated successfully.") -} diff --git a/Backend/cmd/migrate/game_card_seeder.go b/Backend/cmd/migrate/game_card_seeder.go index ffe228c..4241acf 100644 --- a/Backend/cmd/migrate/game_card_seeder.go +++ b/Backend/cmd/migrate/game_card_seeder.go @@ -3,41 +3,10 @@ package main import ( - "context" - "github.com/Game-as-a-Service/The-Message/config" - "github.com/Game-as-a-Service/The-Message/enums" - "github.com/Game-as-a-Service/The-Message/service/repository" - "github.com/Game-as-a-Service/The-Message/service/repository/mysql" + "github.com/Game-as-a-Service/The-Message/database/seeders" _ "github.com/joho/godotenv/autoload" - "gorm.io/gorm" ) -var actionColors = map[string]map[string]int{ - enums.LockOn: {"紅": 5, "藍": 5, "黑": 4}, - enums.LureAway: {"紅": 2, "藍": 2, "黑": 4}, - enums.Intercept: {"紅": 1, "藍": 1, "黑": 6}, - enums.Diversion: {"紅": 2, "藍": 2, "黑": 2}, - enums.Decipher: {"紅": 3, "藍": 3, "黑": 1}, - enums.Burn: {"紅": 1, "藍": 1, "黑": 4}, - enums.SeeThrough: {"紅": 3, "藍": 3, "黑": 6}, - enums.Probe: {"紅": 6, "藍": 6, "黑": 6}, - enums.BlurOfTruth: {"紅": 1, "藍": 1}, -} - func main() { - db := config.InitDB() - SeederCards(db) -} - -func SeederCards(db *gorm.DB) { - for actionType, colors := range actionColors { - for color, count := range colors { - for i := 0; i < count; i++ { - mysql.NewCardRepository(db).CreateCard(context.TODO(), &repository.Card{ - Name: actionType, - Color: color, - }) - } - } - } + seeders.Run() } diff --git a/Backend/cmd/migrate/migrate.go b/Backend/cmd/migrate/migrate.go index d76980b..8aa4e1d 100644 --- a/Backend/cmd/migrate/migrate.go +++ b/Backend/cmd/migrate/migrate.go @@ -3,15 +3,26 @@ package main import ( + "fmt" "github.com/Game-as-a-Service/The-Message/config" "github.com/Game-as-a-Service/The-Message/service/repository" - _ "github.com/joho/godotenv/autoload" "gorm.io/gorm" ) func main() { - db := config.InitDB() - MigrationMysql(db) + m, err := config.NewMigration() + if err != nil { + panic(err) + } + + err = m.Up() + if err != nil { + if err.Error() == "no change" { + fmt.Println("no change") + return + } + panic(err) + } } func MigrationMysql(db *gorm.DB) { diff --git a/Backend/cmd/migrate/refresh.go b/Backend/cmd/migrate/refresh.go new file mode 100644 index 0000000..7eb4a67 --- /dev/null +++ b/Backend/cmd/migrate/refresh.go @@ -0,0 +1,13 @@ +//go:build migrate + +package main + +import ( + "github.com/Game-as-a-Service/The-Message/config" + "github.com/Game-as-a-Service/The-Message/database/seeders" +) + +func main() { + config.RunRefresh() + seeders.Run() +} diff --git a/Backend/cmd/migrate/rollback.go b/Backend/cmd/migrate/rollback.go new file mode 100644 index 0000000..7dcc54f --- /dev/null +++ b/Backend/cmd/migrate/rollback.go @@ -0,0 +1,23 @@ +//go:build migrate + +package main + +import ( + "github.com/Game-as-a-Service/The-Message/config" + _ "github.com/joho/godotenv/autoload" +) + +func main() { + m, err := config.NewMigration() + if err != nil { + panic(err) + } + + err = m.Down() + if err != nil { + if err.Error() == "no change" { + } else { + panic(err) + } + } +} diff --git a/Backend/config/database.go b/Backend/config/database.go index cc15ab2..48ff591 100644 --- a/Backend/config/database.go +++ b/Backend/config/database.go @@ -5,25 +5,39 @@ import ( "gorm.io/driver/mysql" "gorm.io/gorm" "log" + "net/url" "os" ) -func InitDB() *gorm.DB { - dbHost := os.Getenv("DB_HOST") - dbDatabase := os.Getenv("DB_DATABASE") - dbUser := os.Getenv("DB_USER") - dbPwd := os.Getenv("DB_PASSWORD") - dbPort := os.Getenv("DB_PORT") +func NewDatabase() *gorm.DB { + dsn := DefaultDSN() - DSN := GetDSN(dbUser, dbPwd, dbHost, dbPort, dbDatabase) - db, err := gorm.Open(mysql.Open(DSN), &gorm.Config{}) + db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { log.Fatalf("Cannot connect to database: %v", err) - return nil } + return db } -func GetDSN(user string, password string, host string, port string, database string) string { - return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true", user, password, host, port, database) +func DefaultDSN() string { + dsn := BaseDSN() + + val := url.Values{} + val.Add("parseTime", "true") + val.Add("loc", "Local") + + dsn = fmt.Sprintf("%s?%s", dsn, val.Encode()) + return dsn +} + +func BaseDSN() string { + username := os.Getenv("DB_USER") + password := os.Getenv("DB_PASSWORD") + host := os.Getenv("DB_HOST") + port := os.Getenv("DB_PORT") + database := os.Getenv("DB_DATABASE") + + dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", username, password, host, port, database) + return dsn } diff --git a/Backend/config/migration.go b/Backend/config/migration.go new file mode 100644 index 0000000..c86555d --- /dev/null +++ b/Backend/config/migration.go @@ -0,0 +1,72 @@ +package config + +import ( + "database/sql" + "fmt" + "github.com/golang-migrate/migrate/v4" + "github.com/golang-migrate/migrate/v4/database/mysql" + _ "github.com/joho/godotenv/autoload" + "net/url" + "os" + + _ "github.com/go-sql-driver/mysql" + _ "github.com/golang-migrate/migrate/v4/source/file" +) + +func NewMigration() (*migrate.Migrate, error) { + dir, _ := os.Getwd() + sourceURL := "file://" + dir + "/database/migrations" + + dsn := BaseDSN() + + val := url.Values{} + val.Add("multiStatements", "true") + + dsn = fmt.Sprintf("%s?%s", dsn, val.Encode()) + + db, err := sql.Open("mysql", dsn) + if err != nil { + return nil, err + } + defer db.Close() + + if err = db.Ping(); err != nil { + return nil, err + } + + driver, err := mysql.WithInstance(db, &mysql.Config{}) + if err != nil { + return nil, err + } + + m, err := migrate.NewWithDatabaseInstance( + sourceURL, + "mysql", + driver, + ) + if err != nil { + return nil, err + } + + return m, nil +} + +func RunRefresh() { + m, err := NewMigration() + if err != nil { + panic(err) + } + + err = m.Down() + if err != nil { + if err.Error() == "no change" { + } else { + panic(err) + } + } + + err = m.Up() + if err != nil { + panic(err) + } +} diff --git a/Backend/config/test_database.go b/Backend/config/test_database.go index f01de5c..905ffd3 100644 --- a/Backend/config/test_database.go +++ b/Backend/config/test_database.go @@ -13,7 +13,7 @@ func InitTestDB() *gorm.DB { return nil } - db := InitDB() + db := NewDatabase() return db } diff --git a/Backend/database/seeders/database_seeder.go b/Backend/database/seeders/database_seeder.go new file mode 100644 index 0000000..d5ef4ed --- /dev/null +++ b/Backend/database/seeders/database_seeder.go @@ -0,0 +1,10 @@ +package seeders + +import ( + "github.com/Game-as-a-Service/The-Message/config" +) + +func Run() { + db := config.NewDatabase() + SeederCards(db) +} diff --git a/Backend/database/seeders/game_card_seeder.go b/Backend/database/seeders/game_card_seeder.go new file mode 100644 index 0000000..a6e0084 --- /dev/null +++ b/Backend/database/seeders/game_card_seeder.go @@ -0,0 +1,34 @@ +package seeders + +import ( + "context" + "github.com/Game-as-a-Service/The-Message/enums" + "github.com/Game-as-a-Service/The-Message/service/repository" + "github.com/Game-as-a-Service/The-Message/service/repository/mysql" + "gorm.io/gorm" +) + +var actionColors = map[string]map[string]int{ + enums.LockOn: {"紅": 5, "藍": 5, "黑": 4}, + enums.LureAway: {"紅": 2, "藍": 2, "黑": 4}, + enums.Intercept: {"紅": 1, "藍": 1, "黑": 6}, + enums.Diversion: {"紅": 2, "藍": 2, "黑": 2}, + enums.Decipher: {"紅": 3, "藍": 3, "黑": 1}, + enums.Burn: {"紅": 1, "藍": 1, "黑": 4}, + enums.SeeThrough: {"紅": 3, "藍": 3, "黑": 6}, + enums.Probe: {"紅": 6, "藍": 6, "黑": 6}, + enums.BlurOfTruth: {"紅": 1, "藍": 1}, +} + +func SeederCards(db *gorm.DB) { + for actionType, colors := range actionColors { + for color, count := range colors { + for i := 0; i < count; i++ { + mysql.NewCardRepository(db).CreateCard(context.TODO(), &repository.Card{ + Name: actionType, + Color: color, + }) + } + } + } +} diff --git a/Backend/go.mod b/Backend/go.mod index 0d464b6..35979d1 100644 --- a/Backend/go.mod +++ b/Backend/go.mod @@ -1,9 +1,10 @@ module github.com/Game-as-a-Service/The-Message -go 1.21.0 +go 1.21 require ( github.com/gin-gonic/gin v1.9.1 + github.com/golang-migrate/migrate/v4 v4.16.2 github.com/joho/godotenv v1.5.1 github.com/stretchr/testify v1.8.3 github.com/swaggo/files v1.0.1 @@ -31,7 +32,8 @@ require ( github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -39,6 +41,7 @@ require ( github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/mailru/easyjson v0.7.6 // indirect + github.com/mattes/migrate v3.0.1+incompatible // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -46,12 +49,13 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect + go.uber.org/atomic v1.7.0 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.14.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.9.1 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/Backend/go.sum b/Backend/go.sum index 8b474dc..bb59329 100644 --- a/Backend/go.sum +++ b/Backend/go.sum @@ -1,5 +1,9 @@ +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= @@ -14,6 +18,16 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dhui/dktest v0.3.16 h1:i6gq2YQEtcrjKbeJpBkWjE8MmLZPYllcjOFbTZuPDnw= +github.com/dhui/dktest v0.3.16/go.mod h1:gYaA3LRmM8Z4vJl2MA0THIigJoZrwOansEOsp+kqxp0= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE= +github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= @@ -45,11 +59,20 @@ github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrt github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-migrate/migrate/v4 v4.16.2 h1:8coYbMKUyInrFk1lfGfRovTLAW7PhWp8qQDT2iKfuoA= +github.com/golang-migrate/migrate/v4 v4.16.2/go.mod h1:pfcJX4nPHaVdc5nmdCikFBWtm+UBpiZjRNNsyBbp0/o= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= @@ -74,19 +97,33 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattes/migrate v3.0.1+incompatible h1:PhAZP82Vqejw8JZLF4U5UkLGzEVaCnbtJpB6DONcDow= +github.com/mattes/migrate v3.0.1+incompatible/go.mod h1:LJcqgpj1jQoxv3m2VXd3drv0suK5CbN/RCX7MXwgnVI= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= +github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -110,6 +147,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2 github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= @@ -118,8 +157,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= @@ -153,8 +192,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= diff --git a/Backend/tests/e2e/game_api_test.go b/Backend/tests/e2e/game_api_test.go index 10c19b4..a29a370 100644 --- a/Backend/tests/e2e/game_api_test.go +++ b/Backend/tests/e2e/game_api_test.go @@ -4,17 +4,17 @@ import ( "bytes" "context" "encoding/json" + "github.com/Game-as-a-Service/The-Message/config" v1 "github.com/Game-as-a-Service/The-Message/service/delivery/http/v1" "github.com/Game-as-a-Service/The-Message/service/service" + "github.com/gin-gonic/gin" "log" "net/http" "net/http/httptest" "os" "testing" - "github.com/Game-as-a-Service/The-Message/config" mysqlRepo "github.com/Game-as-a-Service/The-Message/service/repository/mysql" - "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" ) diff --git a/README.md b/README.md index de74098..711562b 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ cd Backend docker-compose up -d ``` -- 進行 DB 資料表清空與重新建置 +- 進行 DB 資料表rollback ```bash -go run ./cmd/migrate/fresh.go +go run ./cmd/migrate/rollback.go ``` - 進行 DB Migration @@ -23,6 +23,11 @@ go run ./cmd/migrate/fresh.go go run ./cmd/migrate/migrate.go ``` +- 進行 DB 資料表清空與重新建置 +```bash +go run ./cmd/migrate/refresh.go +``` + - 進行 DB Seeder ```bash go run ./cmd/migrate/game_card_seeder.go From c01c16ef1ea9c6b24a189eb5abbcebcc2b9e6f17 Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 5 Dec 2023 17:31:40 +0800 Subject: [PATCH 26/89] feat: Migration related func adjustments for easy integration testing --- Backend/cmd/migrate/game_card_seeder.go | 4 +- Backend/cmd/migrate/migrate.go | 60 ++++----------------- Backend/cmd/migrate/refresh.go | 3 +- Backend/cmd/migrate/rollback.go | 14 ++++- Backend/config/migration.go | 27 +++++----- Backend/database/seeders/database_seeder.go | 5 +- 6 files changed, 42 insertions(+), 71 deletions(-) diff --git a/Backend/cmd/migrate/game_card_seeder.go b/Backend/cmd/migrate/game_card_seeder.go index 4241acf..9677b20 100644 --- a/Backend/cmd/migrate/game_card_seeder.go +++ b/Backend/cmd/migrate/game_card_seeder.go @@ -3,10 +3,12 @@ package main import ( + "github.com/Game-as-a-Service/The-Message/config" "github.com/Game-as-a-Service/The-Message/database/seeders" _ "github.com/joho/godotenv/autoload" ) func main() { - seeders.Run() + db := config.NewDatabase() + seeders.Run(db) } diff --git a/Backend/cmd/migrate/migrate.go b/Backend/cmd/migrate/migrate.go index 8aa4e1d..f76e57f 100644 --- a/Backend/cmd/migrate/migrate.go +++ b/Backend/cmd/migrate/migrate.go @@ -5,12 +5,20 @@ package main import ( "fmt" "github.com/Game-as-a-Service/The-Message/config" - "github.com/Game-as-a-Service/The-Message/service/repository" - "gorm.io/gorm" + "net/url" + "os" ) func main() { - m, err := config.NewMigration() + dir, _ := os.Getwd() + sourceURL := "file://" + dir + "/database/migrations" + + dsn := config.BaseDSN() + val := url.Values{} + val.Add("multiStatements", "true") + dsn = fmt.Sprintf("%s?%s", dsn, val.Encode()) + + m, err := config.NewMigration(dsn, sourceURL) if err != nil { panic(err) } @@ -24,49 +32,3 @@ func main() { panic(err) } } - -func MigrationMysql(db *gorm.DB) { - err := db.AutoMigrate(&repository.Game{}) - if err != nil { - return - } - err = db.AutoMigrate(&repository.Player{}) - if err != nil { - return - } - err = db.AutoMigrate(&repository.Card{}) - if err != nil { - return - } - err = db.AutoMigrate(&repository.Deck{}) - if err != nil { - return - } - err = db.AutoMigrate(&repository.PlayerCard{}) - if err != nil { - return - } -} - -func Migration(db *gorm.DB) { - err := db.AutoMigrate(&repository.Game{}) - if err != nil { - return - } - err = db.AutoMigrate(&repository.Player{}) - if err != nil { - return - } - err = db.AutoMigrate(&repository.Card{}) - if err != nil { - return - } - err = db.AutoMigrate(&repository.Deck{}) - if err != nil { - return - } - err = db.AutoMigrate(&repository.PlayerCard{}) - if err != nil { - return - } -} diff --git a/Backend/cmd/migrate/refresh.go b/Backend/cmd/migrate/refresh.go index 7eb4a67..7c015ea 100644 --- a/Backend/cmd/migrate/refresh.go +++ b/Backend/cmd/migrate/refresh.go @@ -9,5 +9,6 @@ import ( func main() { config.RunRefresh() - seeders.Run() + db := config.NewDatabase() + seeders.Run(db) } diff --git a/Backend/cmd/migrate/rollback.go b/Backend/cmd/migrate/rollback.go index 7dcc54f..1bb94ad 100644 --- a/Backend/cmd/migrate/rollback.go +++ b/Backend/cmd/migrate/rollback.go @@ -3,12 +3,22 @@ package main import ( + "fmt" "github.com/Game-as-a-Service/The-Message/config" - _ "github.com/joho/godotenv/autoload" + "net/url" + "os" ) func main() { - m, err := config.NewMigration() + dir, _ := os.Getwd() + sourceURL := "file://" + dir + "/database/migrations" + + dsn := config.BaseDSN() + val := url.Values{} + val.Add("multiStatements", "true") + dsn = fmt.Sprintf("%s?%s", dsn, val.Encode()) + + m, err := config.NewMigration(dsn, sourceURL) if err != nil { panic(err) } diff --git a/Backend/config/migration.go b/Backend/config/migration.go index c86555d..7c7c617 100644 --- a/Backend/config/migration.go +++ b/Backend/config/migration.go @@ -3,27 +3,16 @@ package config import ( "database/sql" "fmt" + _ "github.com/go-sql-driver/mysql" "github.com/golang-migrate/migrate/v4" "github.com/golang-migrate/migrate/v4/database/mysql" + _ "github.com/golang-migrate/migrate/v4/source/file" _ "github.com/joho/godotenv/autoload" "net/url" "os" - - _ "github.com/go-sql-driver/mysql" - _ "github.com/golang-migrate/migrate/v4/source/file" ) -func NewMigration() (*migrate.Migrate, error) { - dir, _ := os.Getwd() - sourceURL := "file://" + dir + "/database/migrations" - - dsn := BaseDSN() - - val := url.Values{} - val.Add("multiStatements", "true") - - dsn = fmt.Sprintf("%s?%s", dsn, val.Encode()) - +func NewMigration(dsn string, sourceURL string) (*migrate.Migrate, error) { db, err := sql.Open("mysql", dsn) if err != nil { return nil, err @@ -52,7 +41,15 @@ func NewMigration() (*migrate.Migrate, error) { } func RunRefresh() { - m, err := NewMigration() + dir, _ := os.Getwd() + sourceURL := "file://" + dir + "/database/migrations" + + dsn := BaseDSN() + val := url.Values{} + val.Add("multiStatements", "true") + dsn = fmt.Sprintf("%s?%s", dsn, val.Encode()) + + m, err := NewMigration(dsn, sourceURL) if err != nil { panic(err) } diff --git a/Backend/database/seeders/database_seeder.go b/Backend/database/seeders/database_seeder.go index d5ef4ed..6ee14a4 100644 --- a/Backend/database/seeders/database_seeder.go +++ b/Backend/database/seeders/database_seeder.go @@ -1,10 +1,9 @@ package seeders import ( - "github.com/Game-as-a-Service/The-Message/config" + "gorm.io/gorm" ) -func Run() { - db := config.NewDatabase() +func Run(db *gorm.DB) { SeederCards(db) } From fc7b9917bcb470b7fcbd569e9e30cb5d32761274 Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 5 Dec 2023 17:34:44 +0800 Subject: [PATCH 27/89] feat: Improve the test structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just specify the receiver in the e2e test, you don’t need to write the code to start the server, db, and other environments repeatedly --- Backend/config/test_database.go | 12 ++ Backend/tests/acceptance/game_test.go | 136 --------------------- Backend/tests/e2e/game_api_test.go | 117 ++---------------- Backend/tests/e2e/suite_test.go | 167 ++++++++++++++++++++++++++ 4 files changed, 192 insertions(+), 240 deletions(-) delete mode 100644 Backend/tests/acceptance/game_test.go create mode 100644 Backend/tests/e2e/suite_test.go diff --git a/Backend/config/test_database.go b/Backend/config/test_database.go index 905ffd3..7bc8419 100644 --- a/Backend/config/test_database.go +++ b/Backend/config/test_database.go @@ -1,6 +1,7 @@ package config import ( + "fmt" "github.com/joho/godotenv" "gorm.io/gorm" "log" @@ -36,3 +37,14 @@ func getEnvPath() (string, error) { envPath := cwd + "/../../.env" return envPath, err } + +func BaseTestDSN() string { + username := os.Getenv("DB_USER") + password := os.Getenv("DB_PASSWORD") + host := os.Getenv("DB_HOST") + port := os.Getenv("DB_PORT") + database := os.Getenv("DB_DATABASE") + + dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", username, password, host, port, database) + return dsn +} diff --git a/Backend/tests/acceptance/game_test.go b/Backend/tests/acceptance/game_test.go deleted file mode 100644 index b6e20a7..0000000 --- a/Backend/tests/acceptance/game_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package acceptance - -import ( - "bytes" - "context" - "encoding/json" - v1 "github.com/Game-as-a-Service/The-Message/service/delivery/http/v1" - "github.com/Game-as-a-Service/The-Message/service/service" - "log" - "net/http" - "net/http/httptest" - "os" - "testing" - - "github.com/Game-as-a-Service/The-Message/config" - mysqlRepo "github.com/Game-as-a-Service/The-Message/service/repository/mysql" - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" -) - -var serverURL string -var gameRepo *mysqlRepo.GameRepository - -func TestMain(m *testing.M) { - testDB := config.InitTestDB() - - engine := gin.Default() - - gameRepo = mysqlRepo.NewGameRepository(testDB) - playerRepo := mysqlRepo.NewPlayerRepository(testDB) - cardRepo := mysqlRepo.NewCardRepository(testDB) - deckRepo := mysqlRepo.NewDeckRepository(testDB) - playerCardRepo := mysqlRepo.NewPlayerCardRepository(testDB) - - cardService := service.NewCardService(&service.CardServiceOptions{ - CardRepo: cardRepo, - }) - - deckService := service.NewDeckService(&service.DeckServiceOptions{ - DeckRepo: deckRepo, - CardService: cardService, - }) - - playerService := service.NewPlayerService(&service.PlayerServiceOptions{ - PlayerRepo: playerRepo, - PlayerCardRepo: playerCardRepo, - }) - - gameService := service.NewGameService( - &service.GameServiceOptions{ - GameRepo: gameRepo, - PlayerService: playerService, - CardService: cardService, - DeckService: deckService, - }, - ) - - v1.RegisterGameHandler( - &v1.GameHandlerOptions{ - Engine: engine, - Service: gameService, - }, - ) - - server := httptest.NewServer(engine) - serverURL = server.URL - - code := m.Run() - defer server.Close() - os.Exit(code) -} - -type Player struct { - ID string `json:"id"` - Name string `json:"name"` -} - -type Request struct { - Players []Player `json:"players"` -} - -func TestGive3PlayersABC_whenStartGame_thenABCHadIdentityCard(t *testing.T) { - - players := []Player{ - {ID: "6497f6f226b40d440b9a90cc", Name: "A"}, - {ID: "6498112b26b40d440b9a90ce", Name: "B"}, - {ID: "6499df157fed0c21a4fd0425", Name: "C"}, - } - requestBody := Request{Players: players} - - jsonBody, err := json.Marshal(requestBody) - if err != nil { - t.Fatalf("Failed to marshal JSON: %v", err) - } - - api := "/api/v1/games" - resp := request(t, api, jsonBody) - - assert.Equal(t, 200, resp.StatusCode) - - responseJson := response(t, resp) - - assert.NotNil(t, responseJson["Token"], "JSON response should contain a 'Token' field") - assert.NotNil(t, responseJson["Id"], "JSON response should contain a 'Id' field") - - // 驗證Game內的玩家都持有identity - game, _ := gameRepo.GetGameWithPlayers(context.TODO(), int(responseJson["Id"].(float64))) - assert.NotEmpty(t, game.Players[0].IdentityCard) - assert.NotEmpty(t, game.Players[1].IdentityCard) - assert.NotEmpty(t, game.Players[2].IdentityCard) -} - -func response(t *testing.T, resp *http.Response) map[string]interface{} { - var responseMap map[string]interface{} - err := json.NewDecoder(resp.Body).Decode(&responseMap) - if err != nil { - t.Fatalf("Failed to decode JSON: %v", err) - } - return responseMap -} - -func request(t *testing.T, api string, jsonBody []byte) *http.Response { - req, err := http.NewRequest(http.MethodPost, serverURL+api, bytes.NewBuffer(jsonBody)) - if err != nil { - t.Fatalf("Failed to send request: %v", err) - } - - client := &http.Client{} - - resp, err := client.Do(req) - if err != nil { - log.Fatal(err) - } - - return resp -} diff --git a/Backend/tests/e2e/game_api_test.go b/Backend/tests/e2e/game_api_test.go index a29a370..ef4258b 100644 --- a/Backend/tests/e2e/game_api_test.go +++ b/Backend/tests/e2e/game_api_test.go @@ -1,77 +1,12 @@ package e2e import ( - "bytes" "context" "encoding/json" - "github.com/Game-as-a-Service/The-Message/config" - v1 "github.com/Game-as-a-Service/The-Message/service/delivery/http/v1" - "github.com/Game-as-a-Service/The-Message/service/service" - "github.com/gin-gonic/gin" - "log" - "net/http" - "net/http/httptest" - "os" - "testing" - - mysqlRepo "github.com/Game-as-a-Service/The-Message/service/repository/mysql" "github.com/stretchr/testify/assert" ) -var serverURL string -var gameRepo *mysqlRepo.GameRepository -var playerRepo *mysqlRepo.PlayerRepository - -func TestMain(m *testing.M) { - testDB := config.InitTestDB() - - engine := gin.Default() - - gameRepo = mysqlRepo.NewGameRepository(testDB) - playerRepo = mysqlRepo.NewPlayerRepository(testDB) - cardRepo := mysqlRepo.NewCardRepository(testDB) - deckRepo := mysqlRepo.NewDeckRepository(testDB) - playerCardRepo := mysqlRepo.NewPlayerCardRepository(testDB) - - cardService := service.NewCardService(&service.CardServiceOptions{ - CardRepo: cardRepo, - }) - - deckService := service.NewDeckService(&service.DeckServiceOptions{ - DeckRepo: deckRepo, - CardService: cardService, - }) - - playerService := service.NewPlayerService(&service.PlayerServiceOptions{ - PlayerRepo: playerRepo, - PlayerCardRepo: playerCardRepo, - }) - - gameService := service.NewGameService( - &service.GameServiceOptions{ - GameRepo: gameRepo, - PlayerService: playerService, - CardService: cardService, - DeckService: deckService, - }, - ) - - v1.RegisterGameHandler( - &v1.GameHandlerOptions{ - Engine: engine, - Service: gameService, - }, - ) - - server := httptest.NewServer(engine) - serverURL = server.URL - - code := m.Run() - defer server.Close() - os.Exit(code) -} - -func TestStartGameE2E(t *testing.T) { +func (suite *IntegrationTestSuite) TestStartGameE2E() { players := []Player{ {ID: "6497f6f226b40d440b9a90cc", Name: "A"}, {ID: "6498112b26b40d440b9a90ce", Name: "B"}, @@ -81,33 +16,32 @@ func TestStartGameE2E(t *testing.T) { jsonBody, err := json.Marshal(requestBody) if err != nil { - t.Fatalf("Failed to marshal JSON: %v", err) + suite.T().Fatalf("Failed to marshal JSON: %v", err) } api := "/api/v1/games" - resp := requestJson(t, api, jsonBody) + resp := suite.requestJson(api, jsonBody) - assert.Equal(t, 200, resp.StatusCode) + assert.Equal(suite.T(), 200, resp.StatusCode) - responseJson := responseJson(t, resp) + responseJson := suite.responseJson(resp) - assert.NotNil(t, responseJson["Token"], "JSON response should contain a 'Token' field") - assert.NotNil(t, responseJson["Id"], "JSON response should contain a 'Id' field") + assert.NotNil(suite.T(), responseJson["Token"], "JSON response should contain a 'Token' field") + assert.NotNil(suite.T(), responseJson["Id"], "JSON response should contain a 'Id' field") // 驗證Game內的玩家都持有identity - game, _ := gameRepo.GetGameWithPlayers(context.TODO(), int(responseJson["Id"].(float64))) + game, _ := suite.gameRepo.GetGameWithPlayers(context.TODO(), int(responseJson["Id"].(float64))) - assert.NotEmpty(t, game.Players[0].IdentityCard) - assert.NotEmpty(t, game.Players[1].IdentityCard) - assert.NotEmpty(t, game.Players[2].IdentityCard) + assert.NotEmpty(suite.T(), game.Players[0].IdentityCard) + assert.NotEmpty(suite.T(), game.Players[1].IdentityCard) + assert.NotEmpty(suite.T(), game.Players[2].IdentityCard) for _, player := range game.Players { - playerCards, _ := playerRepo.GetPlayerWithPlayerCards(context.TODO(), player.Id) - assert.NotEmpty(t, playerCards.PlayerCards) + playerCards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), player.Id) + assert.NotEmpty(suite.T(), playerCards.PlayerCards) } } -// Helper functions type Player struct { ID string `json:"id"` Name string `json:"name"` @@ -116,28 +50,3 @@ type Player struct { type Request struct { Players []Player `json:"players"` } - -func responseJson(t *testing.T, resp *http.Response) map[string]interface{} { - var responseMap map[string]interface{} - err := json.NewDecoder(resp.Body).Decode(&responseMap) - if err != nil { - t.Fatalf("Failed to decode JSON: %v", err) - } - return responseMap -} - -func requestJson(t *testing.T, api string, jsonBody []byte) *http.Response { - req, err := http.NewRequest(http.MethodPost, serverURL+api, bytes.NewBuffer(jsonBody)) - if err != nil { - t.Fatalf("Failed to send request: %v", err) - } - - client := &http.Client{} - - resp, err := client.Do(req) - if err != nil { - log.Fatal(err) - } - - return resp -} diff --git a/Backend/tests/e2e/suite_test.go b/Backend/tests/e2e/suite_test.go new file mode 100644 index 0000000..f7ef8dd --- /dev/null +++ b/Backend/tests/e2e/suite_test.go @@ -0,0 +1,167 @@ +package e2e + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/Game-as-a-Service/The-Message/config" + "github.com/Game-as-a-Service/The-Message/database/seeders" + v1 "github.com/Game-as-a-Service/The-Message/service/delivery/http/v1" + "github.com/Game-as-a-Service/The-Message/service/repository" + mysqlRepo "github.com/Game-as-a-Service/The-Message/service/repository/mysql" + "github.com/Game-as-a-Service/The-Message/service/service" + "github.com/gin-gonic/gin" + _ "github.com/go-sql-driver/mysql" + _ "github.com/golang-migrate/migrate/v4/source/file" + "github.com/joho/godotenv" + _ "github.com/joho/godotenv/autoload" + _ "github.com/mattes/migrate/source/file" + "github.com/stretchr/testify/suite" + "gorm.io/gorm" + "log" + "net/http" + "net/http/httptest" + "net/url" + "os" + "testing" +) + +type IntegrationTestSuite struct { + suite.Suite + db *gorm.DB + tx *gorm.DB + server *httptest.Server + gameRepo repository.GameRepository + playerRepo repository.PlayerRepository +} + +func (suite *IntegrationTestSuite) SetupSuite() { + dir, _ := os.Getwd() + sourceURL := "file://" + dir + "/../../database/migrations" + + err := godotenv.Load("../../.env") + if err != nil { + log.Fatalf("Error loading .env file: %v", err) + } + + dsn := config.BaseTestDSN() + val := url.Values{} + val.Add("multiStatements", "true") + dsn = fmt.Sprintf("%s?%s", dsn, val.Encode()) + + m, err := config.NewMigration(dsn, sourceURL) + if err != nil { + panic(err) + } + + err = m.Down() + if err != nil { + if err.Error() == "no change" { + fmt.Println("no change") + } else { + panic(err) + } + } + + err = m.Up() + if err != nil { + if err.Error() == "no change" { + fmt.Println("no change") + } else { + panic(err) + } + } + + db := config.NewDatabase() + seeders.SeederCards(db) + + engine := gin.Default() + + gameRepo := mysqlRepo.NewGameRepository(db) + playerRepo := mysqlRepo.NewPlayerRepository(db) + cardRepo := mysqlRepo.NewCardRepository(db) + deckRepo := mysqlRepo.NewDeckRepository(db) + playerCardRepo := mysqlRepo.NewPlayerCardRepository(db) + + cardService := service.NewCardService(&service.CardServiceOptions{ + CardRepo: cardRepo, + }) + + deckService := service.NewDeckService(&service.DeckServiceOptions{ + DeckRepo: deckRepo, + CardService: cardService, + }) + + playerService := service.NewPlayerService(&service.PlayerServiceOptions{ + PlayerRepo: playerRepo, + PlayerCardRepo: playerCardRepo, + }) + + gameService := service.NewGameService( + &service.GameServiceOptions{ + GameRepo: gameRepo, + PlayerService: playerService, + CardService: cardService, + DeckService: deckService, + }, + ) + + v1.RegisterGameHandler( + &v1.GameHandlerOptions{ + Engine: engine, + Service: gameService, + }, + ) + + server := httptest.NewServer(engine) + + suite.db = db + suite.server = server + suite.gameRepo = gameRepo + suite.playerRepo = playerRepo + +} + +func (suite *IntegrationTestSuite) TearDownSuite() { + sqlDB, _ := suite.db.DB() + sqlDB.Close() + + suite.server.Close() +} + +func (suite *IntegrationTestSuite) SetupTest() { + suite.tx = suite.db.Begin() +} + +func (suite *IntegrationTestSuite) TearDownTest() { + suite.tx.Rollback() +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} + +func (suite *IntegrationTestSuite) responseJson(resp *http.Response) map[string]interface{} { + var responseMap map[string]interface{} + err := json.NewDecoder(resp.Body).Decode(&responseMap) + if err != nil { + suite.T().Fatalf("Failed to decode JSON: %v", err) + } + return responseMap +} + +func (suite *IntegrationTestSuite) requestJson(api string, jsonBody []byte) *http.Response { + req, err := http.NewRequest(http.MethodPost, suite.server.URL+api, bytes.NewBuffer(jsonBody)) + if err != nil { + suite.T().Fatalf("Failed to send request: %v", err) + } + + client := &http.Client{} + + resp, err := client.Do(req) + if err != nil { + log.Fatal(err) + } + + return resp +} From 863a09a331b76a5c253e6bd5a974036c62f0cc48 Mon Sep 17 00:00:00 2001 From: KenLin Date: Wed, 6 Dec 2023 13:43:08 +0800 Subject: [PATCH 28/89] feat: Player play card api --- Backend/cmd/app/main.go | 7 +++ .../delivery/http/v1/player_handler.go | 55 +++++++++++++++++++ .../mysql/player_card_repository.go | 13 +++++ .../repository/player_card_repository.go | 1 + Backend/service/service/player_service.go | 32 ++++++++++- Backend/tests/e2e/game_api_test.go | 4 +- Backend/tests/e2e/player_api_test.go | 44 +++++++++++++++ Backend/tests/e2e/suite_test.go | 13 ++++- 8 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 Backend/service/delivery/http/v1/player_handler.go create mode 100644 Backend/tests/e2e/player_api_test.go diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index 14a36c7..9ed01b9 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -55,6 +55,13 @@ func main() { Service: gameService, }, ) + + http.RegisterPlayerHandler( + &http.PlayerHandlerOptions{ + Engine: engine, + Service: playerService, + }, + ) engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) err := engine.Run(":8080") diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go new file mode 100644 index 0000000..d8f691c --- /dev/null +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -0,0 +1,55 @@ +package http + +import ( + "net/http" + "strconv" + + "github.com/Game-as-a-Service/The-Message/service/service" + "github.com/gin-gonic/gin" +) + +type PlayerHandler struct { + playerService service.PlayerService +} + +type PlayerHandlerOptions struct { + Engine *gin.Engine + Service service.PlayerService +} + +func RegisterPlayerHandler(opts *PlayerHandlerOptions) { + handler := &PlayerHandler{ + playerService: opts.Service, + } + + opts.Engine.POST("/api/v1/players/:playerId/player-cards", handler.PlayCard) +} + +// PlayCard godoc +// @Summary Play a card +// @Description Play a card +// @Tags players +// @Accept json +// @Produce json +// @Param playerId path int true "Player ID" +// @Param card_id body int true "Card ID" +// @Success 200 {object} string +// @Router /api/v1/players/{playerId}/player-cards [post] +func (p *PlayerHandler) PlayCard(c *gin.Context) { + playerId, _ := strconv.Atoi(c.Param("playerId")) + json := make(map[string]int) + err := c.BindJSON(&json) + if err != nil { + return + } + + result, err := p.playerService.PlayCard(c, playerId, json["card_id"]) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{ + "result": result, + }) +} diff --git a/Backend/service/repository/mysql/player_card_repository.go b/Backend/service/repository/mysql/player_card_repository.go index cadd32a..ad7d991 100644 --- a/Backend/service/repository/mysql/player_card_repository.go +++ b/Backend/service/repository/mysql/player_card_repository.go @@ -2,6 +2,7 @@ package mysql import ( "context" + "github.com/Game-as-a-Service/The-Message/service/repository" "gorm.io/gorm" ) @@ -61,3 +62,15 @@ func (p PlayerCardRepository) DeletePlayerCard(ctx context.Context, id int) erro return nil } + +func (p *PlayerCardRepository) GetPlayerCardsByPlayerId(ctx context.Context, id int, gameId int, cardType string, cardId int) ([]*repository.PlayerCard, error) { + + var cards []*repository.PlayerCard + result := p.db.Preload("Card").Find(&cards, "player_id = ? and type = ? and game_id = ? and card_id = ?", id, cardType, gameId, cardId) + + if result.Error != nil { + return nil, result.Error + } + + return cards, nil +} diff --git a/Backend/service/repository/player_card_repository.go b/Backend/service/repository/player_card_repository.go index 04c7b0b..d20965f 100644 --- a/Backend/service/repository/player_card_repository.go +++ b/Backend/service/repository/player_card_repository.go @@ -25,4 +25,5 @@ type PlayerCardRepository interface { GetPlayerCardsByGameId(ctx context.Context, id int) ([]*PlayerCard, error) CreatePlayerCard(ctx context.Context, card *PlayerCard) (*PlayerCard, error) DeletePlayerCard(ctx context.Context, id int) error + GetPlayerCardsByPlayerId(ctx context.Context, id int, gameId int, condition string, cardId int) ([]*PlayerCard, error) } diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index cbb8ee6..c62a0f8 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -2,26 +2,31 @@ package service import ( "context" + "github.com/gin-gonic/gin" + "math/rand" + "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" "github.com/Game-as-a-Service/The-Message/service/request" - "math/rand" ) type PlayerService struct { PlayerRepo repository.PlayerRepository PlayerCardRepo repository.PlayerCardRepository + GameRepo repository.GameRepository } type PlayerServiceOptions struct { PlayerRepo repository.PlayerRepository PlayerCardRepo repository.PlayerCardRepository + GameRepo repository.GameRepository } func NewPlayerService(opts *PlayerServiceOptions) PlayerService { return PlayerService{ PlayerRepo: opts.PlayerRepo, PlayerCardRepo: opts.PlayerCardRepo, + GameRepo: opts.GameRepo, } } @@ -83,3 +88,28 @@ func (p *PlayerService) CreatePlayerCard(c context.Context, card *repository.Pla } return nil } + +func (p *PlayerService) PlayCard(c *gin.Context, playerId int, cardId int) (bool, error) { + player, err := p.PlayerRepo.GetPlayer(c, playerId) + if err != nil { + return false, err + } + + cardType := "hand" + + cards, err := p.PlayerCardRepo.GetPlayerCardsByPlayerId(c, player.Id, player.GameId, cardType, cardId) + if err != nil { + return false, err + } + + if len(cards) == 0 { + return false, nil + } + + err = p.PlayerCardRepo.DeletePlayerCard(c, cards[0].Id) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/Backend/tests/e2e/game_api_test.go b/Backend/tests/e2e/game_api_test.go index ef4258b..871fea3 100644 --- a/Backend/tests/e2e/game_api_test.go +++ b/Backend/tests/e2e/game_api_test.go @@ -12,7 +12,7 @@ func (suite *IntegrationTestSuite) TestStartGameE2E() { {ID: "6498112b26b40d440b9a90ce", Name: "B"}, {ID: "6499df157fed0c21a4fd0425", Name: "C"}, } - requestBody := Request{Players: players} + requestBody := StartGameRequest{Players: players} jsonBody, err := json.Marshal(requestBody) if err != nil { @@ -47,6 +47,6 @@ type Player struct { Name string `json:"name"` } -type Request struct { +type StartGameRequest struct { Players []Player `json:"players"` } diff --git a/Backend/tests/e2e/player_api_test.go b/Backend/tests/e2e/player_api_test.go new file mode 100644 index 0000000..7b8fa09 --- /dev/null +++ b/Backend/tests/e2e/player_api_test.go @@ -0,0 +1,44 @@ +package e2e + +import ( + "context" + "encoding/json" + "github.com/Game-as-a-Service/The-Message/service/request" + "github.com/stretchr/testify/assert" +) + +func (suite *IntegrationTestSuite) TestPlayCardE2E() { + // given + game, _ := suite.gameServ.InitGame(context.TODO()) + createGameRequest := request.CreateGameRequest{ + Players: []request.PlayerInfo{ + {ID: "6497f6f226b40d440b9a90cc", Name: "A"}, + {ID: "6498112b26b40d440b9a90ce", Name: "B"}, + {ID: "6499df157fed0c21a4fd0425", Name: "C"}, + }, + } + _ = suite.playerServ.InitPlayers(context.TODO(), game, createGameRequest) + _ = suite.gameServ.InitDeck(context.TODO(), game) + _ = suite.gameServ.DrawCardsForAllPlayers(context.TODO(), game) + cards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), 1) + playCardId := cards.PlayerCards[0].CardId + + // when + api := "/api/v1/players/1/player-cards" + playCardRequest := PlayCardRequest{CardId: playCardId} + jsonBody, err := json.Marshal(playCardRequest) + if err != nil { + suite.T().Fatalf("Failed to marshal JSON: %v", err) + } + resp := suite.requestJson(api, jsonBody) + + // then + assert.Equal(suite.T(), 200, resp.StatusCode) + assert.Equal(suite.T(), 200, resp.StatusCode) + cards, _ = suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), 1) + assert.Equal(suite.T(), 2, len(cards.PlayerCards)) +} + +type PlayCardRequest struct { + CardId int `json:"card_id"` +} diff --git a/Backend/tests/e2e/suite_test.go b/Backend/tests/e2e/suite_test.go index f7ef8dd..9c152fb 100644 --- a/Backend/tests/e2e/suite_test.go +++ b/Backend/tests/e2e/suite_test.go @@ -33,6 +33,9 @@ type IntegrationTestSuite struct { server *httptest.Server gameRepo repository.GameRepository playerRepo repository.PlayerRepository + cardRepo repository.CardRepository + gameServ *service.GameService + playerServ *service.PlayerService } func (suite *IntegrationTestSuite) SetupSuite() { @@ -113,13 +116,21 @@ func (suite *IntegrationTestSuite) SetupSuite() { }, ) + v1.RegisterPlayerHandler( + &v1.PlayerHandlerOptions{ + Engine: engine, + Service: playerService, + }, + ) + server := httptest.NewServer(engine) suite.db = db suite.server = server suite.gameRepo = gameRepo suite.playerRepo = playerRepo - + suite.gameServ = &gameService + suite.playerServ = &playerService } func (suite *IntegrationTestSuite) TearDownSuite() { From 0b50e4c1c81c53c335c91fcfff7522cdb7818ad4 Mon Sep 17 00:00:00 2001 From: KenLin Date: Wed, 6 Dec 2023 14:10:54 +0800 Subject: [PATCH 29/89] feat: Generate document and organize play card request and response --- Backend/cmd/app/docs/docs.go | 57 +++++++++++++++++++ .../delivery/http/v1/player_handler.go | 14 +++-- ...create_game_request.go => game_request.go} | 8 +++ 3 files changed, 73 insertions(+), 6 deletions(-) rename Backend/service/request/{create_game_request.go => game_request.go} (67%) diff --git a/Backend/cmd/app/docs/docs.go b/Backend/cmd/app/docs/docs.go index 60a9938..42dfd5b 100644 --- a/Backend/cmd/app/docs/docs.go +++ b/Backend/cmd/app/docs/docs.go @@ -48,6 +48,47 @@ const docTemplate = `{ } } } + }, + "/api/v1/players/{playerId}/player-cards": { + "post": { + "description": "Play a card", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "players" + ], + "summary": "Play a card", + "parameters": [ + { + "type": "integer", + "description": "Player ID", + "name": "playerId", + "in": "path", + "required": true + }, + { + "description": "Card ID", + "name": "card_id", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PlayCardRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/request.PlayCardResponse" + } + } + } + } } }, "definitions": { @@ -73,6 +114,22 @@ const docTemplate = `{ } } }, + "request.PlayCardRequest": { + "type": "object", + "properties": { + "card_id": { + "type": "integer" + } + } + }, + "request.PlayCardResponse": { + "type": "object", + "properties": { + "result": { + "type": "boolean" + } + } + }, "request.PlayerInfo": { "type": "object", "properties": { diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index d8f691c..17f840c 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -1,6 +1,7 @@ package http import ( + "github.com/Game-as-a-Service/The-Message/service/request" "net/http" "strconv" @@ -32,18 +33,19 @@ func RegisterPlayerHandler(opts *PlayerHandlerOptions) { // @Accept json // @Produce json // @Param playerId path int true "Player ID" -// @Param card_id body int true "Card ID" -// @Success 200 {object} string +// @Param card_id body request.PlayCardRequest true "Card ID" +// @Success 200 {object} request.PlayCardResponse // @Router /api/v1/players/{playerId}/player-cards [post] func (p *PlayerHandler) PlayCard(c *gin.Context) { playerId, _ := strconv.Atoi(c.Param("playerId")) - json := make(map[string]int) - err := c.BindJSON(&json) - if err != nil { + var req request.PlayCardRequest + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - result, err := p.playerService.PlayCard(c, playerId, json["card_id"]) + result, err := p.playerService.PlayCard(c, playerId, req.CardID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return diff --git a/Backend/service/request/create_game_request.go b/Backend/service/request/game_request.go similarity index 67% rename from Backend/service/request/create_game_request.go rename to Backend/service/request/game_request.go index 3cae100..4d4e3c2 100644 --- a/Backend/service/request/create_game_request.go +++ b/Backend/service/request/game_request.go @@ -13,3 +13,11 @@ type CreateGameResponse struct { ID string `json:"id"` Token string `json:"token"` } + +type PlayCardResponse struct { + Result bool `json:"result"` +} + +type PlayCardRequest struct { + CardID int `json:"card_id"` +} From 811ede31c73ddf932e84c8c8dff81b000c456e66 Mon Sep 17 00:00:00 2001 From: KenLin Date: Wed, 6 Dec 2023 14:55:29 +0800 Subject: [PATCH 30/89] refactor: GetPlayerCardsByPlayerId func to GetPlayerCards func to make it easier to use --- .../repository/mysql/player_card_repository.go | 11 ++++------- .../service/repository/player_card_repository.go | 2 +- Backend/service/service/player_service.go | 13 ++++++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Backend/service/repository/mysql/player_card_repository.go b/Backend/service/repository/mysql/player_card_repository.go index ad7d991..e1c8b75 100644 --- a/Backend/service/repository/mysql/player_card_repository.go +++ b/Backend/service/repository/mysql/player_card_repository.go @@ -2,7 +2,6 @@ package mysql import ( "context" - "github.com/Game-as-a-Service/The-Message/service/repository" "gorm.io/gorm" ) @@ -63,14 +62,12 @@ func (p PlayerCardRepository) DeletePlayerCard(ctx context.Context, id int) erro return nil } -func (p *PlayerCardRepository) GetPlayerCardsByPlayerId(ctx context.Context, id int, gameId int, cardType string, cardId int) ([]*repository.PlayerCard, error) { - - var cards []*repository.PlayerCard - result := p.db.Preload("Card").Find(&cards, "player_id = ? and type = ? and game_id = ? and card_id = ?", id, cardType, gameId, cardId) - +func (p *PlayerCardRepository) GetPlayerCards(ctx context.Context, playerCard *repository.PlayerCard) (*[]repository.PlayerCard, error) { + var playerCards *[]repository.PlayerCard + result := p.db.Model(&repository.PlayerCard{}).Preload("Card").Where(&playerCard).Find(&playerCards) if result.Error != nil { return nil, result.Error } - return cards, nil + return playerCards, nil } diff --git a/Backend/service/repository/player_card_repository.go b/Backend/service/repository/player_card_repository.go index d20965f..eeb0176 100644 --- a/Backend/service/repository/player_card_repository.go +++ b/Backend/service/repository/player_card_repository.go @@ -25,5 +25,5 @@ type PlayerCardRepository interface { GetPlayerCardsByGameId(ctx context.Context, id int) ([]*PlayerCard, error) CreatePlayerCard(ctx context.Context, card *PlayerCard) (*PlayerCard, error) DeletePlayerCard(ctx context.Context, id int) error - GetPlayerCardsByPlayerId(ctx context.Context, id int, gameId int, condition string, cardId int) ([]*PlayerCard, error) + GetPlayerCards(ctx context.Context, playerCard *PlayerCard) (*[]PlayerCard, error) } diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index c62a0f8..b2e2599 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -95,18 +95,21 @@ func (p *PlayerService) PlayCard(c *gin.Context, playerId int, cardId int) (bool return false, err } - cardType := "hand" - - cards, err := p.PlayerCardRepo.GetPlayerCardsByPlayerId(c, player.Id, player.GameId, cardType, cardId) + cards, err := p.PlayerCardRepo.GetPlayerCards(c, &repository.PlayerCard{ + PlayerId: player.Id, + GameId: player.GameId, + Type: "hand", + CardId: cardId, + }) if err != nil { return false, err } - if len(cards) == 0 { + if len(*cards) == 0 { return false, nil } - err = p.PlayerCardRepo.DeletePlayerCard(c, cards[0].Id) + err = p.PlayerCardRepo.DeletePlayerCard(c, (*cards)[0].Id) if err != nil { return false, err } From fff15ed51784829ee2eeda0b7b996db917b78526 Mon Sep 17 00:00:00 2001 From: Nicole Date: Fri, 17 Nov 2023 22:05:31 +0800 Subject: [PATCH 31/89] add playerHandler --- Backend/cmd/app/main.go | 6 +++ .../delivery/http/v1/player_handler.go | 41 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 Backend/service/delivery/http/v1/player_handler.go diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index 4526d0e..3350e85 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -61,6 +61,12 @@ func main() { &http.HeartbeatHandler{ Engine: engine, }) + http.RegisterPlayerHandler( + &http.PlayerHandlerOptions{ + Engine: engine, + Service: playerService, + }, + ) engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go new file mode 100644 index 0000000..19c062f --- /dev/null +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -0,0 +1,41 @@ +package http + +import ( + "net/http" + "strconv" + + "github.com/Game-as-a-Service/The-Message/service/service" + "github.com/gin-gonic/gin" +) + +type PlayerHandler struct { + playerService service.PlayerService +} + +type PlayerHandlerOptions struct { + Engine *gin.Engine + Service service.PlayerService +} + +func RegisterPlayerHandler(opts *PlayerHandlerOptions) { + handler := &PlayerHandler{ + playerService: opts.Service, + } + + opts.Engine.GET("/api/v1/player_cards", handler.GetPlayerCards) +} + +func (p *PlayerHandler) GetPlayerCards(c *gin.Context) { + playerId, _ := strconv.Atoi(c.Param("playerId")) + cards, err := p.playerService.GetPlayerCardsByPlayerId(c, playerId) + + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{ + "Id": playerId, + "Cards": cards, + }) +} From 6acdf1a5e956c71d4f57272b5c0a5818c8bca8b1 Mon Sep 17 00:00:00 2001 From: Nicole Date: Fri, 17 Nov 2023 22:08:04 +0800 Subject: [PATCH 32/89] implement GetPlayerCardsByPlayerId --- .../repository/mysql/player_card_repository.go | 13 +++++++++++++ .../repository/player_card_repository.go | 4 +++- Backend/service/service/player_service.go | 17 ++++++++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Backend/service/repository/mysql/player_card_repository.go b/Backend/service/repository/mysql/player_card_repository.go index cadd32a..b502013 100644 --- a/Backend/service/repository/mysql/player_card_repository.go +++ b/Backend/service/repository/mysql/player_card_repository.go @@ -2,6 +2,7 @@ package mysql import ( "context" + "github.com/Game-as-a-Service/The-Message/service/repository" "gorm.io/gorm" ) @@ -61,3 +62,15 @@ func (p PlayerCardRepository) DeletePlayerCard(ctx context.Context, id int) erro return nil } + +func (p *PlayerCardRepository) GetPlayerCardsByPlayerId(ctx context.Context, id int) ([]*repository.PlayerCard, error) { + + var cards []*repository.PlayerCard + result := p.db.Preload("player_cards").Find(&cards, "player_id = ?", id) + + if result.Error != nil { + return nil, result.Error + } + + return cards, nil +} diff --git a/Backend/service/repository/player_card_repository.go b/Backend/service/repository/player_card_repository.go index 04c7b0b..c479ac6 100644 --- a/Backend/service/repository/player_card_repository.go +++ b/Backend/service/repository/player_card_repository.go @@ -2,8 +2,9 @@ package repository import ( "context" - "gorm.io/gorm" "time" + + "gorm.io/gorm" ) type PlayerCard struct { @@ -25,4 +26,5 @@ type PlayerCardRepository interface { GetPlayerCardsByGameId(ctx context.Context, id int) ([]*PlayerCard, error) CreatePlayerCard(ctx context.Context, card *PlayerCard) (*PlayerCard, error) DeletePlayerCard(ctx context.Context, id int) error + GetPlayerCardsByPlayerId(ctx context.Context, id int) ([]*PlayerCard, error) } diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index cbb8ee6..4289625 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -2,10 +2,11 @@ package service import ( "context" + "math/rand" + "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" "github.com/Game-as-a-Service/The-Message/service/request" - "math/rand" ) type PlayerService struct { @@ -83,3 +84,17 @@ func (p *PlayerService) CreatePlayerCard(c context.Context, card *repository.Pla } return nil } + +func (p *PlayerService) GetPlayerCardsByPlayerId(c context.Context, id int) ([]*repository.PlayerCard, error) { + player, err := p.PlayerRepo.GetPlayer(c, id) + if err != nil { + return nil, err + } + + cards, err := p.PlayerCardRepo.GetPlayerCardsByPlayerId(c, player.Id) + + if err != nil { + return nil, err + } + return cards, nil +} From e27234c1a40f6a663d059899950d2b5acbed4fed Mon Sep 17 00:00:00 2001 From: Nicole Date: Tue, 21 Nov 2023 20:40:00 +0800 Subject: [PATCH 33/89] modify api --- Backend/service/delivery/http/v1/player_handler.go | 2 +- Backend/service/repository/mysql/player_card_repository.go | 2 +- Backend/service/service/player_service.go | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 19c062f..e9fc9a7 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -22,7 +22,7 @@ func RegisterPlayerHandler(opts *PlayerHandlerOptions) { playerService: opts.Service, } - opts.Engine.GET("/api/v1/player_cards", handler.GetPlayerCards) + opts.Engine.GET("/api/v1/player_cards/:playerId", handler.GetPlayerCards) } func (p *PlayerHandler) GetPlayerCards(c *gin.Context) { diff --git a/Backend/service/repository/mysql/player_card_repository.go b/Backend/service/repository/mysql/player_card_repository.go index b502013..3f0ded1 100644 --- a/Backend/service/repository/mysql/player_card_repository.go +++ b/Backend/service/repository/mysql/player_card_repository.go @@ -66,7 +66,7 @@ func (p PlayerCardRepository) DeletePlayerCard(ctx context.Context, id int) erro func (p *PlayerCardRepository) GetPlayerCardsByPlayerId(ctx context.Context, id int) ([]*repository.PlayerCard, error) { var cards []*repository.PlayerCard - result := p.db.Preload("player_cards").Find(&cards, "player_id = ?", id) + result := p.db.Find(&cards, "player_id = ?", id) if result.Error != nil { return nil, result.Error diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 4289625..7ed9540 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -12,17 +12,20 @@ import ( type PlayerService struct { PlayerRepo repository.PlayerRepository PlayerCardRepo repository.PlayerCardRepository + GameRepo repository.GameRepository } type PlayerServiceOptions struct { PlayerRepo repository.PlayerRepository PlayerCardRepo repository.PlayerCardRepository + GameRepo repository.GameRepository } func NewPlayerService(opts *PlayerServiceOptions) PlayerService { return PlayerService{ PlayerRepo: opts.PlayerRepo, PlayerCardRepo: opts.PlayerCardRepo, + GameRepo: opts.GameRepo, } } From 4d5da41cc97a635e81fc84aa1fa2b0e12a06d56d Mon Sep 17 00:00:00 2001 From: Nicole Date: Tue, 21 Nov 2023 21:08:56 +0800 Subject: [PATCH 34/89] add swaggo --- Backend/service/delivery/http/v1/player_handler.go | 8 ++++++++ Backend/service/request/player_card_request.go | 10 ++++++++++ 2 files changed, 18 insertions(+) create mode 100644 Backend/service/request/player_card_request.go diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index e9fc9a7..a2322cc 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -25,6 +25,14 @@ func RegisterPlayerHandler(opts *PlayerHandlerOptions) { opts.Engine.GET("/api/v1/player_cards/:playerId", handler.GetPlayerCards) } +// GetPlayerCards godoc +// @Summary GetPlayerCards +// @Description GetPlayerCardsByPlayerId +// @Tags player_cards +// @Produce json +// @Param id path int true "Player ID" +// @Success 200 {object} request.PlayerCardsResponse +// @Router /api/v1/player_cards/{id} [get] func (p *PlayerHandler) GetPlayerCards(c *gin.Context) { playerId, _ := strconv.Atoi(c.Param("playerId")) cards, err := p.playerService.GetPlayerCardsByPlayerId(c, playerId) diff --git a/Backend/service/request/player_card_request.go b/Backend/service/request/player_card_request.go new file mode 100644 index 0000000..57bff1c --- /dev/null +++ b/Backend/service/request/player_card_request.go @@ -0,0 +1,10 @@ +package request + +import ( + "github.com/Game-as-a-Service/The-Message/service/repository" +) + +type PlayerCardsResponse struct { + ID string `json:"id"` + Cards []repository.PlayerCard +} From ac1ecf4623048194dfcf4ede25806e600a0d7c71 Mon Sep 17 00:00:00 2001 From: Nicole Date: Fri, 24 Nov 2023 22:08:13 +0800 Subject: [PATCH 35/89] return card id rather than object --- Backend/cmd/app/docs/docs.go | 43 +++++++++++++++++++ .../delivery/http/v1/player_handler.go | 9 ++-- .../service/request/player_card_request.go | 8 +--- 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/Backend/cmd/app/docs/docs.go b/Backend/cmd/app/docs/docs.go index 60a9938..2cb65ca 100644 --- a/Backend/cmd/app/docs/docs.go +++ b/Backend/cmd/app/docs/docs.go @@ -48,6 +48,35 @@ const docTemplate = `{ } } } + }, + "/api/v1/player_cards/{id}": { + "get": { + "description": "GetPlayerCardsByPlayerId", + "produces": [ + "application/json" + ], + "tags": [ + "player_cards" + ], + "summary": "GetPlayerCards", + "parameters": [ + { + "type": "integer", + "description": "Player ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/request.PlayerCardsResponse" + } + } + } + } } }, "definitions": { @@ -73,6 +102,20 @@ const docTemplate = `{ } } }, + "request.PlayerCardsResponse": { + "type": "object", + "properties": { + "cardIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "string" + } + } + }, "request.PlayerInfo": { "type": "object", "properties": { diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index a2322cc..385b6d3 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -41,9 +41,12 @@ func (p *PlayerHandler) GetPlayerCards(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } - + var cards_Ids []string + for _, card := range cards { + cards_Ids = append(cards_Ids, strconv.Itoa(card.CardId)) + } c.JSON(http.StatusOK, gin.H{ - "Id": playerId, - "Cards": cards, + "Id": playerId, + "PlayerCards": cards_Ids, }) } diff --git a/Backend/service/request/player_card_request.go b/Backend/service/request/player_card_request.go index 57bff1c..5b67082 100644 --- a/Backend/service/request/player_card_request.go +++ b/Backend/service/request/player_card_request.go @@ -1,10 +1,6 @@ package request -import ( - "github.com/Game-as-a-Service/The-Message/service/repository" -) - type PlayerCardsResponse struct { - ID string `json:"id"` - Cards []repository.PlayerCard + ID string `json:"id"` + CardIds []string `json:"cardIds"` } From 34deccce856a09dd04f4f91c7a880b73345172a7 Mon Sep 17 00:00:00 2001 From: Nicole Date: Fri, 1 Dec 2023 22:19:04 +0800 Subject: [PATCH 36/89] modify return message --- Backend/cmd/app/docs/docs.go | 5 +- Backend/cmd/app/docs/swagger.json | 120 ++++++++++++++++++ Backend/cmd/app/docs/swagger.yaml | 77 +++++++++++ .../delivery/http/v1/player_handler.go | 18 ++- Backend/service/repository/card_repository.go | 13 +- .../service/repository/player_repository.go | 6 +- .../service/request/player_card_request.go | 3 +- 7 files changed, 220 insertions(+), 22 deletions(-) create mode 100644 Backend/cmd/app/docs/swagger.json create mode 100644 Backend/cmd/app/docs/swagger.yaml diff --git a/Backend/cmd/app/docs/docs.go b/Backend/cmd/app/docs/docs.go index 2cb65ca..60bb426 100644 --- a/Backend/cmd/app/docs/docs.go +++ b/Backend/cmd/app/docs/docs.go @@ -105,14 +105,11 @@ const docTemplate = `{ "request.PlayerCardsResponse": { "type": "object", "properties": { - "cardIds": { + "cards": { "type": "array", "items": { "type": "string" } - }, - "id": { - "type": "string" } } }, diff --git a/Backend/cmd/app/docs/swagger.json b/Backend/cmd/app/docs/swagger.json new file mode 100644 index 0000000..dea7a0d --- /dev/null +++ b/Backend/cmd/app/docs/swagger.json @@ -0,0 +1,120 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is an online version of the \"The Message\" board game backend API", + "title": "The Message API", + "contact": {} + }, + "host": "127.0.0.1:8080", + "paths": { + "/api/v1/games": { + "post": { + "description": "Start a new game", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "games" + ], + "summary": "Start a new game", + "parameters": [ + { + "description": "Players", + "name": "players", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateGameRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/request.CreateGameResponse" + } + } + } + } + }, + "/api/v1/player_cards/{id}": { + "get": { + "description": "GetPlayerCardsByPlayerId", + "produces": [ + "application/json" + ], + "tags": [ + "player_cards" + ], + "summary": "GetPlayerCards", + "parameters": [ + { + "type": "integer", + "description": "Player ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/request.PlayerCardsResponse" + } + } + } + } + } + }, + "definitions": { + "request.CreateGameRequest": { + "type": "object", + "properties": { + "players": { + "type": "array", + "items": { + "$ref": "#/definitions/request.PlayerInfo" + } + } + } + }, + "request.CreateGameResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "token": { + "type": "string" + } + } + }, + "request.PlayerCardsResponse": { + "type": "object", + "properties": { + "cards": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "request.PlayerInfo": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/Backend/cmd/app/docs/swagger.yaml b/Backend/cmd/app/docs/swagger.yaml new file mode 100644 index 0000000..47d6f82 --- /dev/null +++ b/Backend/cmd/app/docs/swagger.yaml @@ -0,0 +1,77 @@ +definitions: + request.CreateGameRequest: + properties: + players: + items: + $ref: '#/definitions/request.PlayerInfo' + type: array + type: object + request.CreateGameResponse: + properties: + id: + type: string + token: + type: string + type: object + request.PlayerCardsResponse: + properties: + cards: + items: + type: string + type: array + type: object + request.PlayerInfo: + properties: + id: + type: string + name: + type: string + type: object +host: 127.0.0.1:8080 +info: + contact: {} + description: This is an online version of the "The Message" board game backend API + title: The Message API +paths: + /api/v1/games: + post: + consumes: + - application/json + description: Start a new game + parameters: + - description: Players + in: body + name: players + required: true + schema: + $ref: '#/definitions/request.CreateGameRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/request.CreateGameResponse' + summary: Start a new game + tags: + - games + /api/v1/player_cards/{id}: + get: + description: GetPlayerCardsByPlayerId + parameters: + - description: Player ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/request.PlayerCardsResponse' + summary: GetPlayerCards + tags: + - player_cards +swagger: "2.0" diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 385b6d3..5435a2b 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -22,7 +22,7 @@ func RegisterPlayerHandler(opts *PlayerHandlerOptions) { playerService: opts.Service, } - opts.Engine.GET("/api/v1/player_cards/:playerId", handler.GetPlayerCards) + opts.Engine.GET("/api/v1/player/:playerId/player-cards/", handler.GetPlayerCards) } // GetPlayerCards godoc @@ -41,12 +41,16 @@ func (p *PlayerHandler) GetPlayerCards(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } - var cards_Ids []string + // var cards_Ids []string + player_cards := []map[string]interface{}{} + for _, card := range cards { - cards_Ids = append(cards_Ids, strconv.Itoa(card.CardId)) + dict := map[string]interface{}{ + "id": card.CardId, + "name": card.Card.Name, + "color": card.Card.Color, + } + player_cards = append(player_cards, dict) } - c.JSON(http.StatusOK, gin.H{ - "Id": playerId, - "PlayerCards": cards_Ids, - }) + c.JSON(http.StatusOK, gin.H{"player_cards": player_cards}) } diff --git a/Backend/service/repository/card_repository.go b/Backend/service/repository/card_repository.go index f4a92ba..e3859c3 100644 --- a/Backend/service/repository/card_repository.go +++ b/Backend/service/repository/card_repository.go @@ -9,12 +9,13 @@ import ( type Card struct { gorm.Model - Id int `gorm:"primaryKey;auto_increment"` - Name string - Color string - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoCreateTime"` - DeletedAt gorm.DeletedAt + Id int `gorm:"primaryKey;auto_increment"` + Name string + Color string + PlayerCard []PlayerCard `gorm:"foreignKey:Card"` + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoCreateTime"` + DeletedAt gorm.DeletedAt } type CardRepository interface { diff --git a/Backend/service/repository/player_repository.go b/Backend/service/repository/player_repository.go index d5f963d..38520ee 100644 --- a/Backend/service/repository/player_repository.go +++ b/Backend/service/repository/player_repository.go @@ -13,9 +13,9 @@ type Player struct { Name string GameId int `gorm:"foreignKey:GameId;references:Id"` IdentityCard string - PlayerCards []PlayerCard - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoCreateTime"` + PlayerCards []PlayerCard `gorm:"foreignKey:Player"` + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoCreateTime"` DeletedAt gorm.DeletedAt } diff --git a/Backend/service/request/player_card_request.go b/Backend/service/request/player_card_request.go index 5b67082..cfa6ac5 100644 --- a/Backend/service/request/player_card_request.go +++ b/Backend/service/request/player_card_request.go @@ -1,6 +1,5 @@ package request type PlayerCardsResponse struct { - ID string `json:"id"` - CardIds []string `json:"cardIds"` + Cards []string `json:"cards"` } From 2756de67bb2d41eedc75ee0127625375aabd95e9 Mon Sep 17 00:00:00 2001 From: Nicole Date: Wed, 6 Dec 2023 21:43:23 +0800 Subject: [PATCH 37/89] add player card api test --- Backend/coverage.out | 1 + Backend/go.mod | 1 + Backend/go.sum | 2 + .../delivery/http/v1/player_handler.go | 2 +- Backend/service/repository/card_repository.go | 13 ++-- .../service/repository/player_repository.go | 6 +- Backend/tests/e2e/player_card_api_test.go | 62 +++++++++++++++++++ 7 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 Backend/coverage.out create mode 100644 Backend/tests/e2e/player_card_api_test.go diff --git a/Backend/coverage.out b/Backend/coverage.out new file mode 100644 index 0000000..5f02b11 --- /dev/null +++ b/Backend/coverage.out @@ -0,0 +1 @@ +mode: set diff --git a/Backend/go.mod b/Backend/go.mod index 35979d1..9258d5c 100644 --- a/Backend/go.mod +++ b/Backend/go.mod @@ -3,6 +3,7 @@ module github.com/Game-as-a-Service/The-Message go 1.21 require ( + github.com/bxcodec/faker/v3 v3.8.1 github.com/gin-gonic/gin v1.9.1 github.com/golang-migrate/migrate/v4 v4.16.2 github.com/joho/godotenv v1.5.1 diff --git a/Backend/go.sum b/Backend/go.sum index bb59329..470bd1b 100644 --- a/Backend/go.sum +++ b/Backend/go.sum @@ -8,6 +8,8 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/bxcodec/faker/v3 v3.8.1 h1:qO/Xq19V6uHt2xujwpaetgKhraGCapqY2CRWGD/SqcM= +github.com/bxcodec/faker/v3 v3.8.1/go.mod h1:DdSDccxF5msjFo5aO4vrobRQ8nIApg8kq3QWPEQD6+o= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 5435a2b..a7fe465 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -41,7 +41,7 @@ func (p *PlayerHandler) GetPlayerCards(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } - // var cards_Ids []string + player_cards := []map[string]interface{}{} for _, card := range cards { diff --git a/Backend/service/repository/card_repository.go b/Backend/service/repository/card_repository.go index e3859c3..f4a92ba 100644 --- a/Backend/service/repository/card_repository.go +++ b/Backend/service/repository/card_repository.go @@ -9,13 +9,12 @@ import ( type Card struct { gorm.Model - Id int `gorm:"primaryKey;auto_increment"` - Name string - Color string - PlayerCard []PlayerCard `gorm:"foreignKey:Card"` - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoCreateTime"` - DeletedAt gorm.DeletedAt + Id int `gorm:"primaryKey;auto_increment"` + Name string + Color string + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoCreateTime"` + DeletedAt gorm.DeletedAt } type CardRepository interface { diff --git a/Backend/service/repository/player_repository.go b/Backend/service/repository/player_repository.go index 38520ee..d5f963d 100644 --- a/Backend/service/repository/player_repository.go +++ b/Backend/service/repository/player_repository.go @@ -13,9 +13,9 @@ type Player struct { Name string GameId int `gorm:"foreignKey:GameId;references:Id"` IdentityCard string - PlayerCards []PlayerCard `gorm:"foreignKey:Player"` - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoCreateTime"` + PlayerCards []PlayerCard + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoCreateTime"` DeletedAt gorm.DeletedAt } diff --git a/Backend/tests/e2e/player_card_api_test.go b/Backend/tests/e2e/player_card_api_test.go new file mode 100644 index 0000000..0ae3c91 --- /dev/null +++ b/Backend/tests/e2e/player_card_api_test.go @@ -0,0 +1,62 @@ +package e2e + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + v1 "github.com/Game-as-a-Service/The-Message/service/delivery/http/v1" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" +) + +func TestGetPlayerCards(t *testing.T) { + player := &PlayerCard{ + ID: "1", + PlayerID: "player1", + GameID: "game1", + CardID: "card1", + Type: "type1", + } + + // Create a new Gin router + router := gin.Default() + + // Create a new PlayerHandler instance + playerHandler := &v1.PlayerHandler{} + + // Define the request + playerID := player.ID // Replace with a valid player ID + req, _ := http.NewRequest(http.MethodGet, "/players/"+playerID+"/cards", nil) + router.GET("/players/:playerId/cards", playerHandler.GetPlayerCards) + + // Create a test response recorder + w := httptest.NewRecorder() + + // Perform the request + router.ServeHTTP(w, req) + + // Check the response status code + assert.Equal(t, http.StatusOK, w.Code) + + // Parse the response body + var responseBody map[string]interface{} + err := json.Unmarshal(w.Body.Bytes(), &responseBody) + assert.NoError(t, err) + + // Assert the expected response + expectedResponse := map[string]interface{}{ + "player_cards": []map[string]interface{}{}, + } + assert.Equal(t, expectedResponse, responseBody) +} + +type PlayerCard struct { + ID string `json:"id"` + PlayerID string `json:"player_id"` + GameID string `json:"game_id"` + CardID string `json:"card_id"` + Type string `json:"type"` +} From 20da49d9281321040b77de994f07a6e19fdc32ea Mon Sep 17 00:00:00 2001 From: Nicole Date: Wed, 6 Dec 2023 21:57:14 +0800 Subject: [PATCH 38/89] add game id and PlayerCards[] in Card --- Backend/service/repository/card_repository.go | 13 +++++++------ .../repository/mysql/player_card_repository.go | 4 ++-- .../service/repository/player_card_repository.go | 2 +- Backend/service/service/player_service.go | 3 ++- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Backend/service/repository/card_repository.go b/Backend/service/repository/card_repository.go index f4a92ba..5e15f19 100644 --- a/Backend/service/repository/card_repository.go +++ b/Backend/service/repository/card_repository.go @@ -9,12 +9,13 @@ import ( type Card struct { gorm.Model - Id int `gorm:"primaryKey;auto_increment"` - Name string - Color string - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoCreateTime"` - DeletedAt gorm.DeletedAt + Id int `gorm:"primaryKey;auto_increment"` + Name string + Color string + PlayerCards []PlayerCard + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoCreateTime"` + DeletedAt gorm.DeletedAt } type CardRepository interface { diff --git a/Backend/service/repository/mysql/player_card_repository.go b/Backend/service/repository/mysql/player_card_repository.go index 3f0ded1..4e0c416 100644 --- a/Backend/service/repository/mysql/player_card_repository.go +++ b/Backend/service/repository/mysql/player_card_repository.go @@ -63,10 +63,10 @@ func (p PlayerCardRepository) DeletePlayerCard(ctx context.Context, id int) erro return nil } -func (p *PlayerCardRepository) GetPlayerCardsByPlayerId(ctx context.Context, id int) ([]*repository.PlayerCard, error) { +func (p *PlayerCardRepository) GetPlayerCardsByPlayerId(ctx context.Context, id int, gid int) ([]*repository.PlayerCard, error) { var cards []*repository.PlayerCard - result := p.db.Find(&cards, "player_id = ?", id) + result := p.db.Find(&cards, "player_id = ? AND game_id = ?", id, gid) if result.Error != nil { return nil, result.Error diff --git a/Backend/service/repository/player_card_repository.go b/Backend/service/repository/player_card_repository.go index c479ac6..8cc170b 100644 --- a/Backend/service/repository/player_card_repository.go +++ b/Backend/service/repository/player_card_repository.go @@ -26,5 +26,5 @@ type PlayerCardRepository interface { GetPlayerCardsByGameId(ctx context.Context, id int) ([]*PlayerCard, error) CreatePlayerCard(ctx context.Context, card *PlayerCard) (*PlayerCard, error) DeletePlayerCard(ctx context.Context, id int) error - GetPlayerCardsByPlayerId(ctx context.Context, id int) ([]*PlayerCard, error) + GetPlayerCardsByPlayerId(ctx context.Context, id int, gid int) ([]*PlayerCard, error) } diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 7ed9540..7a372c1 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -90,11 +90,12 @@ func (p *PlayerService) CreatePlayerCard(c context.Context, card *repository.Pla func (p *PlayerService) GetPlayerCardsByPlayerId(c context.Context, id int) ([]*repository.PlayerCard, error) { player, err := p.PlayerRepo.GetPlayer(c, id) + game, err := p.GameRepo.GetGameWithPlayers(c, id) if err != nil { return nil, err } - cards, err := p.PlayerCardRepo.GetPlayerCardsByPlayerId(c, player.Id) + cards, err := p.PlayerCardRepo.GetPlayerCardsByPlayerId(c, player.Id, game.Id) if err != nil { return nil, err From 04e75a28a47b0eb1fb9ff83d19ba39ddd73b6879 Mon Sep 17 00:00:00 2001 From: Nicole Date: Sat, 9 Dec 2023 18:34:40 +0800 Subject: [PATCH 39/89] move api to card service and repo --- Backend/cmd/app/main.go | 11 ++- .../service/delivery/http/v1/card_handler.go | 64 +++++++++++++ .../delivery/http/v1/player_handler.go | 56 ------------ Backend/service/repository/card_repository.go | 1 + .../repository/mysql/card_repository.go | 24 +++++ .../mysql/player_card_repository.go | 12 --- .../repository/player_card_repository.go | 5 +- Backend/service/service/card_service.go | 31 ++++++- Backend/service/service/player_service.go | 24 ++--- Backend/tests/e2e/game_api_test.go | 4 +- Backend/tests/e2e/player_card_api_test.go | 91 ++++++++++++------- Backend/tests/e2e/suite_test.go | 56 +++++++++--- 12 files changed, 232 insertions(+), 147 deletions(-) create mode 100644 Backend/service/delivery/http/v1/card_handler.go delete mode 100644 Backend/service/delivery/http/v1/player_handler.go diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index 3350e85..d707c29 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -27,7 +27,10 @@ func main() { playerCardRepo := mysqlRepo.NewPlayerCardRepository(db) cardService := service.NewCardService(&service.CardServiceOptions{ - CardRepo: cardRepo, + CardRepo: cardRepo, + PlayerRepo: playerRepo, + PlayerCardRepo: playerCardRepo, + GameRepo: gameRepo, }) deckService := service.NewDeckService(&service.DeckServiceOptions{ @@ -61,10 +64,10 @@ func main() { &http.HeartbeatHandler{ Engine: engine, }) - http.RegisterPlayerHandler( - &http.PlayerHandlerOptions{ + http.RegisterCardHandler( + &http.CardHandlerOptions{ Engine: engine, - Service: playerService, + Service: cardService, }, ) diff --git a/Backend/service/delivery/http/v1/card_handler.go b/Backend/service/delivery/http/v1/card_handler.go new file mode 100644 index 0000000..40390eb --- /dev/null +++ b/Backend/service/delivery/http/v1/card_handler.go @@ -0,0 +1,64 @@ +package http + +import ( + "encoding/json" + "fmt" + "net/http" + "strconv" + + "github.com/Game-as-a-Service/The-Message/service/service" + "github.com/gin-gonic/gin" +) + +type CardHandler struct { + cardService service.CardService +} + +type CardHandlerOptions struct { + Engine *gin.Engine + Service service.CardService +} + +func RegisterCardHandler(opts *CardHandlerOptions) { + handler := &CardHandler{ + cardService: opts.Service, + } + + opts.Engine.GET("/api/v1/player/:playerId/player-cards/", handler.GetPlayerCards) +} + +// GetPlayerCards godoc +// @Summary GetPlayerCards +// @Description GetPlayerCardsByPlayerId +// @Tags player_cards +// @Produce json +// @Param id path int true "Player ID" +// @Success 200 {object} request.PlayerCardsResponse +// @Router /api/v1/player_cards/{id} [get] +func (p *CardHandler) GetPlayerCards(c *gin.Context) { + playerId, _ := strconv.Atoi(c.Param("playerId")) + player_cards, err := p.cardService.GetPlayerCardsByPlayerId(c, playerId) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + + player_cards_info := []map[string]interface{}{} + + for _, card := range player_cards { + dict := map[string]interface{}{ + "id": card.Id, + "name": card.Name, + "color": card.Color, + } + player_cards_info = append(player_cards_info, dict) + } + jsonData, err := json.Marshal(player_cards_info) + fmt.Println(jsonData) + if err != nil { + fmt.Println("Error encoding JSON:", err) + return + } + jsonString := string(jsonData) + c.JSON(http.StatusOK, gin.H{"player_cards": jsonString}) +} diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go deleted file mode 100644 index a7fe465..0000000 --- a/Backend/service/delivery/http/v1/player_handler.go +++ /dev/null @@ -1,56 +0,0 @@ -package http - -import ( - "net/http" - "strconv" - - "github.com/Game-as-a-Service/The-Message/service/service" - "github.com/gin-gonic/gin" -) - -type PlayerHandler struct { - playerService service.PlayerService -} - -type PlayerHandlerOptions struct { - Engine *gin.Engine - Service service.PlayerService -} - -func RegisterPlayerHandler(opts *PlayerHandlerOptions) { - handler := &PlayerHandler{ - playerService: opts.Service, - } - - opts.Engine.GET("/api/v1/player/:playerId/player-cards/", handler.GetPlayerCards) -} - -// GetPlayerCards godoc -// @Summary GetPlayerCards -// @Description GetPlayerCardsByPlayerId -// @Tags player_cards -// @Produce json -// @Param id path int true "Player ID" -// @Success 200 {object} request.PlayerCardsResponse -// @Router /api/v1/player_cards/{id} [get] -func (p *PlayerHandler) GetPlayerCards(c *gin.Context) { - playerId, _ := strconv.Atoi(c.Param("playerId")) - cards, err := p.playerService.GetPlayerCardsByPlayerId(c, playerId) - - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) - return - } - - player_cards := []map[string]interface{}{} - - for _, card := range cards { - dict := map[string]interface{}{ - "id": card.CardId, - "name": card.Card.Name, - "color": card.Card.Color, - } - player_cards = append(player_cards, dict) - } - c.JSON(http.StatusOK, gin.H{"player_cards": player_cards}) -} diff --git a/Backend/service/repository/card_repository.go b/Backend/service/repository/card_repository.go index 5e15f19..3711502 100644 --- a/Backend/service/repository/card_repository.go +++ b/Backend/service/repository/card_repository.go @@ -22,4 +22,5 @@ type CardRepository interface { GetCardById(ctx context.Context, id int) (*Card, error) CreateCard(ctx context.Context, card *Card) (*Card, error) GetCards(ctx context.Context) ([]*Card, error) + GetPlayerCardsByPlayerId(ctx context.Context, id int, gid int) ([]*Card, error) } diff --git a/Backend/service/repository/mysql/card_repository.go b/Backend/service/repository/mysql/card_repository.go index 8ab096c..c2b357e 100644 --- a/Backend/service/repository/mysql/card_repository.go +++ b/Backend/service/repository/mysql/card_repository.go @@ -2,6 +2,7 @@ package mysql import ( "context" + "github.com/Game-as-a-Service/The-Message/service/repository" "gorm.io/gorm" ) @@ -50,3 +51,26 @@ func (c *CardRepository) GetCards(ctx context.Context) ([]*repository.Card, erro return cards, nil } + +func (p *CardRepository) GetPlayerCardsByPlayerId(ctx context.Context, id int, gid int) ([]*repository.Card, error) { + + var player_cards []*repository.PlayerCard + var cards []*repository.Card + result := p.db.Find(&player_cards, "player_id = ? AND game_id = ?", id, gid) + + if result.Error != nil { + return nil, result.Error + } + var cardIDs []int + for _, pc := range player_cards { + cardIDs = append(cardIDs, pc.CardId) + } + + result = p.db.Find(&cards, "id IN ?", cardIDs) + if result.Error != nil { + return nil, result.Error + } + + return cards, nil + +} diff --git a/Backend/service/repository/mysql/player_card_repository.go b/Backend/service/repository/mysql/player_card_repository.go index 4e0c416..341d878 100644 --- a/Backend/service/repository/mysql/player_card_repository.go +++ b/Backend/service/repository/mysql/player_card_repository.go @@ -62,15 +62,3 @@ func (p PlayerCardRepository) DeletePlayerCard(ctx context.Context, id int) erro return nil } - -func (p *PlayerCardRepository) GetPlayerCardsByPlayerId(ctx context.Context, id int, gid int) ([]*repository.PlayerCard, error) { - - var cards []*repository.PlayerCard - result := p.db.Find(&cards, "player_id = ? AND game_id = ?", id, gid) - - if result.Error != nil { - return nil, result.Error - } - - return cards, nil -} diff --git a/Backend/service/repository/player_card_repository.go b/Backend/service/repository/player_card_repository.go index 8cc170b..d1d53f7 100644 --- a/Backend/service/repository/player_card_repository.go +++ b/Backend/service/repository/player_card_repository.go @@ -17,8 +17,8 @@ type PlayerCard struct { CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoCreateTime"` DeletedAt gorm.DeletedAt - Card Card - Player Player + Card Card `gorm:"foreignKey:CardId"` + Player Player `gorm:"foreignKey:PlayerId"` } type PlayerCardRepository interface { @@ -26,5 +26,4 @@ type PlayerCardRepository interface { GetPlayerCardsByGameId(ctx context.Context, id int) ([]*PlayerCard, error) CreatePlayerCard(ctx context.Context, card *PlayerCard) (*PlayerCard, error) DeletePlayerCard(ctx context.Context, id int) error - GetPlayerCardsByPlayerId(ctx context.Context, id int, gid int) ([]*PlayerCard, error) } diff --git a/Backend/service/service/card_service.go b/Backend/service/service/card_service.go index ac12776..f5151d0 100644 --- a/Backend/service/service/card_service.go +++ b/Backend/service/service/card_service.go @@ -2,20 +2,30 @@ package service import ( "context" + "github.com/Game-as-a-Service/The-Message/service/repository" ) type CardService struct { - CardRepo repository.CardRepository + CardRepo repository.CardRepository + GameRepo repository.GameRepository + PlayerRepo repository.PlayerRepository + PlayerCardRepo repository.PlayerCardRepository } type CardServiceOptions struct { - CardRepo repository.CardRepository + CardRepo repository.CardRepository + GameRepo repository.GameRepository + PlayerRepo repository.PlayerRepository + PlayerCardRepo repository.PlayerCardRepository } func NewCardService(opts *CardServiceOptions) CardService { return CardService{ - CardRepo: opts.CardRepo, + CardRepo: opts.CardRepo, + GameRepo: opts.GameRepo, + PlayerRepo: opts.PlayerRepo, + PlayerCardRepo: opts.PlayerCardRepo, } } @@ -26,3 +36,18 @@ func (c *CardService) GetCards(ctx context.Context) ([]*repository.Card, error) } return cards, nil } + +func (p *CardService) GetPlayerCardsByPlayerId(c context.Context, id int) ([]*repository.Card, error) { + player, err := p.PlayerRepo.GetPlayer(c, id) + game, err := p.GameRepo.GetGameWithPlayers(c, id) + if err != nil { + return nil, err + } + + cards, err := p.CardRepo.GetPlayerCardsByPlayerId(c, player.Id, game.Id) + + if err != nil { + return nil, err + } + return cards, nil +} diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 7a372c1..9af77f0 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -12,20 +12,23 @@ import ( type PlayerService struct { PlayerRepo repository.PlayerRepository PlayerCardRepo repository.PlayerCardRepository - GameRepo repository.GameRepository + // GameRepo repository.GameRepository + // CardRepo repository.CardRepository } type PlayerServiceOptions struct { PlayerRepo repository.PlayerRepository PlayerCardRepo repository.PlayerCardRepository - GameRepo repository.GameRepository + // GameRepo repository.GameRepository + // CardRepo repository.CardRepository } func NewPlayerService(opts *PlayerServiceOptions) PlayerService { return PlayerService{ PlayerRepo: opts.PlayerRepo, PlayerCardRepo: opts.PlayerCardRepo, - GameRepo: opts.GameRepo, + // GameRepo: opts.GameRepo, + // CardRepo: opts.CardRepo, } } @@ -87,18 +90,3 @@ func (p *PlayerService) CreatePlayerCard(c context.Context, card *repository.Pla } return nil } - -func (p *PlayerService) GetPlayerCardsByPlayerId(c context.Context, id int) ([]*repository.PlayerCard, error) { - player, err := p.PlayerRepo.GetPlayer(c, id) - game, err := p.GameRepo.GetGameWithPlayers(c, id) - if err != nil { - return nil, err - } - - cards, err := p.PlayerCardRepo.GetPlayerCardsByPlayerId(c, player.Id, game.Id) - - if err != nil { - return nil, err - } - return cards, nil -} diff --git a/Backend/tests/e2e/game_api_test.go b/Backend/tests/e2e/game_api_test.go index ef4258b..4e577c4 100644 --- a/Backend/tests/e2e/game_api_test.go +++ b/Backend/tests/e2e/game_api_test.go @@ -3,6 +3,8 @@ package e2e import ( "context" "encoding/json" + "net/http" + "github.com/stretchr/testify/assert" ) @@ -20,7 +22,7 @@ func (suite *IntegrationTestSuite) TestStartGameE2E() { } api := "/api/v1/games" - resp := suite.requestJson(api, jsonBody) + resp := suite.requestJson(api, jsonBody, http.MethodPost) assert.Equal(suite.T(), 200, resp.StatusCode) diff --git a/Backend/tests/e2e/player_card_api_test.go b/Backend/tests/e2e/player_card_api_test.go index 0ae3c91..f933f2d 100644 --- a/Backend/tests/e2e/player_card_api_test.go +++ b/Backend/tests/e2e/player_card_api_test.go @@ -1,56 +1,73 @@ package e2e import ( + "context" "encoding/json" "net/http" - "net/http/httptest" - "testing" - v1 "github.com/Game-as-a-Service/The-Message/service/delivery/http/v1" - - "github.com/gin-gonic/gin" + "github.com/Game-as-a-Service/The-Message/service/repository" "github.com/stretchr/testify/assert" ) -func TestGetPlayerCards(t *testing.T) { - player := &PlayerCard{ - ID: "1", - PlayerID: "player1", - GameID: "game1", - CardID: "card1", - Type: "type1", - } +func (suite *IntegrationTestSuite) TestGetPlayerCards() { - // Create a new Gin router - router := gin.Default() + // given + game := repository.Game{} + _, err := suite.gameRepo.CreateGame(context.TODO(), &game) + player := repository.Player{ + Name: "player1", + GameId: 1, + IdentityCard: "醬油", + } + _, err = suite.playerRepo.CreatePlayer(context.TODO(), &player) + if err != nil { + panic(err) + } - // Create a new PlayerHandler instance - playerHandler := &v1.PlayerHandler{} + _, err = suite.playerCardRepo.CreatePlayerCard(context.TODO(), &repository.PlayerCard{ + PlayerId: 1, + GameId: 1, + CardId: 1, + Type: "hand", + }) + if err != nil { + panic(err) + } - // Define the request - playerID := player.ID // Replace with a valid player ID - req, _ := http.NewRequest(http.MethodGet, "/players/"+playerID+"/cards", nil) - router.GET("/players/:playerId/cards", playerHandler.GetPlayerCards) + // when + api := "/api/v1/player/1/player-cards/" + resp := suite.requestJson(api, nil, http.MethodGet) + response := suite.responseTest(resp) - // Create a test response recorder - w := httptest.NewRecorder() + // then + assert.Equal(suite.T(), 200, resp.StatusCode) - // Perform the request - router.ServeHTTP(w, req) + jsonStr1 := `{ + "player_cards": [ + { + "color": "", + "id": 1, + "name": "" + } + ] + }` - // Check the response status code - assert.Equal(t, http.StatusOK, w.Code) + playerCard := map[string]interface{}{ + "color": "", + "id": 1, + "name": "", + } - // Parse the response body - var responseBody map[string]interface{} - err := json.Unmarshal(w.Body.Bytes(), &responseBody) - assert.NoError(t, err) + playerCards := map[string]interface{}{ + "player_cards": []interface{}{playerCard}, + } - // Assert the expected response - expectedResponse := map[string]interface{}{ - "player_cards": []map[string]interface{}{}, + err = json.Unmarshal([]byte(jsonStr1), &playerCards) + if err != nil { + panic(err) } - assert.Equal(t, expectedResponse, responseBody) + + assert.Equal(suite.T(), response, playerCards) } type PlayerCard struct { @@ -60,3 +77,7 @@ type PlayerCard struct { CardID string `json:"card_id"` Type string `json:"type"` } + +type Request_p struct { + Player Player `json:"player"` +} diff --git a/Backend/tests/e2e/suite_test.go b/Backend/tests/e2e/suite_test.go index f7ef8dd..6db545e 100644 --- a/Backend/tests/e2e/suite_test.go +++ b/Backend/tests/e2e/suite_test.go @@ -4,6 +4,13 @@ import ( "bytes" "encoding/json" "fmt" + "log" + "net/http" + "net/http/httptest" + "net/url" + "os" + "testing" + "github.com/Game-as-a-Service/The-Message/config" "github.com/Game-as-a-Service/The-Message/database/seeders" v1 "github.com/Game-as-a-Service/The-Message/service/delivery/http/v1" @@ -18,21 +25,18 @@ import ( _ "github.com/mattes/migrate/source/file" "github.com/stretchr/testify/suite" "gorm.io/gorm" - "log" - "net/http" - "net/http/httptest" - "net/url" - "os" - "testing" ) type IntegrationTestSuite struct { suite.Suite - db *gorm.DB - tx *gorm.DB - server *httptest.Server - gameRepo repository.GameRepository - playerRepo repository.PlayerRepository + db *gorm.DB + tx *gorm.DB + server *httptest.Server + gameRepo repository.GameRepository + playerRepo repository.PlayerRepository + playerCardRepo repository.PlayerCardRepository + gameServ *service.GameService + playerServ *service.PlayerService } func (suite *IntegrationTestSuite) SetupSuite() { @@ -71,8 +75,8 @@ func (suite *IntegrationTestSuite) SetupSuite() { panic(err) } } - db := config.NewDatabase() + seeders.SeederCards(db) engine := gin.Default() @@ -84,7 +88,10 @@ func (suite *IntegrationTestSuite) SetupSuite() { playerCardRepo := mysqlRepo.NewPlayerCardRepository(db) cardService := service.NewCardService(&service.CardServiceOptions{ - CardRepo: cardRepo, + CardRepo: cardRepo, + GameRepo: gameRepo, + PlayerRepo: playerRepo, + PlayerCardRepo: playerCardRepo, }) deckService := service.NewDeckService(&service.DeckServiceOptions{ @@ -113,12 +120,22 @@ func (suite *IntegrationTestSuite) SetupSuite() { }, ) + v1.RegisterCardHandler( + &v1.CardHandlerOptions{ + Engine: engine, + Service: cardService, + }, + ) + server := httptest.NewServer(engine) suite.db = db suite.server = server suite.gameRepo = gameRepo suite.playerRepo = playerRepo + suite.gameServ = &gameService + suite.playerServ = &playerService + suite.playerCardRepo = playerCardRepo } @@ -150,8 +167,8 @@ func (suite *IntegrationTestSuite) responseJson(resp *http.Response) map[string] return responseMap } -func (suite *IntegrationTestSuite) requestJson(api string, jsonBody []byte) *http.Response { - req, err := http.NewRequest(http.MethodPost, suite.server.URL+api, bytes.NewBuffer(jsonBody)) +func (suite *IntegrationTestSuite) requestJson(api string, jsonBody []byte, method string) *http.Response { + req, err := http.NewRequest(method, suite.server.URL+api, bytes.NewBuffer(jsonBody)) if err != nil { suite.T().Fatalf("Failed to send request: %v", err) } @@ -165,3 +182,12 @@ func (suite *IntegrationTestSuite) requestJson(api string, jsonBody []byte) *htt return resp } + +func (suite *IntegrationTestSuite) responseTest(resp *http.Response) interface{} { + var responseMap interface{} + err := json.NewDecoder(resp.Body).Decode(&responseMap) + if err != nil { + suite.T().Fatalf("Failed to decode JSON: %v", err) + } + return responseMap +} From 75441d3ff8c244c8ab38f4cca602285f3ef0e709 Mon Sep 17 00:00:00 2001 From: Nicole Date: Sun, 10 Dec 2023 11:14:31 +0800 Subject: [PATCH 40/89] make api test work --- Backend/tests/e2e/player_card_api_test.go | 46 +++++++++++------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/Backend/tests/e2e/player_card_api_test.go b/Backend/tests/e2e/player_card_api_test.go index f933f2d..30d3143 100644 --- a/Backend/tests/e2e/player_card_api_test.go +++ b/Backend/tests/e2e/player_card_api_test.go @@ -3,6 +3,7 @@ package e2e import ( "context" "encoding/json" + "fmt" "net/http" "github.com/Game-as-a-Service/The-Message/service/repository" @@ -37,37 +38,34 @@ func (suite *IntegrationTestSuite) TestGetPlayerCards() { // when api := "/api/v1/player/1/player-cards/" resp := suite.requestJson(api, nil, http.MethodGet) - response := suite.responseTest(resp) - + response := suite.responseJson(resp) // then assert.Equal(suite.T(), 200, resp.StatusCode) - jsonStr1 := `{ - "player_cards": [ - { - "color": "", - "id": 1, - "name": "" - } - ] - }` - - playerCard := map[string]interface{}{ - "color": "", - "id": 1, - "name": "", + playerCards, ok := response["player_cards"] + if !ok { + fmt.Println("Error: player_cards is not of type []interface{}") + return } - playerCards := map[string]interface{}{ - "player_cards": []interface{}{playerCard}, - } + if str, ok := playerCards.(string); ok { + var slice []map[string]interface{} + if err := json.Unmarshal([]byte(str), &slice); err != nil { + fmt.Println("Error decoding JSON:", err) + return + } - err = json.Unmarshal([]byte(jsonStr1), &playerCards) - if err != nil { - panic(err) - } + // Range over the slice + for _, item := range slice { + fmt.Println(item["id"], item["name"], item["color"]) + for key, value := range item { + if value == nil { + suite.T().Errorf("Field %s is nil", key) + } + } - assert.Equal(suite.T(), response, playerCards) + } + } } type PlayerCard struct { From 293ed19eaef86a70f61291a6ef868995d4480397 Mon Sep 17 00:00:00 2001 From: Nicole Date: Sun, 10 Dec 2023 11:27:29 +0800 Subject: [PATCH 41/89] modify api spec --- Backend/cmd/app/docs/docs.go | 27 ++++++++++++++++--- Backend/cmd/app/docs/swagger.json | 27 ++++++++++++++++--- Backend/cmd/app/docs/swagger.yaml | 20 +++++++++++--- .../service/delivery/http/v1/card_handler.go | 2 +- .../service/request/player_card_request.go | 2 +- 5 files changed, 67 insertions(+), 11 deletions(-) diff --git a/Backend/cmd/app/docs/docs.go b/Backend/cmd/app/docs/docs.go index 60bb426..d682948 100644 --- a/Backend/cmd/app/docs/docs.go +++ b/Backend/cmd/app/docs/docs.go @@ -49,7 +49,27 @@ const docTemplate = `{ } } }, - "/api/v1/player_cards/{id}": { + "/api/v1/heartbeat": { + "get": { + "description": "Check if the server is alive", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "heartbeat" + ], + "summary": "Check if the server is alive", + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/api/v1/player/{id}/player-cards/": { "get": { "description": "GetPlayerCardsByPlayerId", "produces": [ @@ -105,10 +125,11 @@ const docTemplate = `{ "request.PlayerCardsResponse": { "type": "object", "properties": { - "cards": { + "player_cards": { "type": "array", "items": { - "type": "string" + "type": "object", + "additionalProperties": true } } } diff --git a/Backend/cmd/app/docs/swagger.json b/Backend/cmd/app/docs/swagger.json index dea7a0d..676c275 100644 --- a/Backend/cmd/app/docs/swagger.json +++ b/Backend/cmd/app/docs/swagger.json @@ -41,7 +41,27 @@ } } }, - "/api/v1/player_cards/{id}": { + "/api/v1/heartbeat": { + "get": { + "description": "Check if the server is alive", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "heartbeat" + ], + "summary": "Check if the server is alive", + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/api/v1/player/{id}/player-cards/": { "get": { "description": "GetPlayerCardsByPlayerId", "produces": [ @@ -97,10 +117,11 @@ "request.PlayerCardsResponse": { "type": "object", "properties": { - "cards": { + "player_cards": { "type": "array", "items": { - "type": "string" + "type": "object", + "additionalProperties": true } } } diff --git a/Backend/cmd/app/docs/swagger.yaml b/Backend/cmd/app/docs/swagger.yaml index 47d6f82..ca7bf23 100644 --- a/Backend/cmd/app/docs/swagger.yaml +++ b/Backend/cmd/app/docs/swagger.yaml @@ -15,9 +15,10 @@ definitions: type: object request.PlayerCardsResponse: properties: - cards: + player_cards: items: - type: string + additionalProperties: true + type: object type: array type: object request.PlayerInfo: @@ -55,7 +56,20 @@ paths: summary: Start a new game tags: - games - /api/v1/player_cards/{id}: + /api/v1/heartbeat: + get: + consumes: + - application/json + description: Check if the server is alive + produces: + - application/json + responses: + "204": + description: No Content + summary: Check if the server is alive + tags: + - heartbeat + /api/v1/player/{id}/player-cards/: get: description: GetPlayerCardsByPlayerId parameters: diff --git a/Backend/service/delivery/http/v1/card_handler.go b/Backend/service/delivery/http/v1/card_handler.go index 40390eb..74a2747 100644 --- a/Backend/service/delivery/http/v1/card_handler.go +++ b/Backend/service/delivery/http/v1/card_handler.go @@ -34,7 +34,7 @@ func RegisterCardHandler(opts *CardHandlerOptions) { // @Produce json // @Param id path int true "Player ID" // @Success 200 {object} request.PlayerCardsResponse -// @Router /api/v1/player_cards/{id} [get] +// @Router /api/v1/player/{id}/player-cards/ [get] func (p *CardHandler) GetPlayerCards(c *gin.Context) { playerId, _ := strconv.Atoi(c.Param("playerId")) player_cards, err := p.cardService.GetPlayerCardsByPlayerId(c, playerId) diff --git a/Backend/service/request/player_card_request.go b/Backend/service/request/player_card_request.go index cfa6ac5..a283e8e 100644 --- a/Backend/service/request/player_card_request.go +++ b/Backend/service/request/player_card_request.go @@ -1,5 +1,5 @@ package request type PlayerCardsResponse struct { - Cards []string `json:"cards"` + Player_cards []map[string]interface{} `json:"player_cards"` } From 5391d7885f0bcb2158ef8253f68856fb92f72dfd Mon Sep 17 00:00:00 2001 From: Nicole Date: Mon, 11 Dec 2023 22:24:48 +0800 Subject: [PATCH 42/89] modify return format --- Backend/service/delivery/http/v1/card_handler.go | 4 ++-- Backend/service/service/player_service.go | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Backend/service/delivery/http/v1/card_handler.go b/Backend/service/delivery/http/v1/card_handler.go index 74a2747..5e14cd4 100644 --- a/Backend/service/delivery/http/v1/card_handler.go +++ b/Backend/service/delivery/http/v1/card_handler.go @@ -59,6 +59,6 @@ func (p *CardHandler) GetPlayerCards(c *gin.Context) { fmt.Println("Error encoding JSON:", err) return } - jsonString := string(jsonData) - c.JSON(http.StatusOK, gin.H{"player_cards": jsonString}) + + c.JSON(http.StatusOK, gin.H{"player_cards": player_cards_info}) } diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 9af77f0..cf31c3e 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -12,23 +12,17 @@ import ( type PlayerService struct { PlayerRepo repository.PlayerRepository PlayerCardRepo repository.PlayerCardRepository - // GameRepo repository.GameRepository - // CardRepo repository.CardRepository } type PlayerServiceOptions struct { PlayerRepo repository.PlayerRepository PlayerCardRepo repository.PlayerCardRepository - // GameRepo repository.GameRepository - // CardRepo repository.CardRepository } func NewPlayerService(opts *PlayerServiceOptions) PlayerService { return PlayerService{ PlayerRepo: opts.PlayerRepo, PlayerCardRepo: opts.PlayerCardRepo, - // GameRepo: opts.GameRepo, - // CardRepo: opts.CardRepo, } } From 209a44cb4b7a4de504eec8f70591dd8e5f9bb1dd Mon Sep 17 00:00:00 2001 From: KenLin Date: Sun, 17 Dec 2023 15:49:29 +0800 Subject: [PATCH 43/89] style: use camel case --- Backend/service/delivery/http/v1/card_handler.go | 12 ++++++------ Backend/service/repository/mysql/card_repository.go | 6 +++--- Backend/service/service/card_service.go | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Backend/service/delivery/http/v1/card_handler.go b/Backend/service/delivery/http/v1/card_handler.go index 5e14cd4..5f335d8 100644 --- a/Backend/service/delivery/http/v1/card_handler.go +++ b/Backend/service/delivery/http/v1/card_handler.go @@ -37,28 +37,28 @@ func RegisterCardHandler(opts *CardHandlerOptions) { // @Router /api/v1/player/{id}/player-cards/ [get] func (p *CardHandler) GetPlayerCards(c *gin.Context) { playerId, _ := strconv.Atoi(c.Param("playerId")) - player_cards, err := p.cardService.GetPlayerCardsByPlayerId(c, playerId) + playerCards, err := p.cardService.GetPlayerCardsByPlayerId(c, playerId) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } - player_cards_info := []map[string]interface{}{} + playerCardsInfo := []map[string]interface{}{} - for _, card := range player_cards { + for _, card := range playerCards { dict := map[string]interface{}{ "id": card.Id, "name": card.Name, "color": card.Color, } - player_cards_info = append(player_cards_info, dict) + playerCardsInfo = append(playerCardsInfo, dict) } - jsonData, err := json.Marshal(player_cards_info) + jsonData, err := json.Marshal(playerCardsInfo) fmt.Println(jsonData) if err != nil { fmt.Println("Error encoding JSON:", err) return } - c.JSON(http.StatusOK, gin.H{"player_cards": player_cards_info}) + c.JSON(http.StatusOK, gin.H{"player_cards": playerCardsInfo}) } diff --git a/Backend/service/repository/mysql/card_repository.go b/Backend/service/repository/mysql/card_repository.go index 585ea2b..9ba518b 100644 --- a/Backend/service/repository/mysql/card_repository.go +++ b/Backend/service/repository/mysql/card_repository.go @@ -54,15 +54,15 @@ func (c *CardRepository) GetCards(ctx context.Context) ([]*repository.Card, erro func (c *CardRepository) GetPlayerCardsByPlayerId(ctx context.Context, id int, gid int) ([]*repository.Card, error) { - var player_cards []*repository.PlayerCard + var playerCards []*repository.PlayerCard var cards []*repository.Card - result := c.db.Find(&player_cards, "player_id = ? AND game_id = ?", id, gid) + result := c.db.Find(&playerCards, "player_id = ? AND game_id = ?", id, gid) if result.Error != nil { return nil, result.Error } var cardIDs []int - for _, pc := range player_cards { + for _, pc := range playerCards { cardIDs = append(cardIDs, pc.CardId) } diff --git a/Backend/service/service/card_service.go b/Backend/service/service/card_service.go index 71a87fd..9116abd 100644 --- a/Backend/service/service/card_service.go +++ b/Backend/service/service/card_service.go @@ -37,14 +37,14 @@ func (c *CardService) GetCards(ctx context.Context) ([]*repository.Card, error) return cards, nil } -func (p *CardService) GetPlayerCardsByPlayerId(c context.Context, id int) ([]*repository.Card, error) { - player, err := p.PlayerRepo.GetPlayer(c, id) - game, err := p.GameRepo.GetGameWithPlayers(c, player.GameId) +func (c *CardService) GetPlayerCardsByPlayerId(ctx context.Context, id int) ([]*repository.Card, error) { + player, err := c.PlayerRepo.GetPlayer(ctx, id) + game, err := c.GameRepo.GetGameWithPlayers(ctx, player.GameId) if err != nil { return nil, err } - cards, err := p.CardRepo.GetPlayerCardsByPlayerId(c, player.Id, game.Id) + cards, err := c.CardRepo.GetPlayerCardsByPlayerId(ctx, player.Id, game.Id) if err != nil { return nil, err From 31268172fe5f5eb20f36e4a9453cf4838470b3db Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 28 Nov 2023 10:41:00 +0800 Subject: [PATCH 44/89] feat: Game Event SSE Basic Architecture --- Backend/cmd/app/main.go | 10 ++ .../service/delivery/http/v1/game_handler.go | 124 ++++++++++++++++++ Backend/service/service/game_service.go | 3 + 3 files changed, 137 insertions(+) diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index 36a2e11..0d306c4 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -19,6 +19,16 @@ func main() { db := config.NewDatabase() engine := gin.Default() + //go func() { + // for { + // time.Sleep(time.Second * 10) + // now := time.Now().Format("2006-01-02 15:04:05") + // currentTime := fmt.Sprintf("The Current Time Is %v", now) + // + // // Send current time to clients message channel + // stream.Message <- currentTime + // } + //}() gameRepo := mysqlRepo.NewGameRepository(db) playerRepo := mysqlRepo.NewPlayerRepository(db) diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 29e3fa7..b9b29c3 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -1,6 +1,9 @@ package http import ( + "fmt" + "io" + "log" "net/http" "strconv" @@ -22,8 +25,28 @@ func RegisterGameHandler(opts *GameHandlerOptions) { handler := &GameHandler{ gameService: opts.Service, } + stream := NewServer() opts.Engine.POST("/api/v1/games", handler.StartGame) + //opts.Engine.GET("/api/v1/games/:gameId/event", handler.GameEvent) + opts.Engine.GET("/api/v1/games/:gameId/event", HeadersMiddleware(), stream.serveHTTP(), func(c *gin.Context) { + v, ok := c.Get("clientChan") + if !ok { + return + } + clientChan, ok := v.(ClientChan) + if !ok { + return + } + c.Stream(func(w io.Writer) bool { + // Stream message to client from message channel + if msg, ok := <-clientChan; ok { + c.SSEvent("message", msg) + return true + } + return false + }) + }) } // StartGame godoc @@ -64,6 +87,8 @@ func (g *GameHandler) StartGame(c *gin.Context) { return } + c.SSEvent("message", gin.H{"message": "Game started"}) + c.JSON(http.StatusOK, gin.H{ "Id": game.Id, "Token": game.Token, @@ -98,3 +123,102 @@ func (g *GameHandler) DeleteGame(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "Game deleted"}) } + +func (g *GameHandler) GameEvent(c *gin.Context) { + c.Writer.Header().Set("Content-Type", "text/event-stream") + c.Writer.Header().Set("Cache-Control", "no-cache") + c.Writer.Header().Set("Connection", "keep-alive") + c.Writer.Header().Set("Transfer-Encoding", "chunked") + + stream := NewServer() + stream.serveHTTP() + + v, ok := c.Get("clientChan") + if !ok { + return + } + clientChan, ok := v.(ClientChan) + if !ok { + return + } + c.Stream(func(w io.Writer) bool { + // Stream message to client from message channel + if msg, ok := <-clientChan; ok { + c.SSEvent("message", msg) + return true + } + return false + }) + gameId, _ := strconv.Atoi(c.Param("gameId")) + gameStr, _ := fmt.Scanf("Game started %s", &gameId) + c.SSEvent("message", gameStr) + +} + +type ClientChan chan string + +type Event struct { + Message chan string + NewClients chan chan string + ClosedClients chan chan string + TotalClients map[chan string]bool +} + +func (stream *Event) listen() { + for { + select { + case client := <-stream.NewClients: + stream.TotalClients[client] = true + log.Printf("Client added. %d registered clients", len(stream.TotalClients)) + + case client := <-stream.ClosedClients: + delete(stream.TotalClients, client) + close(client) + log.Printf("Removed client. %d registered clients", len(stream.TotalClients)) + + case eventMsg := <-stream.Message: + for clientMessageChan := range stream.TotalClients { + clientMessageChan <- eventMsg + } + } + } +} + +func (stream *Event) serveHTTP() gin.HandlerFunc { + return func(c *gin.Context) { + clientChan := make(ClientChan) + + stream.NewClients <- clientChan + + defer func() { + stream.ClosedClients <- clientChan + }() + + c.Set("clientChan", clientChan) + + c.Next() + } +} + +func NewServer() (event *Event) { + event = &Event{ + Message: make(chan string), + NewClients: make(chan chan string), + ClosedClients: make(chan chan string), + TotalClients: make(map[chan string]bool), + } + + go event.listen() + + return +} + +func HeadersMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + c.Writer.Header().Set("Content-Type", "text/event-stream") + c.Writer.Header().Set("Cache-Control", "no-cache") + c.Writer.Header().Set("Connection", "keep-alive") + c.Writer.Header().Set("Transfer-Encoding", "chunked") + c.Next() + } +} diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index d01f9d3..c1f3ac5 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -42,6 +42,9 @@ func (g *GameService) InitGame(c context.Context) (*repository.Game, error) { if err != nil { return nil, err } + + // sent sse + return game, nil } From 6194dd4308e1c79850e77cb7698bd4dd4315bdf5 Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 28 Nov 2023 11:13:40 +0800 Subject: [PATCH 45/89] feat: Game event SSE use dependency injection --- Backend/cmd/app/main.go | 12 +- .../service/delivery/http/v1/game_handler.go | 108 +----------------- .../service/delivery/http/v1/sse_handler.go | 74 ++++++++++++ 3 files changed, 81 insertions(+), 113 deletions(-) create mode 100644 Backend/service/delivery/http/v1/sse_handler.go diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index 0d306c4..07dbd23 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -19,16 +19,7 @@ func main() { db := config.NewDatabase() engine := gin.Default() - //go func() { - // for { - // time.Sleep(time.Second * 10) - // now := time.Now().Format("2006-01-02 15:04:05") - // currentTime := fmt.Sprintf("The Current Time Is %v", now) - // - // // Send current time to clients message channel - // stream.Message <- currentTime - // } - //}() + sse := http.NewSSEServer() gameRepo := mysqlRepo.NewGameRepository(db) playerRepo := mysqlRepo.NewPlayerRepository(db) @@ -66,6 +57,7 @@ func main() { &http.GameHandlerOptions{ Engine: engine, Service: gameService, + SSE: sse, }, ) diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index b9b29c3..f686f34 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -1,9 +1,7 @@ package http import ( - "fmt" "io" - "log" "net/http" "strconv" @@ -14,39 +12,23 @@ import ( type GameHandler struct { gameService service.GameService + SSE *Event } type GameHandlerOptions struct { Engine *gin.Engine Service service.GameService + SSE *Event } func RegisterGameHandler(opts *GameHandlerOptions) { handler := &GameHandler{ gameService: opts.Service, + SSE: opts.SSE, } - stream := NewServer() opts.Engine.POST("/api/v1/games", handler.StartGame) - //opts.Engine.GET("/api/v1/games/:gameId/event", handler.GameEvent) - opts.Engine.GET("/api/v1/games/:gameId/event", HeadersMiddleware(), stream.serveHTTP(), func(c *gin.Context) { - v, ok := c.Get("clientChan") - if !ok { - return - } - clientChan, ok := v.(ClientChan) - if !ok { - return - } - c.Stream(func(w io.Writer) bool { - // Stream message to client from message channel - if msg, ok := <-clientChan; ok { - c.SSEvent("message", msg) - return true - } - return false - }) - }) + opts.Engine.GET("/api/v1/games/:gameId/event", HeadersMiddleware(), opts.SSE.serveHTTP(), handler.GameEvent) } // StartGame godoc @@ -87,7 +69,7 @@ func (g *GameHandler) StartGame(c *gin.Context) { return } - c.SSEvent("message", gin.H{"message": "Game started"}) + g.SSE.Message <- "Game started" c.JSON(http.StatusOK, gin.H{ "Id": game.Id, @@ -125,14 +107,6 @@ func (g *GameHandler) DeleteGame(c *gin.Context) { } func (g *GameHandler) GameEvent(c *gin.Context) { - c.Writer.Header().Set("Content-Type", "text/event-stream") - c.Writer.Header().Set("Cache-Control", "no-cache") - c.Writer.Header().Set("Connection", "keep-alive") - c.Writer.Header().Set("Transfer-Encoding", "chunked") - - stream := NewServer() - stream.serveHTTP() - v, ok := c.Get("clientChan") if !ok { return @@ -149,76 +123,4 @@ func (g *GameHandler) GameEvent(c *gin.Context) { } return false }) - gameId, _ := strconv.Atoi(c.Param("gameId")) - gameStr, _ := fmt.Scanf("Game started %s", &gameId) - c.SSEvent("message", gameStr) - -} - -type ClientChan chan string - -type Event struct { - Message chan string - NewClients chan chan string - ClosedClients chan chan string - TotalClients map[chan string]bool -} - -func (stream *Event) listen() { - for { - select { - case client := <-stream.NewClients: - stream.TotalClients[client] = true - log.Printf("Client added. %d registered clients", len(stream.TotalClients)) - - case client := <-stream.ClosedClients: - delete(stream.TotalClients, client) - close(client) - log.Printf("Removed client. %d registered clients", len(stream.TotalClients)) - - case eventMsg := <-stream.Message: - for clientMessageChan := range stream.TotalClients { - clientMessageChan <- eventMsg - } - } - } -} - -func (stream *Event) serveHTTP() gin.HandlerFunc { - return func(c *gin.Context) { - clientChan := make(ClientChan) - - stream.NewClients <- clientChan - - defer func() { - stream.ClosedClients <- clientChan - }() - - c.Set("clientChan", clientChan) - - c.Next() - } -} - -func NewServer() (event *Event) { - event = &Event{ - Message: make(chan string), - NewClients: make(chan chan string), - ClosedClients: make(chan chan string), - TotalClients: make(map[chan string]bool), - } - - go event.listen() - - return -} - -func HeadersMiddleware() gin.HandlerFunc { - return func(c *gin.Context) { - c.Writer.Header().Set("Content-Type", "text/event-stream") - c.Writer.Header().Set("Cache-Control", "no-cache") - c.Writer.Header().Set("Connection", "keep-alive") - c.Writer.Header().Set("Transfer-Encoding", "chunked") - c.Next() - } } diff --git a/Backend/service/delivery/http/v1/sse_handler.go b/Backend/service/delivery/http/v1/sse_handler.go new file mode 100644 index 0000000..3c077c6 --- /dev/null +++ b/Backend/service/delivery/http/v1/sse_handler.go @@ -0,0 +1,74 @@ +package http + +import ( + "github.com/gin-gonic/gin" + "log" +) + +type Event struct { + Message chan string + NewClients chan chan string + ClosedClients chan chan string + TotalClients map[chan string]bool +} + +type ClientChan chan string + +func NewSSEServer() (event *Event) { + event = &Event{ + Message: make(chan string), + NewClients: make(chan chan string), + ClosedClients: make(chan chan string), + TotalClients: make(map[chan string]bool), + } + + go event.listen() + + return +} + +func (stream *Event) listen() { + for { + select { + case client := <-stream.NewClients: + stream.TotalClients[client] = true + log.Printf("Client added. %d registered clients", len(stream.TotalClients)) + + case client := <-stream.ClosedClients: + delete(stream.TotalClients, client) + close(client) + log.Printf("Removed client. %d registered clients", len(stream.TotalClients)) + + case eventMsg := <-stream.Message: + for clientMessageChan := range stream.TotalClients { + clientMessageChan <- eventMsg + } + } + } +} + +func (stream *Event) serveHTTP() gin.HandlerFunc { + return func(c *gin.Context) { + clientChan := make(ClientChan) + + stream.NewClients <- clientChan + + defer func() { + stream.ClosedClients <- clientChan + }() + + c.Set("clientChan", clientChan) + + c.Next() + } +} + +func HeadersMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + c.Writer.Header().Set("Content-Type", "text/event-stream") + c.Writer.Header().Set("Cache-Control", "no-cache") + c.Writer.Header().Set("Connection", "keep-alive") + c.Writer.Header().Set("Transfer-Encoding", "chunked") + c.Next() + } +} From a3586318c81632119bc463a7b1ed5368779afc6b Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 28 Nov 2023 11:26:08 +0800 Subject: [PATCH 46/89] feat: Game Event Return json format --- .../service/delivery/http/v1/game_handler.go | 7 +++++-- Backend/service/delivery/http/v1/sse_handler.go | 17 ++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index f686f34..054409f 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -28,7 +28,7 @@ func RegisterGameHandler(opts *GameHandlerOptions) { } opts.Engine.POST("/api/v1/games", handler.StartGame) - opts.Engine.GET("/api/v1/games/:gameId/event", HeadersMiddleware(), opts.SSE.serveHTTP(), handler.GameEvent) + opts.Engine.GET("/api/v1/games/:gameId/events", HeadersMiddleware(), opts.SSE.serveHTTP(), handler.GameEvent) } // StartGame godoc @@ -69,7 +69,10 @@ func (g *GameHandler) StartGame(c *gin.Context) { return } - g.SSE.Message <- "Game started" + g.SSE.Message <- gin.H{ + "message": "Game started", + "game": game, + } c.JSON(http.StatusOK, gin.H{ "Id": game.Id, diff --git a/Backend/service/delivery/http/v1/sse_handler.go b/Backend/service/delivery/http/v1/sse_handler.go index 3c077c6..67b4fd0 100644 --- a/Backend/service/delivery/http/v1/sse_handler.go +++ b/Backend/service/delivery/http/v1/sse_handler.go @@ -1,25 +1,26 @@ package http import ( + "encoding/json" "github.com/gin-gonic/gin" "log" ) type Event struct { - Message chan string + Message chan map[string]any NewClients chan chan string ClosedClients chan chan string - TotalClients map[chan string]bool + TotalClients map[chan string]any } type ClientChan chan string func NewSSEServer() (event *Event) { event = &Event{ - Message: make(chan string), + Message: make(chan map[string]any), NewClients: make(chan chan string), ClosedClients: make(chan chan string), - TotalClients: make(map[chan string]bool), + TotalClients: make(map[chan string]any), } go event.listen() @@ -40,8 +41,14 @@ func (stream *Event) listen() { log.Printf("Removed client. %d registered clients", len(stream.TotalClients)) case eventMsg := <-stream.Message: + jsonMsg, err := json.Marshal(eventMsg) + if err != nil { + // 处理错误 + continue + } + for clientMessageChan := range stream.TotalClients { - clientMessageChan <- eventMsg + clientMessageChan <- string(jsonMsg) } } } From 2cc2deebcadab7f921afd770ef928d33aeef3998 Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 28 Nov 2023 13:30:35 +0800 Subject: [PATCH 47/89] feat: Send event to individual game id --- .../service/delivery/http/v1/game_handler.go | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 054409f..f664830 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -1,6 +1,8 @@ package http import ( + "encoding/json" + "fmt" "io" "net/http" "strconv" @@ -71,7 +73,9 @@ func (g *GameHandler) StartGame(c *gin.Context) { g.SSE.Message <- gin.H{ "message": "Game started", - "game": game, + "status": "started", + "gameId": strconv.Itoa(game.Id), + //"game": game, } c.JSON(http.StatusOK, gin.H{ @@ -110,20 +114,52 @@ func (g *GameHandler) DeleteGame(c *gin.Context) { } func (g *GameHandler) GameEvent(c *gin.Context) { + gameId, err := strconv.Atoi(c.Param("gameId")) + if err != nil { + return + } + v, ok := c.Get("clientChan") if !ok { return } + clientChan, ok := v.(ClientChan) if !ok { return } + c.Stream(func(w io.Writer) bool { // Stream message to client from message channel if msg, ok := <-clientChan; ok { - c.SSEvent("message", msg) + DD(msg) + data := GameSSERequest{} + err := json.Unmarshal([]byte(msg), &data) + if err != nil { + + } + DD(data) + + if data.GameId == gameId { + c.SSEvent("message", msg) + } return true } return false }) } + +type GameSSERequest struct { + GameId int `json:"gameId,string"` + Message string `json:"message"` + Status string `json:"status"` +} + +func VarDD(x interface{}) { + fmt.Printf("%T\n", x) +} + +func DD(x interface{}) { + VarDD(x) + fmt.Printf("%+v\n", x) +} From 40989b2a0f2307be1963fb9978ae68331cb9cd19 Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 28 Nov 2023 13:37:34 +0800 Subject: [PATCH 48/89] stash: clean debug and fix test --- Backend/cmd/app/docs/docs.go | 47 +++++++++++++++++++ .../service/delivery/http/v1/game_handler.go | 25 +++++----- Backend/tests/e2e/suite_test.go | 2 + 3 files changed, 60 insertions(+), 14 deletions(-) diff --git a/Backend/cmd/app/docs/docs.go b/Backend/cmd/app/docs/docs.go index d682948..c718da6 100644 --- a/Backend/cmd/app/docs/docs.go +++ b/Backend/cmd/app/docs/docs.go @@ -97,9 +97,56 @@ const docTemplate = `{ } } } + }, + "/api/v1/games/{gameId}/events": { + "get": { + "description": "Get game events", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "games" + ], + "summary": "Get game events", + "parameters": [ + { + "type": "integer", + "description": "Game ID", + "name": "gameId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.GameSSERequest" + } + } + } + } } }, "definitions": { + "http.GameSSERequest": { + "type": "object", + "properties": { + "gameId": { + "type": "string", + "example": "0" + }, + "message": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, "request.CreateGameRequest": { "type": "object", "properties": { diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index f664830..798ed8b 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -2,8 +2,8 @@ package http import ( "encoding/json" - "fmt" "io" + "log" "net/http" "strconv" @@ -113,6 +113,15 @@ func (g *GameHandler) DeleteGame(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "Game deleted"}) } +// GameEvent godoc +// @Summary Get game events +// @Description Get game events +// @Tags games +// @Accept json +// @Produce json +// @Param gameId path int true "Game ID" +// @Success 200 {object} GameSSERequest +// @Router /api/v1/games/{gameId}/events [get] func (g *GameHandler) GameEvent(c *gin.Context) { gameId, err := strconv.Atoi(c.Param("gameId")) if err != nil { @@ -130,15 +139,12 @@ func (g *GameHandler) GameEvent(c *gin.Context) { } c.Stream(func(w io.Writer) bool { - // Stream message to client from message channel if msg, ok := <-clientChan; ok { - DD(msg) data := GameSSERequest{} err := json.Unmarshal([]byte(msg), &data) if err != nil { - + log.Fatalf(err.Error()) } - DD(data) if data.GameId == gameId { c.SSEvent("message", msg) @@ -154,12 +160,3 @@ type GameSSERequest struct { Message string `json:"message"` Status string `json:"status"` } - -func VarDD(x interface{}) { - fmt.Printf("%T\n", x) -} - -func DD(x interface{}) { - VarDD(x) - fmt.Printf("%+v\n", x) -} diff --git a/Backend/tests/e2e/suite_test.go b/Backend/tests/e2e/suite_test.go index a2e1735..fa68ba3 100644 --- a/Backend/tests/e2e/suite_test.go +++ b/Backend/tests/e2e/suite_test.go @@ -79,6 +79,7 @@ func (suite *IntegrationTestSuite) SetupSuite() { seeders.SeederCards(db) engine := gin.Default() + sse := v1.NewSSEServer() gameRepo := mysqlRepo.NewGameRepository(db) playerRepo := mysqlRepo.NewPlayerRepository(db) @@ -116,6 +117,7 @@ func (suite *IntegrationTestSuite) SetupSuite() { &v1.GameHandlerOptions{ Engine: engine, Service: gameService, + SSE: sse, }, ) From 094b8208464828ff1a45392008459ae0bb89ebcb Mon Sep 17 00:00:00 2001 From: KenLin Date: Fri, 5 Jan 2024 14:56:23 +0800 Subject: [PATCH 49/89] feat: Get the first event when starting SSE --- Backend/cmd/app/main.go | 1 + Backend/service/delivery/http/v1/game_handler.go | 7 ++++++- Backend/service/delivery/http/v1/player_handler.go | 9 +++++++++ Backend/service/delivery/http/v1/sse_handler.go | 1 - Backend/service/service/game_service.go | 3 --- Backend/tests/e2e/suite_test.go | 1 + 6 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index 07dbd23..9cc8b35 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -78,6 +78,7 @@ func main() { &http.PlayerHandlerOptions{ Engine: engine, Service: playerService, + SSE: sse, }, ) engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 798ed8b..c217c87 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -75,7 +75,6 @@ func (g *GameHandler) StartGame(c *gin.Context) { "message": "Game started", "status": "started", "gameId": strconv.Itoa(game.Id), - //"game": game, } c.JSON(http.StatusOK, gin.H{ @@ -138,6 +137,12 @@ func (g *GameHandler) GameEvent(c *gin.Context) { return } + g.SSE.Message <- gin.H{ + "message": "這邊要去取得最新的狀態", + "status": "?", + "gameId": strconv.Itoa(gameId), + } + c.Stream(func(w io.Writer) bool { if msg, ok := <-clientChan; ok { data := GameSSERequest{} diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 17f840c..cde1da8 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -11,16 +11,19 @@ import ( type PlayerHandler struct { playerService service.PlayerService + SSE *Event } type PlayerHandlerOptions struct { Engine *gin.Engine Service service.PlayerService + SSE *Event } func RegisterPlayerHandler(opts *PlayerHandlerOptions) { handler := &PlayerHandler{ playerService: opts.Service, + SSE: opts.SSE, } opts.Engine.POST("/api/v1/players/:playerId/player-cards", handler.PlayCard) @@ -51,6 +54,12 @@ func (p *PlayerHandler) PlayCard(c *gin.Context) { return } + p.SSE.Message <- gin.H{ + "message": "Game started", + "status": "started", + "gameId": "1", // TODO: get gameId from request + } + c.JSON(http.StatusOK, gin.H{ "result": result, }) diff --git a/Backend/service/delivery/http/v1/sse_handler.go b/Backend/service/delivery/http/v1/sse_handler.go index 67b4fd0..1c74a6d 100644 --- a/Backend/service/delivery/http/v1/sse_handler.go +++ b/Backend/service/delivery/http/v1/sse_handler.go @@ -43,7 +43,6 @@ func (stream *Event) listen() { case eventMsg := <-stream.Message: jsonMsg, err := json.Marshal(eventMsg) if err != nil { - // 处理错误 continue } diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index c1f3ac5..afd1ead 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -43,8 +43,6 @@ func (g *GameService) InitGame(c context.Context) (*repository.Game, error) { return nil, err } - // sent sse - return game, nil } @@ -122,5 +120,4 @@ func (g *GameService) DeleteGame(c context.Context, id int) error { return err } return nil - } diff --git a/Backend/tests/e2e/suite_test.go b/Backend/tests/e2e/suite_test.go index fa68ba3..49b6afe 100644 --- a/Backend/tests/e2e/suite_test.go +++ b/Backend/tests/e2e/suite_test.go @@ -132,6 +132,7 @@ func (suite *IntegrationTestSuite) SetupSuite() { &v1.PlayerHandlerOptions{ Engine: engine, Service: playerService, + SSE: sse, }, ) From ca55b7aff1ba280aacb7077f030dc90390d40bd7 Mon Sep 17 00:00:00 2001 From: KenLin Date: Fri, 5 Jan 2024 16:37:55 +0800 Subject: [PATCH 50/89] feat: After creating the game, SSE can get the ID of the first player to play the card --- Backend/service/delivery/http/v1/game_handler.go | 12 +++++++++--- Backend/service/service/game_service.go | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index c217c87..6e75db9 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -71,10 +71,16 @@ func (g *GameHandler) StartGame(c *gin.Context) { return } + game, err = g.gameService.GetGameById(c, game.Id) + if err != nil { + return + } + g.SSE.Message <- gin.H{ - "message": "Game started", - "status": "started", - "gameId": strconv.Itoa(game.Id), + "message": "Game started", + "status": "started", + "gameId": strconv.Itoa(game.Id), + "next_player": game.Players[0].Id, } c.JSON(http.StatusOK, gin.H{ diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index afd1ead..4722b24 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -107,7 +107,7 @@ func (g *GameService) CreateGame(c context.Context, game *repository.Game) (*rep } func (g *GameService) GetGameById(c context.Context, id int) (*repository.Game, error) { - game, err := g.GameRepo.GetGameById(c, id) + game, err := g.GameRepo.GetGameWithPlayers(c, id) if err != nil { return nil, err } From d0871c48a59ce4d23d3f781a7b9c5c9b7529a9cd Mon Sep 17 00:00:00 2001 From: KenLin Date: Fri, 5 Jan 2024 18:00:34 +0800 Subject: [PATCH 51/89] feat: When the player plays the card, SSE can get the ID of the next player to play the card --- Backend/cmd/app/main.go | 7 +-- .../service/delivery/http/v1/game_handler.go | 2 + .../delivery/http/v1/player_handler.go | 49 ++++++++++++++++--- 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index 9cc8b35..615da79 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -76,9 +76,10 @@ func main() { http.RegisterPlayerHandler( &http.PlayerHandlerOptions{ - Engine: engine, - Service: playerService, - SSE: sse, + Engine: engine, + Service: playerService, + GameService: gameService, + SSE: sse, }, ) engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 6e75db9..c8d0741 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -56,6 +56,7 @@ func (g *GameHandler) StartGame(c *gin.Context) { return } + // TODO 這邊可以優化 https://gorm.io/zh_CN/docs/associations.html if err := g.gameService.PlayerService.InitPlayers(c, game, req); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return @@ -151,6 +152,7 @@ func (g *GameHandler) GameEvent(c *gin.Context) { c.Stream(func(w io.Writer) bool { if msg, ok := <-clientChan; ok { + log.Printf("msg: %+v", msg) data := GameSSERequest{} err := json.Unmarshal([]byte(msg), &data) if err != nil { diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index cde1da8..602b4fe 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -2,6 +2,7 @@ package http import ( "github.com/Game-as-a-Service/The-Message/service/request" + "log" "net/http" "strconv" @@ -11,18 +12,21 @@ import ( type PlayerHandler struct { playerService service.PlayerService + gameService service.GameService SSE *Event } type PlayerHandlerOptions struct { - Engine *gin.Engine - Service service.PlayerService - SSE *Event + Engine *gin.Engine + Service service.PlayerService + GameService service.GameService + SSE *Event } func RegisterPlayerHandler(opts *PlayerHandlerOptions) { handler := &PlayerHandler{ playerService: opts.Service, + gameService: opts.GameService, SSE: opts.SSE, } @@ -54,10 +58,41 @@ func (p *PlayerHandler) PlayCard(c *gin.Context) { return } - p.SSE.Message <- gin.H{ - "message": "Game started", - "status": "started", - "gameId": "1", // TODO: get gameId from request + // TODO to Service + if result == false { + player, err := p.playerService.PlayerRepo.GetPlayer(c, playerId) + game, err := p.gameService.GameRepo.GetGameWithPlayers(c, player.GameId) + if err != nil { + return + } + + var currentPlayerIndex int + for index, gPlayer := range game.Players { + if gPlayer.Id == playerId { + currentPlayerIndex = index + break + } + } + // TODO 這邊要判斷超過index數量進入下一階段 + maxLen := len(game.Players) + if currentPlayerIndex+1 >= maxLen { + p.SSE.Message <- gin.H{ + "message": "傳遞", + "status": "傳遞", + "gameId": game.Id, + "next_player": game.Players[0].Id, + } + } else { + nextId := game.Players[currentPlayerIndex+1].Id + + p.SSE.Message <- gin.H{ + "message": "message", + "status": "command", + "gameId": "2", + "next_player": nextId, + } + } + log.Printf("End") } c.JSON(http.StatusOK, gin.H{ From 63ced45323729a82042861cfb75fc93895dd0d60 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Fri, 5 Jan 2024 18:31:35 +0800 Subject: [PATCH 52/89] Feat: Encapsulate the function of getting the migration file and solve the file path problem --- Backend/cmd/migrate/migrate.go | 4 +--- Backend/cmd/migrate/rollback.go | 4 +--- Backend/config/migration.go | 13 +++++++++++-- Backend/tests/e2e/suite_test.go | 4 +--- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Backend/cmd/migrate/migrate.go b/Backend/cmd/migrate/migrate.go index f76e57f..0e261f2 100644 --- a/Backend/cmd/migrate/migrate.go +++ b/Backend/cmd/migrate/migrate.go @@ -6,12 +6,10 @@ import ( "fmt" "github.com/Game-as-a-Service/The-Message/config" "net/url" - "os" ) func main() { - dir, _ := os.Getwd() - sourceURL := "file://" + dir + "/database/migrations" + sourceURL := config.GetSourceURL() dsn := config.BaseDSN() val := url.Values{} diff --git a/Backend/cmd/migrate/rollback.go b/Backend/cmd/migrate/rollback.go index 1bb94ad..3347b21 100644 --- a/Backend/cmd/migrate/rollback.go +++ b/Backend/cmd/migrate/rollback.go @@ -6,12 +6,10 @@ import ( "fmt" "github.com/Game-as-a-Service/The-Message/config" "net/url" - "os" ) func main() { - dir, _ := os.Getwd() - sourceURL := "file://" + dir + "/database/migrations" + sourceURL := config.GetSourceURL() dsn := config.BaseDSN() val := url.Values{} diff --git a/Backend/config/migration.go b/Backend/config/migration.go index 7c7c617..bfef445 100644 --- a/Backend/config/migration.go +++ b/Backend/config/migration.go @@ -10,6 +10,7 @@ import ( _ "github.com/joho/godotenv/autoload" "net/url" "os" + "strings" ) func NewMigration(dsn string, sourceURL string) (*migrate.Migrate, error) { @@ -41,8 +42,7 @@ func NewMigration(dsn string, sourceURL string) (*migrate.Migrate, error) { } func RunRefresh() { - dir, _ := os.Getwd() - sourceURL := "file://" + dir + "/database/migrations" + sourceURL := GetSourceURL() dsn := BaseDSN() val := url.Values{} @@ -67,3 +67,12 @@ func RunRefresh() { panic(err) } } + +func GetSourceURL() string { + dir, _ := os.Getwd() + dir = strings.ReplaceAll(dir, "\\", "/") + + sourceURL := "file://" + dir + "/database/migrations" + + return sourceURL +} diff --git a/Backend/tests/e2e/suite_test.go b/Backend/tests/e2e/suite_test.go index fa68ba3..886e32e 100644 --- a/Backend/tests/e2e/suite_test.go +++ b/Backend/tests/e2e/suite_test.go @@ -22,7 +22,6 @@ import ( "net/http" "net/http/httptest" "net/url" - "os" "testing" ) @@ -39,8 +38,7 @@ type IntegrationTestSuite struct { } func (suite *IntegrationTestSuite) SetupSuite() { - dir, _ := os.Getwd() - sourceURL := "file://" + dir + "/../../database/migrations" + sourceURL := config.GetSourceURL() err := godotenv.Load("../../.env") if err != nil { From 8c303dfa0030c6584355244ac9b5139b50d4b138 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Mon, 8 Jan 2024 15:07:49 +0800 Subject: [PATCH 53/89] Chore: Added .gitattributes and .editorconfig files to the project --- .editorconfig | 21 +++++++++++++++++++++ .gitattributes | 22 ++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ca3f339 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +; https://editorconfig.org/ + +root = true + +[*] +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[{Makefile,go.mod,go.sum,*.go,.gitmodules}] +indent_style = tab +indent_size = 4 + +[*.md] +indent_size = 4 +trim_trailing_whitespace = false + +[Dockerfile] +indent_size = 4 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d234cfe --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Treat all files in the Go repo as binary, with no git magic updating +# line endings. This produces predictable results in different environments. +# +# Windows users contributing to Go will need to use a modern version +# of git and editors capable of LF line endings. +# +# Windows .bat files are known to have multiple bugs when run with LF +# endings, and so they are checked in with CRLF endings, with a test +# in test/winbatch.go to catch problems. (See golang.org/issue/37791.) +# +# We'll prevent accidental CRLF line endings from entering the repo +# via the git-codereview gofmt checks and tests. +# +# See golang.org/issue/9281. + +* -text +*.go eol=lf diff=golang +*.ts diff=typescript +*.tsx diff=typescript +*.json diff=json +*.sql diff=sql +*.md text diff=markdown From 0649525cbf0e51ee6f23da71fc3e8c3105214777 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Mon, 8 Jan 2024 15:10:30 +0800 Subject: [PATCH 54/89] Chore: Added api document ignore to gitignore to avoid merge problem --- .gitignore | 1 + Backend/cmd/app/docs/docs.go | 73 ++++++++++++++-- Backend/cmd/app/docs/swagger.json | 141 ------------------------------ Backend/cmd/app/docs/swagger.yaml | 91 ------------------- 4 files changed, 66 insertions(+), 240 deletions(-) delete mode 100644 Backend/cmd/app/docs/swagger.json delete mode 100644 Backend/cmd/app/docs/swagger.yaml diff --git a/.gitignore b/.gitignore index 0f961ed..67cdc41 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Backend +Backend/cmd/app/docs/swagger.* Backend/.env /.idea/* vendor/ diff --git a/Backend/cmd/app/docs/docs.go b/Backend/cmd/app/docs/docs.go index c718da6..240b875 100644 --- a/Backend/cmd/app/docs/docs.go +++ b/Backend/cmd/app/docs/docs.go @@ -49,6 +49,38 @@ const docTemplate = `{ } } }, + "/api/v1/games/{gameId}/events": { + "get": { + "description": "Get game events", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "games" + ], + "summary": "Get game events", + "parameters": [ + { + "type": "integer", + "description": "Game ID", + "name": "gameId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.GameSSERequest" + } + } + } + } + }, "/api/v1/heartbeat": { "get": { "description": "Check if the server is alive", @@ -98,9 +130,9 @@ const docTemplate = `{ } } }, - "/api/v1/games/{gameId}/events": { - "get": { - "description": "Get game events", + "/api/v1/players/{playerId}/player-cards": { + "post": { + "description": "Play a card", "consumes": [ "application/json" ], @@ -108,23 +140,32 @@ const docTemplate = `{ "application/json" ], "tags": [ - "games" + "players" ], - "summary": "Get game events", + "summary": "Play a card", "parameters": [ { "type": "integer", - "description": "Game ID", - "name": "gameId", + "description": "Player ID", + "name": "playerId", "in": "path", "required": true + }, + { + "description": "Card ID", + "name": "card_id", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PlayCardRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/http.GameSSERequest" + "$ref": "#/definitions/request.PlayCardResponse" } } } @@ -169,6 +210,22 @@ const docTemplate = `{ } } }, + "request.PlayCardRequest": { + "type": "object", + "properties": { + "card_id": { + "type": "integer" + } + } + }, + "request.PlayCardResponse": { + "type": "object", + "properties": { + "result": { + "type": "boolean" + } + } + }, "request.PlayerCardsResponse": { "type": "object", "properties": { diff --git a/Backend/cmd/app/docs/swagger.json b/Backend/cmd/app/docs/swagger.json deleted file mode 100644 index 676c275..0000000 --- a/Backend/cmd/app/docs/swagger.json +++ /dev/null @@ -1,141 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "description": "This is an online version of the \"The Message\" board game backend API", - "title": "The Message API", - "contact": {} - }, - "host": "127.0.0.1:8080", - "paths": { - "/api/v1/games": { - "post": { - "description": "Start a new game", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "games" - ], - "summary": "Start a new game", - "parameters": [ - { - "description": "Players", - "name": "players", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.CreateGameRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/request.CreateGameResponse" - } - } - } - } - }, - "/api/v1/heartbeat": { - "get": { - "description": "Check if the server is alive", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "heartbeat" - ], - "summary": "Check if the server is alive", - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/api/v1/player/{id}/player-cards/": { - "get": { - "description": "GetPlayerCardsByPlayerId", - "produces": [ - "application/json" - ], - "tags": [ - "player_cards" - ], - "summary": "GetPlayerCards", - "parameters": [ - { - "type": "integer", - "description": "Player ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/request.PlayerCardsResponse" - } - } - } - } - } - }, - "definitions": { - "request.CreateGameRequest": { - "type": "object", - "properties": { - "players": { - "type": "array", - "items": { - "$ref": "#/definitions/request.PlayerInfo" - } - } - } - }, - "request.CreateGameResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "token": { - "type": "string" - } - } - }, - "request.PlayerCardsResponse": { - "type": "object", - "properties": { - "player_cards": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true - } - } - } - }, - "request.PlayerInfo": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - } - } - } - } -} \ No newline at end of file diff --git a/Backend/cmd/app/docs/swagger.yaml b/Backend/cmd/app/docs/swagger.yaml deleted file mode 100644 index ca7bf23..0000000 --- a/Backend/cmd/app/docs/swagger.yaml +++ /dev/null @@ -1,91 +0,0 @@ -definitions: - request.CreateGameRequest: - properties: - players: - items: - $ref: '#/definitions/request.PlayerInfo' - type: array - type: object - request.CreateGameResponse: - properties: - id: - type: string - token: - type: string - type: object - request.PlayerCardsResponse: - properties: - player_cards: - items: - additionalProperties: true - type: object - type: array - type: object - request.PlayerInfo: - properties: - id: - type: string - name: - type: string - type: object -host: 127.0.0.1:8080 -info: - contact: {} - description: This is an online version of the "The Message" board game backend API - title: The Message API -paths: - /api/v1/games: - post: - consumes: - - application/json - description: Start a new game - parameters: - - description: Players - in: body - name: players - required: true - schema: - $ref: '#/definitions/request.CreateGameRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/request.CreateGameResponse' - summary: Start a new game - tags: - - games - /api/v1/heartbeat: - get: - consumes: - - application/json - description: Check if the server is alive - produces: - - application/json - responses: - "204": - description: No Content - summary: Check if the server is alive - tags: - - heartbeat - /api/v1/player/{id}/player-cards/: - get: - description: GetPlayerCardsByPlayerId - parameters: - - description: Player ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/request.PlayerCardsResponse' - summary: GetPlayerCards - tags: - - player_cards -swagger: "2.0" From b034a5b6cb66fa59dc0807c12b247b7c18feb27b Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Mon, 8 Jan 2024 15:16:24 +0800 Subject: [PATCH 55/89] Chore: Added Github Actions to wait for MySQL to start and modify the mysql version to the median during acceptance_test --- .github/workflows/test-go-unit.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-go-unit.yml b/.github/workflows/test-go-unit.yml index c403626..4bcd5d2 100644 --- a/.github/workflows/test-go-unit.yml +++ b/.github/workflows/test-go-unit.yml @@ -68,7 +68,7 @@ jobs: services: mysql: - image: mysql:8.1.0 + image: mysql:8.1 env: MYSQL_ROOT_PASSWORD: ${{ secrets.DB_ROOT_PASSWORD }} MYSQL_DATABASE: test @@ -90,6 +90,12 @@ jobs: working-directory: ./Backend run: cp env .env + - name: Wait for MySQL + run: | + wget https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh + chmod +x wait-for-it.sh + ./wait-for-it.sh 127.0.0.1:3306 --timeout=60 + - name: Migration working-directory: ./Backend run: | @@ -104,4 +110,4 @@ jobs: uses: actions/upload-artifact@v2 with: name: acceptance-test-coverage-report - path: ./Backend/coverage.out \ No newline at end of file + path: ./Backend/coverage.out From 39e6cd5b46782f5f977c533062837743f83dd748 Mon Sep 17 00:00:00 2001 From: KenLin Date: Fri, 12 Jan 2024 18:02:10 +0800 Subject: [PATCH 56/89] feat: Add CHARSET and COLLATE to the migrations file --- Backend/database/migrations/000001_create_games_table.up.sql | 2 +- Backend/database/migrations/000002_create_players_table.up.sql | 2 +- Backend/database/migrations/000003_create_cards_table.up.sql | 2 +- Backend/database/migrations/000004_create_decks_table.up.sql | 2 +- .../database/migrations/000005_create_player_cards_table.up.sql | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Backend/database/migrations/000001_create_games_table.up.sql b/Backend/database/migrations/000001_create_games_table.up.sql index e3b9a27..3f09ef0 100644 --- a/Backend/database/migrations/000001_create_games_table.up.sql +++ b/Backend/database/migrations/000001_create_games_table.up.sql @@ -7,6 +7,6 @@ CREATE TABLE games created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, deleted_at DATETIME -) ENGINE=InnoDB; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/database/migrations/000002_create_players_table.up.sql b/Backend/database/migrations/000002_create_players_table.up.sql index bbbf9db..7bedbc6 100644 --- a/Backend/database/migrations/000002_create_players_table.up.sql +++ b/Backend/database/migrations/000002_create_players_table.up.sql @@ -10,6 +10,6 @@ CREATE TABLE players updated_at DATETIME NOT NULL, deleted_at DATETIME, FOREIGN KEY (game_id) REFERENCES games (id) -) ENGINE=InnoDB; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/database/migrations/000003_create_cards_table.up.sql b/Backend/database/migrations/000003_create_cards_table.up.sql index 4f1411b..b7cd9bc 100644 --- a/Backend/database/migrations/000003_create_cards_table.up.sql +++ b/Backend/database/migrations/000003_create_cards_table.up.sql @@ -8,6 +8,6 @@ CREATE TABLE cards created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, deleted_at DATETIME -) ENGINE=InnoDB; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/database/migrations/000004_create_decks_table.up.sql b/Backend/database/migrations/000004_create_decks_table.up.sql index 9af2164..f50684b 100644 --- a/Backend/database/migrations/000004_create_decks_table.up.sql +++ b/Backend/database/migrations/000004_create_decks_table.up.sql @@ -8,6 +8,6 @@ CREATE TABLE decks created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, deleted_at DATETIME -) ENGINE=InnoDB; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/database/migrations/000005_create_player_cards_table.up.sql b/Backend/database/migrations/000005_create_player_cards_table.up.sql index 54fd5f8..5ed141b 100644 --- a/Backend/database/migrations/000005_create_player_cards_table.up.sql +++ b/Backend/database/migrations/000005_create_player_cards_table.up.sql @@ -13,6 +13,6 @@ CREATE TABLE player_cards FOREIGN KEY (player_id) REFERENCES players (id), FOREIGN KEY (game_id) REFERENCES games (id), FOREIGN KEY (card_id) REFERENCES cards (id) -) ENGINE=InnoDB; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; From 414d5b3b674a761ecdf2afdbe9354e1b06dbec87 Mon Sep 17 00:00:00 2001 From: KenLin Date: Fri, 12 Jan 2024 18:17:43 +0800 Subject: [PATCH 57/89] fix: An incorrect file path is read when initiating the test --- Backend/config/migration.go | 1 + 1 file changed, 1 insertion(+) diff --git a/Backend/config/migration.go b/Backend/config/migration.go index bfef445..5914a91 100644 --- a/Backend/config/migration.go +++ b/Backend/config/migration.go @@ -70,6 +70,7 @@ func RunRefresh() { func GetSourceURL() string { dir, _ := os.Getwd() + dir = strings.SplitAfter(dir, "Backend")[0] dir = strings.ReplaceAll(dir, "\\", "/") sourceURL := "file://" + dir + "/database/migrations" From 1159761f621503969f414392ac4026695c02089f Mon Sep 17 00:00:00 2001 From: KenLin Date: Fri, 5 Jan 2024 18:54:24 +0800 Subject: [PATCH 58/89] feat: After the PlayCard API is successful, SSE can be passed to simply determine the next player and game stage --- .../service/delivery/http/v1/game_handler.go | 6 +++--- .../service/delivery/http/v1/player_handler.go | 17 +++++++++-------- .../service/repository/mysql/game_repository.go | 1 - .../repository/mysql/player_repository.go | 2 +- Backend/tests/e2e/suite_test.go | 7 ++++--- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index c8d0741..6fa719d 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -80,7 +80,7 @@ func (g *GameHandler) StartGame(c *gin.Context) { g.SSE.Message <- gin.H{ "message": "Game started", "status": "started", - "gameId": strconv.Itoa(game.Id), + "game_id": game.Id, "next_player": game.Players[0].Id, } @@ -147,7 +147,7 @@ func (g *GameHandler) GameEvent(c *gin.Context) { g.SSE.Message <- gin.H{ "message": "這邊要去取得最新的狀態", "status": "?", - "gameId": strconv.Itoa(gameId), + "game_id": gameId, } c.Stream(func(w io.Writer) bool { @@ -169,7 +169,7 @@ func (g *GameHandler) GameEvent(c *gin.Context) { } type GameSSERequest struct { - GameId int `json:"gameId,string"` + GameId int `json:"game_id,int"` Message string `json:"message"` Status string `json:"status"` } diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 602b4fe..764d11e 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -1,8 +1,8 @@ package http import ( + "fmt" "github.com/Game-as-a-Service/The-Message/service/request" - "log" "net/http" "strconv" @@ -59,7 +59,7 @@ func (p *PlayerHandler) PlayCard(c *gin.Context) { } // TODO to Service - if result == false { + if result { player, err := p.playerService.PlayerRepo.GetPlayer(c, playerId) game, err := p.gameService.GameRepo.GetGameWithPlayers(c, player.GameId) if err != nil { @@ -73,26 +73,27 @@ func (p *PlayerHandler) PlayCard(c *gin.Context) { break } } - // TODO 這邊要判斷超過index數量進入下一階段 + maxLen := len(game.Players) if currentPlayerIndex+1 >= maxLen { p.SSE.Message <- gin.H{ "message": "傳遞", "status": "傳遞", - "gameId": game.Id, + "game_id": game.Id, "next_player": game.Players[0].Id, } } else { nextId := game.Players[currentPlayerIndex+1].Id + message := fmt.Sprintf("玩家: %d 已出牌", nextId) + p.SSE.Message <- gin.H{ - "message": "message", - "status": "command", - "gameId": "2", + "message": message, + "status": "功能", + "game_id": game.Id, "next_player": nextId, } } - log.Printf("End") } c.JSON(http.StatusOK, gin.H{ diff --git a/Backend/service/repository/mysql/game_repository.go b/Backend/service/repository/mysql/game_repository.go index 8c0e143..b9aba54 100644 --- a/Backend/service/repository/mysql/game_repository.go +++ b/Backend/service/repository/mysql/game_repository.go @@ -2,7 +2,6 @@ package mysql import ( "context" - "github.com/Game-as-a-Service/The-Message/service/repository" "gorm.io/gorm" ) diff --git a/Backend/service/repository/mysql/player_repository.go b/Backend/service/repository/mysql/player_repository.go index b7ac24e..f774902 100644 --- a/Backend/service/repository/mysql/player_repository.go +++ b/Backend/service/repository/mysql/player_repository.go @@ -48,7 +48,7 @@ func (p *PlayerRepository) GetPlayersByGameId(ctx context.Context, id int) ([]*r func (p *PlayerRepository) GetPlayerWithPlayerCards(ctx context.Context, playerId int) (*repository.Player, error) { var player repository.Player - if err := p.db.Debug().Preload("PlayerCards").Preload("PlayerCards.Card").First(&player, playerId).Error; err != nil { + if err := p.db.Preload("PlayerCards").Preload("PlayerCards.Card").First(&player, playerId).Error; err != nil { return nil, err } return &player, nil diff --git a/Backend/tests/e2e/suite_test.go b/Backend/tests/e2e/suite_test.go index 49b6afe..9e0b3fd 100644 --- a/Backend/tests/e2e/suite_test.go +++ b/Backend/tests/e2e/suite_test.go @@ -130,9 +130,10 @@ func (suite *IntegrationTestSuite) SetupSuite() { v1.RegisterPlayerHandler( &v1.PlayerHandlerOptions{ - Engine: engine, - Service: playerService, - SSE: sse, + Engine: engine, + Service: playerService, + GameService: gameService, + SSE: sse, }, ) From 997608de5d7b84b8d147ac3046d9041896078fa1 Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 16 Jan 2024 00:51:37 +0800 Subject: [PATCH 59/89] feat: Add game_progresses table --- ...0006_create_game_progresses_table.down.sql | 5 +++++ ...000006_create_game_progresses_table.up.sql | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 Backend/database/migrations/000006_create_game_progresses_table.down.sql create mode 100644 Backend/database/migrations/000006_create_game_progresses_table.up.sql diff --git a/Backend/database/migrations/000006_create_game_progresses_table.down.sql b/Backend/database/migrations/000006_create_game_progresses_table.down.sql new file mode 100644 index 0000000..9b0ebbb --- /dev/null +++ b/Backend/database/migrations/000006_create_game_progresses_table.down.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP TABLE IF EXISTS game_progresses; + +COMMIT; diff --git a/Backend/database/migrations/000006_create_game_progresses_table.up.sql b/Backend/database/migrations/000006_create_game_progresses_table.up.sql new file mode 100644 index 0000000..f9f5ebf --- /dev/null +++ b/Backend/database/migrations/000006_create_game_progresses_table.up.sql @@ -0,0 +1,19 @@ +BEGIN; + +CREATE TABLE game_progresses +( + id INT AUTO_INCREMENT PRIMARY KEY, + player_id INT NOT NULL, + game_id INT NOT NULL, + card_id INT NOT NULL, + action VARCHAR(255) NOT NULL, + target_player_id INT, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME, + FOREIGN KEY (player_id) REFERENCES players (id), + FOREIGN KEY (game_id) REFERENCES games (id), + FOREIGN KEY (card_id) REFERENCES cards (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +COMMIT; From 80fcccea5fd1922e6db73bf8c9f49a9846ec2a54 Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 16 Jan 2024 01:33:05 +0800 Subject: [PATCH 60/89] feat: Add game table fields (status, current player id) and player table fields (status, order) --- .../000001_create_games_table.up.sql | 12 ++++++---- .../000002_create_players_table.up.sql | 18 +++++++------- .../000003_create_cards_table.up.sql | 12 +++++----- .../000004_create_decks_table.up.sql | 12 +++++----- .../000005_create_player_cards_table.up.sql | 22 ++++++++--------- ...000006_create_game_progresses_table.up.sql | 24 +++++++++---------- Backend/enums/game_status.go | 6 +++++ Backend/enums/player_status.go | 6 +++++ Backend/service/repository/game_repository.go | 14 ++++++----- .../service/repository/player_repository.go | 2 ++ Backend/service/service/game_service.go | 4 +++- Backend/service/service/player_service.go | 2 ++ 12 files changed, 79 insertions(+), 55 deletions(-) create mode 100644 Backend/enums/game_status.go create mode 100644 Backend/enums/player_status.go diff --git a/Backend/database/migrations/000001_create_games_table.up.sql b/Backend/database/migrations/000001_create_games_table.up.sql index 3f09ef0..7d8ab74 100644 --- a/Backend/database/migrations/000001_create_games_table.up.sql +++ b/Backend/database/migrations/000001_create_games_table.up.sql @@ -2,11 +2,13 @@ BEGIN; CREATE TABLE games ( - id INT AUTO_INCREMENT PRIMARY KEY, - token LONGTEXT NOT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - deleted_at DATETIME + id INT AUTO_INCREMENT PRIMARY KEY, + token LONGTEXT NOT NULL, + status VARCHAR(10) NOT NULL, + current_player_id INT, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/database/migrations/000002_create_players_table.up.sql b/Backend/database/migrations/000002_create_players_table.up.sql index 7bedbc6..3566df0 100644 --- a/Backend/database/migrations/000002_create_players_table.up.sql +++ b/Backend/database/migrations/000002_create_players_table.up.sql @@ -2,14 +2,16 @@ BEGIN; CREATE TABLE players ( - id INT AUTO_INCREMENT PRIMARY KEY, - game_id INT NOT NULL, - name VARCHAR(255) NOT NULL, - identity_card VARCHAR(255) NOT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - deleted_at DATETIME, - FOREIGN KEY (game_id) REFERENCES games (id) + id INT AUTO_INCREMENT PRIMARY KEY, + game_id INT NOT NULL, + name VARCHAR(255) NOT NULL, + order_number INT NOT NULL, + identity_card VARCHAR(255) NOT NULL, + status VARCHAR(10) NOT NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME, + FOREIGN KEY (game_id) REFERENCES games (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/database/migrations/000003_create_cards_table.up.sql b/Backend/database/migrations/000003_create_cards_table.up.sql index b7cd9bc..9ce71a1 100644 --- a/Backend/database/migrations/000003_create_cards_table.up.sql +++ b/Backend/database/migrations/000003_create_cards_table.up.sql @@ -2,12 +2,12 @@ BEGIN; CREATE TABLE cards ( - id INT AUTO_INCREMENT PRIMARY KEY, - name VARCHAR(255) NOT NULL, - color VARCHAR(255) NOT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - deleted_at DATETIME + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + color VARCHAR(255) NOT NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/database/migrations/000004_create_decks_table.up.sql b/Backend/database/migrations/000004_create_decks_table.up.sql index f50684b..3179477 100644 --- a/Backend/database/migrations/000004_create_decks_table.up.sql +++ b/Backend/database/migrations/000004_create_decks_table.up.sql @@ -2,12 +2,12 @@ BEGIN; CREATE TABLE decks ( - id INT AUTO_INCREMENT PRIMARY KEY, - game_id INT NOT NULL, - card_id INT NOT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - deleted_at DATETIME + id INT AUTO_INCREMENT PRIMARY KEY, + game_id INT NOT NULL, + card_id INT NOT NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/database/migrations/000005_create_player_cards_table.up.sql b/Backend/database/migrations/000005_create_player_cards_table.up.sql index 5ed141b..f5f6ef1 100644 --- a/Backend/database/migrations/000005_create_player_cards_table.up.sql +++ b/Backend/database/migrations/000005_create_player_cards_table.up.sql @@ -2,17 +2,17 @@ BEGIN; CREATE TABLE player_cards ( - id INT AUTO_INCREMENT PRIMARY KEY, - player_id INT NOT NULL, - game_id INT NOT NULL, - card_id INT NOT NULL, - type VARCHAR(255) NOT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - deleted_at DATETIME, - FOREIGN KEY (player_id) REFERENCES players (id), - FOREIGN KEY (game_id) REFERENCES games (id), - FOREIGN KEY (card_id) REFERENCES cards (id) + id INT AUTO_INCREMENT PRIMARY KEY, + player_id INT NOT NULL, + game_id INT NOT NULL, + card_id INT NOT NULL, + type VARCHAR(255) NOT NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME, + FOREIGN KEY (player_id) REFERENCES players (id), + FOREIGN KEY (game_id) REFERENCES games (id), + FOREIGN KEY (card_id) REFERENCES cards (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/database/migrations/000006_create_game_progresses_table.up.sql b/Backend/database/migrations/000006_create_game_progresses_table.up.sql index f9f5ebf..5551949 100644 --- a/Backend/database/migrations/000006_create_game_progresses_table.up.sql +++ b/Backend/database/migrations/000006_create_game_progresses_table.up.sql @@ -2,18 +2,18 @@ BEGIN; CREATE TABLE game_progresses ( - id INT AUTO_INCREMENT PRIMARY KEY, - player_id INT NOT NULL, - game_id INT NOT NULL, - card_id INT NOT NULL, - action VARCHAR(255) NOT NULL, - target_player_id INT, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - deleted_at DATETIME, - FOREIGN KEY (player_id) REFERENCES players (id), - FOREIGN KEY (game_id) REFERENCES games (id), - FOREIGN KEY (card_id) REFERENCES cards (id) + id INT AUTO_INCREMENT PRIMARY KEY, + player_id INT NOT NULL, + game_id INT NOT NULL, + card_id INT NOT NULL, + action VARCHAR(255) NOT NULL, + target_player_id INT, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME, + FOREIGN KEY (player_id) REFERENCES players (id), + FOREIGN KEY (game_id) REFERENCES games (id), + FOREIGN KEY (card_id) REFERENCES cards (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/enums/game_status.go b/Backend/enums/game_status.go new file mode 100644 index 0000000..418b53f --- /dev/null +++ b/Backend/enums/game_status.go @@ -0,0 +1,6 @@ +package enums + +const ( + GameStart = "開始遊戲" + GameEnd = "結束遊戲" +) diff --git a/Backend/enums/player_status.go b/Backend/enums/player_status.go new file mode 100644 index 0000000..ae650a9 --- /dev/null +++ b/Backend/enums/player_status.go @@ -0,0 +1,6 @@ +package enums + +const ( + PlayerStatusAlive = "生存" + PlayerStatusDead = "死亡" +) diff --git a/Backend/service/repository/game_repository.go b/Backend/service/repository/game_repository.go index 924e9f0..2b7e234 100644 --- a/Backend/service/repository/game_repository.go +++ b/Backend/service/repository/game_repository.go @@ -9,12 +9,14 @@ import ( type Game struct { gorm.Model - Id int `gorm:"primaryKey;auto_increment"` - Token string - Players []Player - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoCreateTime"` - DeletedAt gorm.DeletedAt + Id int `gorm:"primaryKey;auto_increment"` + Token string + Status string + CurrentPlayerId int + Players []Player + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoCreateTime"` + DeletedAt gorm.DeletedAt } type GameRepository interface { diff --git a/Backend/service/repository/player_repository.go b/Backend/service/repository/player_repository.go index d5f963d..50e646e 100644 --- a/Backend/service/repository/player_repository.go +++ b/Backend/service/repository/player_repository.go @@ -13,6 +13,8 @@ type Player struct { Name string GameId int `gorm:"foreignKey:GameId;references:Id"` IdentityCard string + Status string + OrderNumber int PlayerCards []PlayerCard CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoCreateTime"` diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index 4722b24..957d3a3 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -4,6 +4,7 @@ import ( "context" "crypto/rand" "encoding/hex" + "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" ) @@ -37,7 +38,8 @@ func (g *GameService) InitGame(c context.Context) (*repository.Game, error) { } game, err := g.CreateGame(c, &repository.Game{ - Token: token, + Token: token, + Status: enums.GameStart, }) if err != nil { return nil, err diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 37f3799..61b612a 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -36,6 +36,8 @@ func (p *PlayerService) InitPlayers(c context.Context, game *repository.Game, re Name: reqPlayer.Name, GameId: game.Id, IdentityCard: identityCards[i], + OrderNumber: i + 1, + Status: enums.PlayerStatusAlive, }) if err != nil { return err From b8e8e52bfb23b088a91844cac089f236f79143f6 Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 16 Jan 2024 01:55:26 +0800 Subject: [PATCH 61/89] feat: Add game status and current player when adding a game, as well as player status and order --- Backend/service/delivery/http/v1/game_handler.go | 12 ++++++++++-- Backend/service/repository/game_repository.go | 1 + Backend/service/repository/mysql/game_repository.go | 10 ++++++++++ Backend/service/service/game_service.go | 6 ++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 6fa719d..9420abe 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -62,6 +62,9 @@ func (g *GameHandler) StartGame(c *gin.Context) { return } + game, _ = g.gameService.GetGameById(c, game.Id) + g.gameService.UpdateCurrentPlayer(c, game, game.Players[0].Id) + if err := g.gameService.InitDeck(c, game); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return @@ -144,9 +147,14 @@ func (g *GameHandler) GameEvent(c *gin.Context) { return } + game, err := g.gameService.GetGameById(c, gameId) + if err != nil { + return + } + g.SSE.Message <- gin.H{ - "message": "這邊要去取得最新的狀態", - "status": "?", + "message": game.Status, + "status": game.Status, "game_id": gameId, } diff --git a/Backend/service/repository/game_repository.go b/Backend/service/repository/game_repository.go index 2b7e234..9e1c506 100644 --- a/Backend/service/repository/game_repository.go +++ b/Backend/service/repository/game_repository.go @@ -24,4 +24,5 @@ type GameRepository interface { CreateGame(ctx context.Context, game *Game) (*Game, error) DeleteGame(ctx context.Context, id int) error GetGameWithPlayers(ctx context.Context, id int) (*Game, error) + UpdateGame(ctx context.Context, game *Game) error } diff --git a/Backend/service/repository/mysql/game_repository.go b/Backend/service/repository/mysql/game_repository.go index b9aba54..a8b8fbc 100644 --- a/Backend/service/repository/mysql/game_repository.go +++ b/Backend/service/repository/mysql/game_repository.go @@ -58,3 +58,13 @@ func (g *GameRepository) GetGameWithPlayers(ctx context.Context, id int) (*repos } return &game, nil } + +func (g *GameRepository) UpdateGame(ctx context.Context, game *repository.Game) error { + result := g.db.Save(&game) + + if result.Error != nil { + return result.Error + } + + return nil +} diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index 957d3a3..0938e7e 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" + "github.com/gin-gonic/gin" ) type GameService struct { @@ -123,3 +124,8 @@ func (g *GameService) DeleteGame(c context.Context, id int) error { } return nil } + +func (g *GameService) UpdateCurrentPlayer(c *gin.Context, game *repository.Game, playerId int) { + game.CurrentPlayerId = playerId + g.GameRepo.UpdateGame(c, game) +} From 3fcc896747a7bde89cd19d2547ea9c01f380c59a Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 16 Jan 2024 02:01:06 +0800 Subject: [PATCH 62/89] chore: Clear useless code --- Backend/service/delivery/http/v1/card_handler.go | 8 -------- Backend/tests/e2e/player_card_api_test.go | 12 ------------ 2 files changed, 20 deletions(-) diff --git a/Backend/service/delivery/http/v1/card_handler.go b/Backend/service/delivery/http/v1/card_handler.go index 5f335d8..bb34d69 100644 --- a/Backend/service/delivery/http/v1/card_handler.go +++ b/Backend/service/delivery/http/v1/card_handler.go @@ -1,8 +1,6 @@ package http import ( - "encoding/json" - "fmt" "net/http" "strconv" @@ -53,12 +51,6 @@ func (p *CardHandler) GetPlayerCards(c *gin.Context) { } playerCardsInfo = append(playerCardsInfo, dict) } - jsonData, err := json.Marshal(playerCardsInfo) - fmt.Println(jsonData) - if err != nil { - fmt.Println("Error encoding JSON:", err) - return - } c.JSON(http.StatusOK, gin.H{"player_cards": playerCardsInfo}) } diff --git a/Backend/tests/e2e/player_card_api_test.go b/Backend/tests/e2e/player_card_api_test.go index 30d3143..a9af03f 100644 --- a/Backend/tests/e2e/player_card_api_test.go +++ b/Backend/tests/e2e/player_card_api_test.go @@ -67,15 +67,3 @@ func (suite *IntegrationTestSuite) TestGetPlayerCards() { } } } - -type PlayerCard struct { - ID string `json:"id"` - PlayerID string `json:"player_id"` - GameID string `json:"game_id"` - CardID string `json:"card_id"` - Type string `json:"type"` -} - -type Request_p struct { - Player Player `json:"player"` -} From e024febd35c33ca6a093f0313f3849429a63111c Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 16 Jan 2024 02:11:53 +0800 Subject: [PATCH 63/89] feat: Add check whether the current game is the turn of the player when playing cards --- .../service/delivery/http/v1/player_handler.go | 6 ++++++ .../service/repository/mysql/player_repository.go | 8 ++++++++ Backend/service/repository/player_repository.go | 2 ++ Backend/service/service/game_service.go | 8 +++++--- Backend/service/service/player_service.go | 15 +++++++++++++++ Backend/tests/e2e/player_api_test.go | 5 ++++- 6 files changed, 40 insertions(+), 4 deletions(-) diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 764d11e..e93036c 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -52,6 +52,12 @@ func (p *PlayerHandler) PlayCard(c *gin.Context) { return } + canPlay, err := p.playerService.CanPlayCard(c, playerId, req.CardID) + if !canPlay { + c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()}) + return + } + result, err := p.playerService.PlayCard(c, playerId, req.CardID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) diff --git a/Backend/service/repository/mysql/player_repository.go b/Backend/service/repository/mysql/player_repository.go index f774902..579ca0c 100644 --- a/Backend/service/repository/mysql/player_repository.go +++ b/Backend/service/repository/mysql/player_repository.go @@ -53,3 +53,11 @@ func (p *PlayerRepository) GetPlayerWithPlayerCards(ctx context.Context, playerI } return &player, nil } + +func (p *PlayerRepository) GetPlayerWithGame(ctx context.Context, playerId int) (*repository.Player, error) { + var player repository.Player + if err := p.db.Preload("Game").First(&player, playerId).Error; err != nil { + return nil, err + } + return &player, nil +} diff --git a/Backend/service/repository/player_repository.go b/Backend/service/repository/player_repository.go index 50e646e..fcfc8e0 100644 --- a/Backend/service/repository/player_repository.go +++ b/Backend/service/repository/player_repository.go @@ -16,6 +16,7 @@ type Player struct { Status string OrderNumber int PlayerCards []PlayerCard + Game *Game `gorm:"foreignKey:GameId;references:Id"` CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoCreateTime"` DeletedAt gorm.DeletedAt @@ -26,4 +27,5 @@ type PlayerRepository interface { GetPlayer(ctx context.Context, playerId int) (*Player, error) GetPlayersByGameId(ctx context.Context, id int) ([]*Player, error) GetPlayerWithPlayerCards(ctx context.Context, playerId int) (*Player, error) + GetPlayerWithGame(ctx context.Context, playerId int) (*Player, error) } diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index 0938e7e..21ab1e7 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -6,7 +6,6 @@ import ( "encoding/hex" "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" - "github.com/gin-gonic/gin" ) type GameService struct { @@ -125,7 +124,10 @@ func (g *GameService) DeleteGame(c context.Context, id int) error { return nil } -func (g *GameService) UpdateCurrentPlayer(c *gin.Context, game *repository.Game, playerId int) { +func (g *GameService) UpdateCurrentPlayer(c context.Context, game *repository.Game, playerId int) { game.CurrentPlayerId = playerId - g.GameRepo.UpdateGame(c, game) + err := g.GameRepo.UpdateGame(c, game) + if err != nil { + return + } } diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 61b612a..5dec329 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -2,6 +2,7 @@ package service import ( "context" + "errors" "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" "github.com/Game-as-a-Service/The-Message/service/request" @@ -117,3 +118,17 @@ func (p *PlayerService) PlayCard(c *gin.Context, playerId int, cardId int) (bool return true, nil } + +func (p *PlayerService) CanPlayCard(c *gin.Context, playerId int, cardId int) (bool, error) { + player, err := p.PlayerRepo.GetPlayerWithGame(c, playerId) + if err != nil { + return false, err + } + if player.Id != player.Game.CurrentPlayerId { + err = errors.New("尚未輪到你出牌") + return false, err + } + + return true, nil + +} diff --git a/Backend/tests/e2e/player_api_test.go b/Backend/tests/e2e/player_api_test.go index e5c0858..76b752b 100644 --- a/Backend/tests/e2e/player_api_test.go +++ b/Backend/tests/e2e/player_api_test.go @@ -6,6 +6,7 @@ import ( "github.com/Game-as-a-Service/The-Message/service/request" "github.com/stretchr/testify/assert" "net/http" + "strconv" ) func (suite *IntegrationTestSuite) TestPlayCardE2E() { @@ -21,11 +22,13 @@ func (suite *IntegrationTestSuite) TestPlayCardE2E() { _ = suite.playerServ.InitPlayers(context.TODO(), game, createGameRequest) _ = suite.gameServ.InitDeck(context.TODO(), game) _ = suite.gameServ.DrawCardsForAllPlayers(context.TODO(), game) + game, _ = suite.gameServ.GetGameById(context.TODO(), game.Id) + suite.gameServ.UpdateCurrentPlayer(context.TODO(), game, game.Players[0].Id) cards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), 2) playCardId := cards.PlayerCards[0].CardId // when - api := "/api/v1/players/2/player-cards" + api := "/api/v1/players/" + strconv.Itoa(game.Players[0].Id) + "/player-cards" playCardRequest := PlayCardRequest{CardId: playCardId} jsonBody, err := json.Marshal(playCardRequest) if err != nil { From d84c29dc5ec5172c979910f02df4dce0427f8de5 Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 16 Jan 2024 02:44:36 +0800 Subject: [PATCH 64/89] feat: Add take turns to play cards --- .../delivery/http/v1/player_handler.go | 2 ++ Backend/service/service/game_service.go | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index e93036c..ada005d 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -66,6 +66,8 @@ func (p *PlayerHandler) PlayCard(c *gin.Context) { // TODO to Service if result { + p.gameService.NextPlayer(c, playerId) + player, err := p.playerService.PlayerRepo.GetPlayer(c, playerId) game, err := p.gameService.GameRepo.GetGameWithPlayers(c, player.GameId) if err != nil { diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index 21ab1e7..b5bd998 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" + "github.com/gin-gonic/gin" ) type GameService struct { @@ -127,7 +128,34 @@ func (g *GameService) DeleteGame(c context.Context, id int) error { func (g *GameService) UpdateCurrentPlayer(c context.Context, game *repository.Game, playerId int) { game.CurrentPlayerId = playerId err := g.GameRepo.UpdateGame(c, game) + if err != nil { + panic(err) + } +} + +func (g *GameService) NextPlayer(c *gin.Context, playerId int) { + play, err := g.PlayerService.PlayerRepo.GetPlayer(c, playerId) + if err != nil { + return + } + + game, err := g.GameRepo.GetGameWithPlayers(c, play.GameId) if err != nil { return } + + var currentPlayerIndex int + for index, gPlayer := range game.Players { + if gPlayer.Id == playerId { + currentPlayerIndex = index + break + } + } + + if currentPlayerIndex+1 >= len(game.Players) { + g.UpdateCurrentPlayer(c, game, game.Players[0].Id) + return + } + + g.UpdateCurrentPlayer(c, game, game.Players[currentPlayerIndex+1].Id) } From 438b0682017c9df7f519ca43479eb4edece1b6cb Mon Sep 17 00:00:00 2001 From: KenLin Date: Tue, 16 Jan 2024 03:02:18 +0800 Subject: [PATCH 65/89] feat: Add that after the player plays a round of cards, the game status changes from the action card stage to the transmit intelligence card stage --- Backend/enums/game_status.go | 6 ++-- .../service/delivery/http/v1/game_handler.go | 9 ++++-- .../delivery/http/v1/player_handler.go | 32 +++---------------- Backend/service/service/game_service.go | 9 ++++++ 4 files changed, 24 insertions(+), 32 deletions(-) diff --git a/Backend/enums/game_status.go b/Backend/enums/game_status.go index 418b53f..c8fa67f 100644 --- a/Backend/enums/game_status.go +++ b/Backend/enums/game_status.go @@ -1,6 +1,8 @@ package enums const ( - GameStart = "開始遊戲" - GameEnd = "結束遊戲" + GameStart = "開始遊戲" + ActionCardStage = "功能牌階段" + TransmitIntelligenceStage = "情報牌階段" + GameEnd = "結束遊戲" ) diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 9420abe..b599900 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -2,6 +2,7 @@ package http import ( "encoding/json" + "github.com/Game-as-a-Service/The-Message/enums" "io" "log" "net/http" @@ -64,6 +65,7 @@ func (g *GameHandler) StartGame(c *gin.Context) { game, _ = g.gameService.GetGameById(c, game.Id) g.gameService.UpdateCurrentPlayer(c, game, game.Players[0].Id) + g.gameService.UpdateStatus(c, game, enums.ActionCardStage) if err := g.gameService.InitDeck(c, game); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) @@ -153,9 +155,10 @@ func (g *GameHandler) GameEvent(c *gin.Context) { } g.SSE.Message <- gin.H{ - "message": game.Status, - "status": game.Status, - "game_id": gameId, + "message": game.Status, + "status": game.Status, + "game_id": gameId, + "current_player": game.CurrentPlayerId, } c.Stream(func(w io.Writer) bool { diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index ada005d..6b13d3c 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -74,33 +74,11 @@ func (p *PlayerHandler) PlayCard(c *gin.Context) { return } - var currentPlayerIndex int - for index, gPlayer := range game.Players { - if gPlayer.Id == playerId { - currentPlayerIndex = index - break - } - } - - maxLen := len(game.Players) - if currentPlayerIndex+1 >= maxLen { - p.SSE.Message <- gin.H{ - "message": "傳遞", - "status": "傳遞", - "game_id": game.Id, - "next_player": game.Players[0].Id, - } - } else { - nextId := game.Players[currentPlayerIndex+1].Id - - message := fmt.Sprintf("玩家: %d 已出牌", nextId) - - p.SSE.Message <- gin.H{ - "message": message, - "status": "功能", - "game_id": game.Id, - "next_player": nextId, - } + p.SSE.Message <- gin.H{ + "message": fmt.Sprintf("玩家: %d 已出牌", playerId), + "status": game.Status, + "game_id": game.Id, + "next_player": game.CurrentPlayerId, } } diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index b5bd998..e27fcfc 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -154,8 +154,17 @@ func (g *GameService) NextPlayer(c *gin.Context, playerId int) { if currentPlayerIndex+1 >= len(game.Players) { g.UpdateCurrentPlayer(c, game, game.Players[0].Id) + g.UpdateStatus(c, game, enums.TransmitIntelligenceStage) return } g.UpdateCurrentPlayer(c, game, game.Players[currentPlayerIndex+1].Id) } + +func (g *GameService) UpdateStatus(c *gin.Context, game *repository.Game, stage string) { + game.Status = stage + err := g.GameRepo.UpdateGame(c, game) + if err != nil { + panic(err) + } +} From 0678cafbca329f685102a5fc0875bf2238fb4e53 Mon Sep 17 00:00:00 2001 From: KenLin Date: Wed, 17 Jan 2024 03:23:02 +0800 Subject: [PATCH 66/89] refactor: PlayCard API, use the method of query, update and push to reduce the number of database queries, and better understand --- Backend/cmd/app/main.go | 2 + .../delivery/http/v1/player_handler.go | 34 +++------- .../repository/mysql/player_repository.go | 9 +++ .../service/repository/player_repository.go | 1 + Backend/service/service/game_service.go | 28 ++++---- Backend/service/service/player_service.go | 64 ++++++++++++------- Backend/tests/e2e/suite_test.go | 7 +- 7 files changed, 80 insertions(+), 65 deletions(-) diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index 615da79..e79f8c3 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -42,6 +42,7 @@ func main() { playerService := service.NewPlayerService(&service.PlayerServiceOptions{ PlayerRepo: playerRepo, PlayerCardRepo: playerCardRepo, + GameRepo: gameRepo, }) gameService := service.NewGameService( @@ -52,6 +53,7 @@ func main() { DeckService: deckService, }, ) + playerService.GameServ = &gameService http.RegisterGameHandler( &http.GameHandlerOptions{ diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 6b13d3c..86efa19 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -46,43 +46,27 @@ func RegisterPlayerHandler(opts *PlayerHandlerOptions) { func (p *PlayerHandler) PlayCard(c *gin.Context) { playerId, _ := strconv.Atoi(c.Param("playerId")) var req request.PlayCardRequest - if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - canPlay, err := p.playerService.CanPlayCard(c, playerId, req.CardID) - if !canPlay { - c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()}) - return - } - - result, err := p.playerService.PlayCard(c, playerId, req.CardID) + game, card, err := p.playerService.PlayCard(c, playerId, req.CardID) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()}) return } // TODO to Service - if result { - p.gameService.NextPlayer(c, playerId) - - player, err := p.playerService.PlayerRepo.GetPlayer(c, playerId) - game, err := p.gameService.GameRepo.GetGameWithPlayers(c, player.GameId) - if err != nil { - return - } - - p.SSE.Message <- gin.H{ - "message": fmt.Sprintf("玩家: %d 已出牌", playerId), - "status": game.Status, - "game_id": game.Id, - "next_player": game.CurrentPlayerId, - } + p.SSE.Message <- gin.H{ + "game_id": game.Id, + "status": game.Status, + "message": fmt.Sprintf("玩家: %d 已出牌", playerId), + "card": card.Name, + "next_player": game.CurrentPlayerId, } c.JSON(http.StatusOK, gin.H{ - "result": result, + "result": true, }) } diff --git a/Backend/service/repository/mysql/player_repository.go b/Backend/service/repository/mysql/player_repository.go index 579ca0c..56fec6c 100644 --- a/Backend/service/repository/mysql/player_repository.go +++ b/Backend/service/repository/mysql/player_repository.go @@ -61,3 +61,12 @@ func (p *PlayerRepository) GetPlayerWithGame(ctx context.Context, playerId int) } return &player, nil } + +func (p *PlayerRepository) GetPlayerWithGamePlayersAndPlayerCardsCard(ctx context.Context, playerId int) (*repository.Player, error) { + var player repository.Player + if err := p.db.Preload("Game.Players").Preload("PlayerCards.Card").First(&player, playerId).Error; err != nil { + return nil, err + } + + return &player, nil +} diff --git a/Backend/service/repository/player_repository.go b/Backend/service/repository/player_repository.go index fcfc8e0..76751c3 100644 --- a/Backend/service/repository/player_repository.go +++ b/Backend/service/repository/player_repository.go @@ -28,4 +28,5 @@ type PlayerRepository interface { GetPlayersByGameId(ctx context.Context, id int) ([]*Player, error) GetPlayerWithPlayerCards(ctx context.Context, playerId int) (*Player, error) GetPlayerWithGame(ctx context.Context, playerId int) (*Player, error) + GetPlayerWithGamePlayersAndPlayerCardsCard(ctx context.Context, playerId int) (*Player, error) } diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index e27fcfc..f6b4a00 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -133,32 +133,26 @@ func (g *GameService) UpdateCurrentPlayer(c context.Context, game *repository.Ga } } -func (g *GameService) NextPlayer(c *gin.Context, playerId int) { - play, err := g.PlayerService.PlayerRepo.GetPlayer(c, playerId) - if err != nil { - return - } +func (g *GameService) NextPlayer(c *gin.Context, player *repository.Player) (*repository.Game, error) { + players := player.Game.Players - game, err := g.GameRepo.GetGameWithPlayers(c, play.GameId) - if err != nil { - return - } + currentPlayerId := player.Id var currentPlayerIndex int - for index, gPlayer := range game.Players { - if gPlayer.Id == playerId { + for index, gPlayer := range players { + if gPlayer.Id == currentPlayerId { currentPlayerIndex = index break } } - if currentPlayerIndex+1 >= len(game.Players) { - g.UpdateCurrentPlayer(c, game, game.Players[0].Id) - g.UpdateStatus(c, game, enums.TransmitIntelligenceStage) - return + if currentPlayerIndex+1 >= len(players) { + player.Game.CurrentPlayerId = players[0].Id + player.Game.Status = enums.TransmitIntelligenceStage + } else { + player.Game.CurrentPlayerId = players[currentPlayerIndex+1].Id } - - g.UpdateCurrentPlayer(c, game, game.Players[currentPlayerIndex+1].Id) + return player.Game, nil } func (g *GameService) UpdateStatus(c *gin.Context, game *repository.Game, stage string) { diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 5dec329..d5d05a4 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -14,12 +14,14 @@ type PlayerService struct { PlayerRepo repository.PlayerRepository PlayerCardRepo repository.PlayerCardRepository GameRepo repository.GameRepository + GameServ *GameService } type PlayerServiceOptions struct { PlayerRepo repository.PlayerRepository PlayerCardRepo repository.PlayerCardRepository GameRepo repository.GameRepository + GameServ *GameService } func NewPlayerService(opts *PlayerServiceOptions) PlayerService { @@ -27,6 +29,7 @@ func NewPlayerService(opts *PlayerServiceOptions) PlayerService { PlayerRepo: opts.PlayerRepo, PlayerCardRepo: opts.PlayerCardRepo, GameRepo: opts.GameRepo, + GameServ: opts.GameServ, } } @@ -91,44 +94,61 @@ func (p *PlayerService) CreatePlayerCard(c context.Context, card *repository.Pla return nil } -func (p *PlayerService) PlayCard(c *gin.Context, playerId int, cardId int) (bool, error) { - player, err := p.PlayerRepo.GetPlayer(c, playerId) +func (p *PlayerService) PlayCard(c *gin.Context, playerId int, cardId int) (*repository.Game, *repository.Card, error) { + player, err := p.PlayerRepo.GetPlayerWithGamePlayersAndPlayerCardsCard(c, playerId) if err != nil { - return false, err + return nil, nil, err } - cards, err := p.PlayerCardRepo.GetPlayerCards(c, &repository.PlayerCard{ - PlayerId: player.Id, - GameId: player.GameId, - Type: "hand", - CardId: cardId, - }) + result, err := p.CanPlayCard(c, player, cardId) + if !result || err != nil { + return nil, nil, err + } + + handCard, err := p.GetHandCardId(player, cardId) if err != nil { - return false, err + return nil, nil, err } - if len(*cards) == 0 { - return false, nil + game, err := p.GameServ.NextPlayer(c, player) + if err != nil { + return nil, nil, err } - err = p.PlayerCardRepo.DeletePlayerCard(c, (*cards)[0].Id) + err = p.PlayerCardRepo.DeletePlayerCard(c, handCard.Id) if err != nil { - return false, err + return nil, nil, err } - return true, nil + err = p.GameRepo.UpdateGame(c, game) + if err != nil { + return nil, nil, err + } + + return game, &handCard.Card, nil } -func (p *PlayerService) CanPlayCard(c *gin.Context, playerId int, cardId int) (bool, error) { - player, err := p.PlayerRepo.GetPlayerWithGame(c, playerId) - if err != nil { - return false, err +func (p *PlayerService) CanPlayCard(c *gin.Context, player *repository.Player, cardId int) (bool, error) { + if player.Game.Status == enums.GameEnd { + return false, errors.New("遊戲已結束") + } + + if player.Status == enums.PlayerStatusDead { + return false, errors.New("你已死亡") } - if player.Id != player.Game.CurrentPlayerId { - err = errors.New("尚未輪到你出牌") - return false, err + + if player.Game.CurrentPlayerId != player.Id { + return false, errors.New("尚未輪到你出牌") } return true, nil +} +func (p *PlayerService) GetHandCardId(player *repository.Player, cardId int) (*repository.PlayerCard, error) { + for _, card := range player.PlayerCards { + if card.CardId == cardId && card.Type == "hand" { + return &card, nil + } + } + return nil, errors.New("找不到手牌") } diff --git a/Backend/tests/e2e/suite_test.go b/Backend/tests/e2e/suite_test.go index d4c060b..b50fa0a 100644 --- a/Backend/tests/e2e/suite_test.go +++ b/Backend/tests/e2e/suite_test.go @@ -100,6 +100,7 @@ func (suite *IntegrationTestSuite) SetupSuite() { playerService := service.NewPlayerService(&service.PlayerServiceOptions{ PlayerRepo: playerRepo, PlayerCardRepo: playerCardRepo, + GameRepo: gameRepo, }) gameService := service.NewGameService( @@ -110,6 +111,7 @@ func (suite *IntegrationTestSuite) SetupSuite() { DeckService: deckService, }, ) + playerService.GameServ = &gameService v1.RegisterGameHandler( &v1.GameHandlerOptions{ @@ -148,7 +150,10 @@ func (suite *IntegrationTestSuite) SetupSuite() { func (suite *IntegrationTestSuite) TearDownSuite() { sqlDB, _ := suite.db.DB() - sqlDB.Close() + err := sqlDB.Close() + if err != nil { + return + } suite.server.Close() } From b224b272914699f71dbdc06deb390777cb1e0a3e Mon Sep 17 00:00:00 2001 From: Nicole Date: Thu, 18 Jan 2024 20:03:46 +0800 Subject: [PATCH 67/89] add accept api --- Backend/.DS_Store | Bin 0 -> 8196 bytes .../delivery/http/v1/player_handler.go | 18 ++++++++++++++- Backend/service/service/player_service.go | 21 +++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 Backend/.DS_Store diff --git a/Backend/.DS_Store b/Backend/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a78e606470c7454fdecc5d6bf2b0082816e3db06 GIT binary patch literal 8196 zcmeHMOHLa>5Us}M6ImdZUiLC86mo*GarQ_o*${vDNS+J|HjzM)X3sqk2VljD4cvnh z(a?&9(7a))Pbr47E>)6QBLFONxXmGIb25aVlWs^iy>zC&us7K?Ncv~j^Bu$pT=i( zF9+7ZSI{>)rZYN`tesn&SHxrP-p#}2{hi}Za+V63a-@z7G`N;AeWe-t5dDHCiI$hT zoLCRy-D~efZNJZvSMx~Z=a^P{dxv>h$OtP&*UO1IFqR0>wV(u zeQmtdT6D%NVCj*kcwX0kHbR>^@D3eldCgtk|9@$I|NjoJOeL!W>cF2mVA_L&K_A&} yRj;YyRop>yNB(xWTEApQ}+=fw|(IJQXTnk-=H5i@A|BEZ?8gF5h69rz8f_3O3( literal 0 HcmV?d00001 diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 17f840c..80f9bda 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -1,10 +1,11 @@ package http import ( - "github.com/Game-as-a-Service/The-Message/service/request" "net/http" "strconv" + "github.com/Game-as-a-Service/The-Message/service/request" + "github.com/Game-as-a-Service/The-Message/service/service" "github.com/gin-gonic/gin" ) @@ -24,6 +25,7 @@ func RegisterPlayerHandler(opts *PlayerHandlerOptions) { } opts.Engine.POST("/api/v1/players/:playerId/player-cards", handler.PlayCard) + opts.Engine.POST("/api/v1/players/:playerId/accept", handler.AcceptCard) } // PlayCard godoc @@ -55,3 +57,17 @@ func (p *PlayerHandler) PlayCard(c *gin.Context) { "result": result, }) } + +func (p *PlayerHandler) AcceptCard(c *gin.Context) { + playerId, _ := strconv.Atoi(c.Param("playerId")) + + result, err := p.playerService.AcceptCard(c, playerId) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "success", + }) +} diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 37f3799..f2f3cb3 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -2,11 +2,12 @@ package service import ( "context" + "math/rand" + "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" "github.com/Game-as-a-Service/The-Message/service/request" "github.com/gin-gonic/gin" - "math/rand" ) type PlayerService struct { @@ -115,3 +116,21 @@ func (p *PlayerService) PlayCard(c *gin.Context, playerId int, cardId int) (bool return true, nil } + +func (p *PlayerService) AcceptCard(c *gin.Context, playerId int) (bool, error) { + player, err := p.PlayerRepo.GetPlayer(c, playerId) + if err != nil { + return false, err + } + + cards, err := p.PlayerCardRepo.GetPlayerCards(c, &repository.PlayerCard{ + PlayerId: player.Id, + GameId: player.GameId, + Type: "hand", + }) + if err != nil { + return false, err + } + + return true, nil +} From e75b1d5a8af69a954ca3c29d304dc72bfeb78697 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Sat, 20 Jan 2024 00:25:20 +0800 Subject: [PATCH 68/89] Refactor: Removed heartbeat redundant breakpoints and tests --- Backend/tests/e2e/hearbeat_api_test.go | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/Backend/tests/e2e/hearbeat_api_test.go b/Backend/tests/e2e/hearbeat_api_test.go index a797671..764801d 100644 --- a/Backend/tests/e2e/hearbeat_api_test.go +++ b/Backend/tests/e2e/hearbeat_api_test.go @@ -12,7 +12,7 @@ import ( func TestHeartbeatEndpoint(t *testing.T) { // Initiate a new gin router gin.SetMode(gin.TestMode) - router := gin.New() + router := gin.Default() // Set up the heartbeat endpoint router.GET("/api/v1/heartbeat", func(c *gin.Context) { @@ -20,21 +20,13 @@ func TestHeartbeatEndpoint(t *testing.T) { }) // Prepare a new HTTP request - req, err := http.NewRequest("GET", "/api/v1/heartbeat", nil) - if err != nil { - t.Fatalf("Couldn't create request: %v", err) - } + req, _ := http.NewRequest("GET", "/api/v1/heartbeat", nil) // Create a response recorder - response := httptest.NewRecorder() - router.ServeHTTP(response, req) - - // Check if the status code is 204 - if response.Code != http.StatusNoContent { - t.Errorf("Expected status code %d but got %d", http.StatusNoContent, response.Code) - } + res := httptest.NewRecorder() + router.ServeHTTP(res, req) // Assert that the response body is empty - assert.Equal(t, http.StatusNoContent, response.Code) - assert.Empty(t, response.Body.String()) + assert.Equal(t, http.StatusNoContent, res.Code) + assert.Empty(t, res.Body.String()) } From 809964ba2d0d3291407a6c5ecfe6e15498660dfb Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Sat, 20 Jan 2024 01:03:08 +0800 Subject: [PATCH 69/89] Feat: Added api for transmit intelligence card --- Backend/enums/intelligence_types.go | 20 +++++ .../delivery/http/v1/player_handler.go | 60 +++++++++++++- .../mysql/player_card_repository.go | 59 +++++++++----- .../repository/player_card_repository.go | 5 +- Backend/service/request/game_request.go | 3 +- Backend/service/service/player_service.go | 78 +++++++++++++------ 6 files changed, 176 insertions(+), 49 deletions(-) create mode 100644 Backend/enums/intelligence_types.go diff --git a/Backend/enums/intelligence_types.go b/Backend/enums/intelligence_types.go new file mode 100644 index 0000000..fe95fe3 --- /dev/null +++ b/Backend/enums/intelligence_types.go @@ -0,0 +1,20 @@ +package enums + +const ( + SecretTelegram = 1 + DIRECT = 2 + DOCUMENT = 3 +) + +func ToString(secretTelegramType int) string { + switch secretTelegramType { + case SecretTelegram: + return "密電" + case DIRECT: + return "直達" + case DOCUMENT: + return "文件" + default: + return "" + } +} diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 86efa19..45ff881 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -2,12 +2,12 @@ package http import ( "fmt" + "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/request" - "net/http" - "strconv" - "github.com/Game-as-a-Service/The-Message/service/service" "github.com/gin-gonic/gin" + "net/http" + "strconv" ) type PlayerHandler struct { @@ -31,6 +31,7 @@ func RegisterPlayerHandler(opts *PlayerHandlerOptions) { } opts.Engine.POST("/api/v1/players/:playerId/player-cards", handler.PlayCard) + opts.Engine.POST("/api/v1/player/:playerId/transmit-intelligence", handler.TransmitIntelligence) } // PlayCard godoc @@ -70,3 +71,56 @@ func (p *PlayerHandler) PlayCard(c *gin.Context) { "result": true, }) } + +// TransmitIntelligence godoc +// @Summary Transmit intelligence +// @Description Transmit an intelligence card +// @Tags players +// @Accept json +// @Produce json +// @Param playerId path int true "Player ID" +// @Param card_id body request.PlayCardRequest true "Card ID" +// @Param intelligence_type body request.PlayCardRequest true "Intelligence Type" +// @Success 200 {object} request.PlayCardResponse +// @Router /api/v1/player/{playerId}/transmit-intelligence [post] +func (p *PlayerHandler) TransmitIntelligence(c *gin.Context) { + playerId, _ := strconv.Atoi(c.Param("playerId")) + var req request.PlayCardRequest + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()}) + return + } + + intelligenceType := enums.ToString(req.IntelligenceType) + + if intelligenceType == "" { + c.JSON(http.StatusBadRequest, gin.H{"message": "Invalid intelligence type"}) + return + } + + player, err := p.playerService.GetPlayerById(c, playerId) + + if err != nil || player == nil { + c.JSON(http.StatusInternalServerError, gin.H{"message": "Player not found"}) + return + } + + // Check card_id exists in player_cards + exist, err := p.playerService.CheckPlayerCardExist(c, playerId, player.GameId, req.CardID) + if err != nil || !exist { + c.JSON(http.StatusInternalServerError, gin.H{"message": "Card not found"}) + return + } + + ret, err := p.playerService.TransmitIntelligenceCard(c, playerId, player.GameId, req.CardID) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to transmit intelligence"}) + return + } + + c.JSON(http.StatusOK, gin.H{ + "result": ret, + "message": enums.ToString(req.IntelligenceType) + " intelligence transmitted", + }) +} diff --git a/Backend/service/repository/mysql/player_card_repository.go b/Backend/service/repository/mysql/player_card_repository.go index c92515a..a7f3b72 100644 --- a/Backend/service/repository/mysql/player_card_repository.go +++ b/Backend/service/repository/mysql/player_card_repository.go @@ -2,7 +2,7 @@ package mysql import ( "context" - + "errors" "github.com/Game-as-a-Service/The-Message/service/repository" "gorm.io/gorm" ) @@ -17,10 +17,8 @@ func NewPlayerCardRepository(db *gorm.DB) *PlayerCardRepository { } } -func (p PlayerCardRepository) GetPlayerCardById(ctx context.Context, id int) (*repository.PlayerCard, error) { - card := new(repository.PlayerCard) - - result := p.db.First(&card, "id = ?", id) +func (p PlayerCardRepository) CreatePlayerCard(ctx context.Context, card *repository.PlayerCard) (*repository.PlayerCard, error) { + result := p.db.Create(&card) if result.Error != nil { return nil, result.Error @@ -29,38 +27,51 @@ func (p PlayerCardRepository) GetPlayerCardById(ctx context.Context, id int) (*r return card, nil } -func (p PlayerCardRepository) GetPlayerCardsByGameId(ctx context.Context, id int) ([]*repository.PlayerCard, error) { - var cards []*repository.PlayerCard +func (p PlayerCardRepository) DeletePlayerCard(ctx context.Context, id int) error { + card := new(repository.PlayerCard) - result := p.db.Find(&cards, "game_id = ?", id) + result := p.db.Delete(&card, "id = ?", id) if result.Error != nil { - return nil, result.Error + return result.Error } - return cards, nil + return nil } -func (p PlayerCardRepository) CreatePlayerCard(ctx context.Context, card *repository.PlayerCard) (*repository.PlayerCard, error) { - result := p.db.Create(&card) +func (p PlayerCardRepository) DeletePlayerCardByPlayerIdAndCardId(ctx context.Context, playerId int, gameId int, cardId int) (bool, error) { + card := new(repository.PlayerCard) + result := p.db.Delete(&card, "player_id = ? AND game_id = ? AND card_id = ?", playerId, gameId, cardId) if result.Error != nil { - return nil, result.Error + return false, result.Error } - return card, nil + return true, nil } -func (p PlayerCardRepository) DeletePlayerCard(ctx context.Context, id int) error { +func (p *PlayerCardRepository) ExistPlayerCardByPlayerIdAndCardId(ctx context.Context, playerId int, gameId int, cardId int) (bool, error) { + var card repository.PlayerCard + result := p.db.First(&card, "player_id = ? AND game_id = ? AND card_id = ?", playerId, gameId, cardId) + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return false, nil + } + return false, result.Error + } + return true, nil +} + +func (p PlayerCardRepository) GetPlayerCardById(ctx context.Context, id int) (*repository.PlayerCard, error) { card := new(repository.PlayerCard) - result := p.db.Delete(&card, "id = ?", id) + result := p.db.First(&card, "id = ?", id) if result.Error != nil { - return result.Error + return nil, result.Error } - return nil + return card, nil } func (p *PlayerCardRepository) GetPlayerCards(ctx context.Context, playerCard *repository.PlayerCard) (*[]repository.PlayerCard, error) { @@ -72,3 +83,15 @@ func (p *PlayerCardRepository) GetPlayerCards(ctx context.Context, playerCard *r return playerCards, nil } + +func (p PlayerCardRepository) GetPlayerCardsByGameId(ctx context.Context, id int) ([]*repository.PlayerCard, error) { + var cards []*repository.PlayerCard + + result := p.db.Find(&cards, "game_id = ?", id) + + if result.Error != nil { + return nil, result.Error + } + + return cards, nil +} diff --git a/Backend/service/repository/player_card_repository.go b/Backend/service/repository/player_card_repository.go index b417f2c..c3aa657 100644 --- a/Backend/service/repository/player_card_repository.go +++ b/Backend/service/repository/player_card_repository.go @@ -2,9 +2,8 @@ package repository import ( "context" - "time" - "gorm.io/gorm" + "time" ) type PlayerCard struct { @@ -26,5 +25,7 @@ type PlayerCardRepository interface { GetPlayerCardsByGameId(ctx context.Context, id int) ([]*PlayerCard, error) CreatePlayerCard(ctx context.Context, card *PlayerCard) (*PlayerCard, error) DeletePlayerCard(ctx context.Context, id int) error + DeletePlayerCardByPlayerIdAndCardId(ctx context.Context, playerId int, gameId int, cardId int) (bool, error) + ExistPlayerCardByPlayerIdAndCardId(ctx context.Context, playerId int, gameId int, cardId int) (bool, error) GetPlayerCards(ctx context.Context, playerCard *PlayerCard) (*[]PlayerCard, error) } diff --git a/Backend/service/request/game_request.go b/Backend/service/request/game_request.go index 4d4e3c2..e17d5c8 100644 --- a/Backend/service/request/game_request.go +++ b/Backend/service/request/game_request.go @@ -19,5 +19,6 @@ type PlayCardResponse struct { } type PlayCardRequest struct { - CardID int `json:"card_id"` + CardID int `json:"card_id"` + IntelligenceType int `json:"intelligence_type"` } diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index d5d05a4..6fad64a 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -70,6 +70,32 @@ func (p *PlayerService) ShuffleIdentityCards(cards []string) []string { return shuffledCards } +func (p *PlayerService) CanPlayCard(c *gin.Context, player *repository.Player, cardId int) (bool, error) { + if player.Game.Status == enums.GameEnd { + return false, errors.New("遊戲已結束") + } + + if player.Status == enums.PlayerStatusDead { + return false, errors.New("你已死亡") + } + + if player.Game.CurrentPlayerId != player.Id { + return false, errors.New("尚未輪到你出牌") + } + + return true, nil +} + +func (p *PlayerService) CheckPlayerCardExist(c context.Context, playerId int, gameId int, cardId int) (bool, error) { + exist, err := p.PlayerCardRepo.ExistPlayerCardByPlayerIdAndCardId(c, playerId, gameId, cardId) + + if err != nil { + return false, err + } + + return exist, nil +} + func (p *PlayerService) CreatePlayer(c context.Context, player *repository.Player) (*repository.Player, error) { player, err := p.PlayerRepo.CreatePlayer(c, player) if err != nil { @@ -78,6 +104,22 @@ func (p *PlayerService) CreatePlayer(c context.Context, player *repository.Playe return player, nil } +func (p *PlayerService) CreatePlayerCard(c context.Context, card *repository.PlayerCard) error { + _, err := p.PlayerCardRepo.CreatePlayerCard(c, card) + if err != nil { + return err + } + return nil +} + +func (p *PlayerService) GetPlayerById(c context.Context, id int) (*repository.Player, error) { + player, err := p.PlayerRepo.GetPlayer(c, id) + if err != nil { + return nil, err + } + return player, nil +} + func (p *PlayerService) GetPlayersByGameId(c context.Context, id int) ([]*repository.Player, error) { players, err := p.PlayerRepo.GetPlayersByGameId(c, id) if err != nil { @@ -86,12 +128,13 @@ func (p *PlayerService) GetPlayersByGameId(c context.Context, id int) ([]*reposi return players, nil } -func (p *PlayerService) CreatePlayerCard(c context.Context, card *repository.PlayerCard) error { - _, err := p.PlayerCardRepo.CreatePlayerCard(c, card) - if err != nil { - return err +func (p *PlayerService) GetHandCardId(player *repository.Player, cardId int) (*repository.PlayerCard, error) { + for _, card := range player.PlayerCards { + if card.CardId == cardId && card.Type == "hand" { + return &card, nil + } } - return nil + return nil, errors.New("找不到手牌") } func (p *PlayerService) PlayCard(c *gin.Context, playerId int, cardId int) (*repository.Game, *repository.Card, error) { @@ -128,27 +171,12 @@ func (p *PlayerService) PlayCard(c *gin.Context, playerId int, cardId int) (*rep return game, &handCard.Card, nil } -func (p *PlayerService) CanPlayCard(c *gin.Context, player *repository.Player, cardId int) (bool, error) { - if player.Game.Status == enums.GameEnd { - return false, errors.New("遊戲已結束") - } - - if player.Status == enums.PlayerStatusDead { - return false, errors.New("你已死亡") - } +func (p *PlayerService) TransmitIntelligenceCard(c *gin.Context, playerId int, gameId int, cardId int) (bool, error) { + ret, err := p.PlayerCardRepo.DeletePlayerCardByPlayerIdAndCardId(c, playerId, gameId, cardId) - if player.Game.CurrentPlayerId != player.Id { - return false, errors.New("尚未輪到你出牌") + if err != nil { + return false, err } - return true, nil -} - -func (p *PlayerService) GetHandCardId(player *repository.Player, cardId int) (*repository.PlayerCard, error) { - for _, card := range player.PlayerCards { - if card.CardId == cardId && card.Type == "hand" { - return &card, nil - } - } - return nil, errors.New("找不到手牌") + return ret, nil } From bd0ecf00def46e9c3f91ec9a3f2523c50f77eea7 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Sat, 20 Jan 2024 01:03:34 +0800 Subject: [PATCH 70/89] Test: Added transmit intelligence card e2e test --- Backend/tests/e2e/player_api_test.go | 166 ++++++++++++++++++++++++++- 1 file changed, 165 insertions(+), 1 deletion(-) diff --git a/Backend/tests/e2e/player_api_test.go b/Backend/tests/e2e/player_api_test.go index 76b752b..98c851d 100644 --- a/Backend/tests/e2e/player_api_test.go +++ b/Backend/tests/e2e/player_api_test.go @@ -3,10 +3,17 @@ package e2e import ( "context" "encoding/json" + "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/request" + "github.com/bxcodec/faker/v3" "github.com/stretchr/testify/assert" + "io" + "math" + "math/rand" "net/http" "strconv" + "strings" + "testing" ) func (suite *IntegrationTestSuite) TestPlayCardE2E() { @@ -42,6 +49,163 @@ func (suite *IntegrationTestSuite) TestPlayCardE2E() { assert.Equal(suite.T(), 2, len(play.PlayerCards)) } +func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { + api := "/api/v1/player/{player_id}/transmit-intelligence" + game, _ := suite.gameServ.InitGame(context.TODO()) + + // Fake player count random 1~3 + playerCount := rand.Intn(3) + 1 + + // Fake players data + var players []request.PlayerInfo + for i := 0; i < playerCount; i++ { + player := request.PlayerInfo{ + ID: faker.UUIDDigit(), + Name: faker.FirstName(), + } + players = append(players, player) + } + + // Fake game data + createGameRequest := request.CreateGameRequest{ + Players: players, + } + + _ = suite.playerServ.InitPlayers(context.TODO(), game, createGameRequest) + _ = suite.gameServ.InitDeck(context.TODO(), game) + _ = suite.gameServ.DrawCardsForAllPlayers(context.TODO(), game) + + suite.T().Run("it can validate intelligence type", func(t *testing.T) { + playerId := rand.Intn(playerCount) + 1 + cardId := rand.Intn(playerCount) + + // Request only card id + url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) + req := PlayCardRequest{CardId: cardId} + reqBody, _ := json.Marshal(req) + + res := suite.requestJson(url, reqBody, http.MethodPost) + + // Convert response body from json to map + resBodyAsByteArray, _ := io.ReadAll(res.Body) + resBody := make(map[string]interface{}) + _ = json.Unmarshal(resBodyAsByteArray, &resBody) + + assert.Equal(t, http.StatusBadRequest, res.StatusCode) + assert.Equal(t, "Invalid intelligence type", resBody["message"]) + }) + + suite.T().Run("it can validate card id", func(t *testing.T) { + playerId := rand.Intn(playerCount) + 1 + intelligenceType := rand.Intn(3) + 1 + + // Request only intelligence type + url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) + req := PlayCardRequest{IntelligenceType: intelligenceType} + reqBody, _ := json.Marshal(req) + + res := suite.requestJson(url, reqBody, http.MethodPost) + + // Convert response body from json to map + resBodyAsByteArray, _ := io.ReadAll(res.Body) + resBody := make(map[string]interface{}) + _ = json.Unmarshal(resBodyAsByteArray, &resBody) + + assert.Equal(t, http.StatusInternalServerError, res.StatusCode) + assert.Equal(t, "Card not found", resBody["message"]) + }) + + suite.T().Run("it can fail when player not found", func(t *testing.T) { + playerId := math.MaxInt32 + cardId := rand.Intn(playerCount) + intelligenceType := rand.Intn(3) + 1 + + url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) + req := PlayCardRequest{CardId: cardId, IntelligenceType: intelligenceType} + reqBody, _ := json.Marshal(req) + + res := suite.requestJson(url, reqBody, http.MethodPost) + + // Convert response body from json to map + resBodyAsByteArray, _ := io.ReadAll(res.Body) + resBody := make(map[string]interface{}) + _ = json.Unmarshal(resBodyAsByteArray, &resBody) + + assert.Equal(t, http.StatusInternalServerError, res.StatusCode) + assert.Equal(t, "Player not found", resBody["message"]) + }) + + suite.T().Run("it can fail when intelligence type is not valid", func(t *testing.T) { + playerId := rand.Intn(playerCount) + 1 + cardId := rand.Intn(playerCount) + intelligenceType := math.MaxInt32 + + url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) + req := PlayCardRequest{CardId: cardId, IntelligenceType: intelligenceType} + reqBody, _ := json.Marshal(req) + + res := suite.requestJson(url, reqBody, http.MethodPost) + + // Convert response body from json to map + resBodyAsByteArray, _ := io.ReadAll(res.Body) + resBody := make(map[string]interface{}) + _ = json.Unmarshal(resBodyAsByteArray, &resBody) + + assert.Equal(t, http.StatusBadRequest, res.StatusCode) + assert.Equal(t, "Invalid intelligence type", resBody["message"]) + + }) + + suite.T().Run("it can fail when player card not found", func(t *testing.T) { + playerId := rand.Intn(playerCount) + 1 + cardId := math.MaxInt32 + intelligenceType := rand.Intn(3) + 1 + + url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) + req := PlayCardRequest{CardId: cardId, IntelligenceType: intelligenceType} + reqBody, _ := json.Marshal(req) + + res := suite.requestJson(url, reqBody, http.MethodPost) + + // Convert response body from json to map + resBodyAsByteArray, _ := io.ReadAll(res.Body) + resBody := make(map[string]interface{}) + _ = json.Unmarshal(resBodyAsByteArray, &resBody) + + assert.Equal(t, http.StatusInternalServerError, res.StatusCode) + assert.Equal(t, "Card not found", resBody["message"]) + }) + + suite.T().Run("it can success when valid card id and intelligence type", func(t *testing.T) { + playerId := rand.Intn(playerCount) + 1 + intelligenceType := rand.Intn(3) + 1 + + // Get player's card + cards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), playerId) + + // Random card id + num := rand.Intn(len(cards.PlayerCards)) + cardId := cards.PlayerCards[num].CardId + + url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) + req := PlayCardRequest{CardId: cardId, IntelligenceType: intelligenceType} + reqBody, _ := json.Marshal(req) + + res := suite.requestJson(url, reqBody, http.MethodPost) + + // Convert response body from json to map + resBodyAsByteArray, _ := io.ReadAll(res.Body) + resBody := make(map[string]interface{}) + _ = json.Unmarshal(resBodyAsByteArray, &resBody) + + msg := enums.ToString(intelligenceType) + " intelligence transmitted" + assert.Equal(t, http.StatusOK, res.StatusCode) + assert.Equal(t, msg, resBody["message"]) + assert.Equal(t, true, resBody["result"]) + }) +} + type PlayCardRequest struct { - CardId int `json:"card_id"` + CardId int `json:"card_id"` + IntelligenceType int `json:"intelligence_type"` } From f4f69acf723530372f0da7d49931cd9bb845d6a3 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Mon, 22 Jan 2024 18:06:27 +0800 Subject: [PATCH 71/89] Feat: Added game status check and update when transmitting intelligence card --- .../delivery/http/v1/player_handler.go | 2 +- Backend/service/service/player_service.go | 24 +++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 45ff881..6d50d02 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -115,7 +115,7 @@ func (p *PlayerHandler) TransmitIntelligence(c *gin.Context) { ret, err := p.playerService.TransmitIntelligenceCard(c, playerId, player.GameId, req.CardID) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to transmit intelligence"}) + c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 6fad64a..2a0d550 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -70,7 +70,7 @@ func (p *PlayerService) ShuffleIdentityCards(cards []string) []string { return shuffledCards } -func (p *PlayerService) CanPlayCard(c *gin.Context, player *repository.Player, cardId int) (bool, error) { +func (p *PlayerService) CanPlayCard(c context.Context, player *repository.Player) (bool, error) { if player.Game.Status == enums.GameEnd { return false, errors.New("遊戲已結束") } @@ -143,7 +143,7 @@ func (p *PlayerService) PlayCard(c *gin.Context, playerId int, cardId int) (*rep return nil, nil, err } - result, err := p.CanPlayCard(c, player, cardId) + result, err := p.CanPlayCard(c, player) if !result || err != nil { return nil, nil, err } @@ -172,11 +172,31 @@ func (p *PlayerService) PlayCard(c *gin.Context, playerId int, cardId int) (*rep } func (p *PlayerService) TransmitIntelligenceCard(c *gin.Context, playerId int, gameId int, cardId int) (bool, error) { + player, err := p.PlayerRepo.GetPlayerWithGamePlayersAndPlayerCardsCard(c, playerId) + if err != nil { + return false, err + } + + result, err := p.CanPlayCard(c, player) + if !result || err != nil { + return false, err + } + + game, err := p.GameServ.NextPlayer(c, player) + if err != nil { + return false, err + } + ret, err := p.PlayerCardRepo.DeletePlayerCardByPlayerIdAndCardId(c, playerId, gameId, cardId) if err != nil { return false, err } + err = p.GameRepo.UpdateGame(c, game) + if err != nil { + return false, err + } + return ret, nil } From 464f7dcb492edb24dff667c1d7489c5f7d75ef8f Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Mon, 22 Jan 2024 18:09:27 +0800 Subject: [PATCH 72/89] Test: Added e2e test for game status check when transmitting the intelligence card --- Backend/service/service/game_service.go | 2 +- Backend/tests/e2e/player_api_test.go | 67 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index f6b4a00..a9f0c79 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -155,7 +155,7 @@ func (g *GameService) NextPlayer(c *gin.Context, player *repository.Player) (*re return player.Game, nil } -func (g *GameService) UpdateStatus(c *gin.Context, game *repository.Game, stage string) { +func (g *GameService) UpdateStatus(c context.Context, game *repository.Game, stage string) { game.Status = stage err := g.GameRepo.UpdateGame(c, game) if err != nil { diff --git a/Backend/tests/e2e/player_api_test.go b/Backend/tests/e2e/player_api_test.go index 98c851d..57119be 100644 --- a/Backend/tests/e2e/player_api_test.go +++ b/Backend/tests/e2e/player_api_test.go @@ -176,6 +176,70 @@ func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { assert.Equal(t, "Card not found", resBody["message"]) }) + suite.T().Run("it can fail when game is end", func(t *testing.T) { + playerId := rand.Intn(playerCount) + 1 + intelligenceType := rand.Intn(3) + 1 + + // Get player's card + cards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), playerId) + + // Random card id + num := rand.Intn(len(cards.PlayerCards)) + cardId := cards.PlayerCards[num].CardId + + // Set player to current player + suite.gameServ.UpdateCurrentPlayer(context.TODO(), game, playerId) + + // Set game status to end + suite.gameServ.UpdateStatus(context.TODO(), game, enums.GameEnd) + + url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) + req := PlayCardRequest{CardId: cardId, IntelligenceType: intelligenceType} + reqBody, _ := json.Marshal(req) + + res := suite.requestJson(url, reqBody, http.MethodPost) + + // Convert response body from json to map + resBodyAsByteArray, _ := io.ReadAll(res.Body) + resBody := make(map[string]interface{}) + _ = json.Unmarshal(resBodyAsByteArray, &resBody) + + assert.Equal(t, http.StatusInternalServerError, res.StatusCode) + assert.Equal(t, "遊戲已結束", resBody["message"]) + + // Recover game status to start + suite.gameServ.UpdateStatus(context.TODO(), game, enums.GameStart) + }) + + suite.T().Run("it can fail when not player's turn", func(t *testing.T) { + playerId := rand.Intn(playerCount) + 1 + intelligenceType := rand.Intn(3) + 1 + + // Get player's card + cards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), playerId) + + // Random card id + num := rand.Intn(len(cards.PlayerCards)) + cardId := cards.PlayerCards[num].CardId + + // Set other player to current player + suite.gameServ.UpdateCurrentPlayer(context.TODO(), game, playerId-1) + + url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) + req := PlayCardRequest{CardId: cardId, IntelligenceType: intelligenceType} + reqBody, _ := json.Marshal(req) + + res := suite.requestJson(url, reqBody, http.MethodPost) + + // Convert response body from json to map + resBodyAsByteArray, _ := io.ReadAll(res.Body) + resBody := make(map[string]interface{}) + _ = json.Unmarshal(resBodyAsByteArray, &resBody) + + assert.Equal(t, http.StatusInternalServerError, res.StatusCode) + assert.Equal(t, "尚未輪到你出牌", resBody["message"]) + }) + suite.T().Run("it can success when valid card id and intelligence type", func(t *testing.T) { playerId := rand.Intn(playerCount) + 1 intelligenceType := rand.Intn(3) + 1 @@ -187,6 +251,9 @@ func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { num := rand.Intn(len(cards.PlayerCards)) cardId := cards.PlayerCards[num].CardId + // Set player to current player + suite.gameServ.UpdateCurrentPlayer(context.TODO(), game, playerId) + url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) req := PlayCardRequest{CardId: cardId, IntelligenceType: intelligenceType} reqBody, _ := json.Marshal(req) From 6416fdb26c5777bedd3e3949ff4c11c82af247e0 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Mon, 22 Jan 2024 18:54:43 +0800 Subject: [PATCH 73/89] Test: Refactor player play card e2e test and add test refresh DB --- Backend/tests/e2e/player_api_test.go | 70 +++++++++++++++++++--------- Backend/tests/e2e/suite_test.go | 5 ++ 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/Backend/tests/e2e/player_api_test.go b/Backend/tests/e2e/player_api_test.go index 57119be..c9e2523 100644 --- a/Backend/tests/e2e/player_api_test.go +++ b/Backend/tests/e2e/player_api_test.go @@ -17,36 +17,60 @@ import ( ) func (suite *IntegrationTestSuite) TestPlayCardE2E() { - // given + // Given + api := "/api/v1/players/{player_id}/player-cards" game, _ := suite.gameServ.InitGame(context.TODO()) + + // Fake player count random 1~3 + playerCount := rand.Intn(3) + 1 + + // Fake players data + var players []request.PlayerInfo + for i := 0; i < playerCount; i++ { + player := request.PlayerInfo{ + ID: faker.UUIDDigit(), + Name: faker.FirstName(), + } + players = append(players, player) + } + + // Fake game data createGameRequest := request.CreateGameRequest{ - Players: []request.PlayerInfo{ - {ID: "6497f6f226b40d440b9a90cc", Name: "A"}, - {ID: "6498112b26b40d440b9a90ce", Name: "B"}, - {ID: "6499df157fed0c21a4fd0425", Name: "C"}, - }, + Players: players, } + _ = suite.playerServ.InitPlayers(context.TODO(), game, createGameRequest) _ = suite.gameServ.InitDeck(context.TODO(), game) _ = suite.gameServ.DrawCardsForAllPlayers(context.TODO(), game) - game, _ = suite.gameServ.GetGameById(context.TODO(), game.Id) - suite.gameServ.UpdateCurrentPlayer(context.TODO(), game, game.Players[0].Id) - cards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), 2) - playCardId := cards.PlayerCards[0].CardId - - // when - api := "/api/v1/players/" + strconv.Itoa(game.Players[0].Id) + "/player-cards" - playCardRequest := PlayCardRequest{CardId: playCardId} - jsonBody, err := json.Marshal(playCardRequest) - if err != nil { - suite.T().Fatalf("Failed to marshal JSON: %v", err) - } - resp := suite.requestJson(api, jsonBody, http.MethodPost) - // then - assert.Equal(suite.T(), 200, resp.StatusCode) - play, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), 2) - assert.Equal(suite.T(), 2, len(play.PlayerCards)) + playerId := rand.Intn(playerCount) + 1 + + // Get player's card + cards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), playerId) + + // Random card id + num := rand.Intn(len(cards.PlayerCards)) + cardId := cards.PlayerCards[num].CardId + + // Set player to current player + suite.gameServ.UpdateCurrentPlayer(context.TODO(), game, playerId) + + url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) + req := PlayCardRequest{CardId: cardId} + reqBody, _ := json.Marshal(req) + + res := suite.requestJson(url, reqBody, http.MethodPost) + + // Convert response body from json to map + resBodyAsByteArray, _ := io.ReadAll(res.Body) + resBody := make(map[string]interface{}) + _ = json.Unmarshal(resBodyAsByteArray, &resBody) + + // Then + assert.Equal(suite.T(), 200, res.StatusCode) + + player, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), playerId) + assert.Equal(suite.T(), len(cards.PlayerCards)-1, len(player.PlayerCards)) } func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { diff --git a/Backend/tests/e2e/suite_test.go b/Backend/tests/e2e/suite_test.go index b50fa0a..9863498 100644 --- a/Backend/tests/e2e/suite_test.go +++ b/Backend/tests/e2e/suite_test.go @@ -160,6 +160,11 @@ func (suite *IntegrationTestSuite) TearDownSuite() { func (suite *IntegrationTestSuite) SetupTest() { suite.tx = suite.db.Begin() + + //Fixme Run db refresh and seeders + config.RunRefresh() + db := config.NewDatabase() + seeders.Run(db) } func (suite *IntegrationTestSuite) TearDownTest() { From 562b78bed67ccfa7a4dfb4dccf5319935410d856 Mon Sep 17 00:00:00 2001 From: KenLin Date: Mon, 22 Jan 2024 23:35:29 +0800 Subject: [PATCH 74/89] feat: After transmit intelligence, write the content into the game progress table --- Backend/cmd/app/main.go | 8 +++-- Backend/enums/game_action.go | 5 +++ .../repository/game_progresses_repository.go | 23 ++++++++++++ .../mysql/game_process_repository.go | 27 ++++++++++++++ Backend/service/service/player_service.go | 36 ++++++++++++------- Backend/tests/e2e/suite_test.go | 8 +++-- 6 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 Backend/enums/game_action.go create mode 100644 Backend/service/repository/game_progresses_repository.go create mode 100644 Backend/service/repository/mysql/game_process_repository.go diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index e79f8c3..9da22ca 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -26,6 +26,7 @@ func main() { cardRepo := mysqlRepo.NewCardRepository(db) deckRepo := mysqlRepo.NewDeckRepository(db) playerCardRepo := mysqlRepo.NewPlayerCardRepository(db) + gameProgressRepo := mysqlRepo.NewGameProgressRepository(db) cardService := service.NewCardService(&service.CardServiceOptions{ CardRepo: cardRepo, @@ -40,9 +41,10 @@ func main() { }) playerService := service.NewPlayerService(&service.PlayerServiceOptions{ - PlayerRepo: playerRepo, - PlayerCardRepo: playerCardRepo, - GameRepo: gameRepo, + PlayerRepo: playerRepo, + PlayerCardRepo: playerCardRepo, + GameRepo: gameRepo, + GameProgressRepo: gameProgressRepo, }) gameService := service.NewGameService( diff --git a/Backend/enums/game_action.go b/Backend/enums/game_action.go new file mode 100644 index 0000000..9156e71 --- /dev/null +++ b/Backend/enums/game_action.go @@ -0,0 +1,5 @@ +package enums + +const ( + TransmitIntelligence = "傳情報" +) diff --git a/Backend/service/repository/game_progresses_repository.go b/Backend/service/repository/game_progresses_repository.go new file mode 100644 index 0000000..fd30996 --- /dev/null +++ b/Backend/service/repository/game_progresses_repository.go @@ -0,0 +1,23 @@ +package repository + +import ( + "context" + "gorm.io/gorm" + "time" +) + +type GameProgresses struct { + Id int `gorm:"primaryKey;auto_increment"` + PlayerId int + GameId int + CardId int + Action string + TargetPlayerId int + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoCreateTime"` + DeletedAt gorm.DeletedAt +} + +type GameProgressesRepository interface { + CreateGameProgress(c context.Context, gameProgress *GameProgresses) (*GameProgresses, error) +} diff --git a/Backend/service/repository/mysql/game_process_repository.go b/Backend/service/repository/mysql/game_process_repository.go new file mode 100644 index 0000000..012b266 --- /dev/null +++ b/Backend/service/repository/mysql/game_process_repository.go @@ -0,0 +1,27 @@ +package mysql + +import ( + "context" + "github.com/Game-as-a-Service/The-Message/service/repository" + "gorm.io/gorm" +) + +type GameProgressRepository struct { + db *gorm.DB +} + +func NewGameProgressRepository(db *gorm.DB) *GameProgressRepository { + return &GameProgressRepository{ + db: db, + } +} + +func (g *GameProgressRepository) CreateGameProgress(c context.Context, gameProgress *repository.GameProgresses) (*repository.GameProgresses, error) { + result := g.db.Create(&gameProgress) + + if result.Error != nil { + return nil, result.Error + } + + return gameProgress, nil +} diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 2a0d550..7f523d8 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -11,25 +11,28 @@ import ( ) type PlayerService struct { - PlayerRepo repository.PlayerRepository - PlayerCardRepo repository.PlayerCardRepository - GameRepo repository.GameRepository - GameServ *GameService + PlayerRepo repository.PlayerRepository + PlayerCardRepo repository.PlayerCardRepository + GameRepo repository.GameRepository + GameServ *GameService + GameProgressRepo repository.GameProgressesRepository } type PlayerServiceOptions struct { - PlayerRepo repository.PlayerRepository - PlayerCardRepo repository.PlayerCardRepository - GameRepo repository.GameRepository - GameServ *GameService + PlayerRepo repository.PlayerRepository + PlayerCardRepo repository.PlayerCardRepository + GameRepo repository.GameRepository + GameServ *GameService + GameProgressRepo repository.GameProgressesRepository } func NewPlayerService(opts *PlayerServiceOptions) PlayerService { return PlayerService{ - PlayerRepo: opts.PlayerRepo, - PlayerCardRepo: opts.PlayerCardRepo, - GameRepo: opts.GameRepo, - GameServ: opts.GameServ, + PlayerRepo: opts.PlayerRepo, + PlayerCardRepo: opts.PlayerCardRepo, + GameRepo: opts.GameRepo, + GameServ: opts.GameServ, + GameProgressRepo: opts.GameProgressRepo, } } @@ -188,7 +191,6 @@ func (p *PlayerService) TransmitIntelligenceCard(c *gin.Context, playerId int, g } ret, err := p.PlayerCardRepo.DeletePlayerCardByPlayerIdAndCardId(c, playerId, gameId, cardId) - if err != nil { return false, err } @@ -198,5 +200,13 @@ func (p *PlayerService) TransmitIntelligenceCard(c *gin.Context, playerId int, g return false, err } + _, err = p.GameProgressRepo.CreateGameProgress(c, &repository.GameProgresses{ + GameId: game.Id, + PlayerId: playerId, + CardId: cardId, + Action: enums.TransmitIntelligence, + TargetPlayerId: game.CurrentPlayerId, + }) + return ret, nil } diff --git a/Backend/tests/e2e/suite_test.go b/Backend/tests/e2e/suite_test.go index 9863498..26a4d49 100644 --- a/Backend/tests/e2e/suite_test.go +++ b/Backend/tests/e2e/suite_test.go @@ -84,6 +84,7 @@ func (suite *IntegrationTestSuite) SetupSuite() { cardRepo := mysqlRepo.NewCardRepository(db) deckRepo := mysqlRepo.NewDeckRepository(db) playerCardRepo := mysqlRepo.NewPlayerCardRepository(db) + gameProgressRepo := mysqlRepo.NewGameProgressRepository(db) cardService := service.NewCardService(&service.CardServiceOptions{ CardRepo: cardRepo, @@ -98,9 +99,10 @@ func (suite *IntegrationTestSuite) SetupSuite() { }) playerService := service.NewPlayerService(&service.PlayerServiceOptions{ - PlayerRepo: playerRepo, - PlayerCardRepo: playerCardRepo, - GameRepo: gameRepo, + PlayerRepo: playerRepo, + PlayerCardRepo: playerCardRepo, + GameRepo: gameRepo, + GameProgressRepo: gameProgressRepo, }) gameService := service.NewGameService( From 6ff71300b4495cae01bfdce824ffd426114d7dc8 Mon Sep 17 00:00:00 2001 From: Nicole Date: Tue, 23 Jan 2024 20:24:39 +0800 Subject: [PATCH 75/89] resolve conflict --- Backend/cmd/app/main.go | 8 +- Backend/config/migration.go | 1 + .../000001_create_games_table.up.sql | 14 +-- .../000002_create_players_table.up.sql | 20 +++-- .../000003_create_cards_table.up.sql | 14 +-- .../000004_create_decks_table.up.sql | 14 +-- .../000005_create_player_cards_table.up.sql | 24 ++--- ...0006_create_game_progresses_table.down.sql | 5 ++ ...000006_create_game_progresses_table.up.sql | 19 ++++ Backend/enums/game_status.go | 8 ++ Backend/enums/player_status.go | 6 ++ .../service/delivery/http/v1/card_handler.go | 8 -- .../service/delivery/http/v1/game_handler.go | 34 +++++-- .../delivery/http/v1/player_handler.go | 51 +++++++---- .../service/delivery/http/v1/sse_handler.go | 1 - Backend/service/repository/deck_repository.go | 3 +- Backend/service/repository/game_repository.go | 15 ++-- .../repository/mysql/game_repository.go | 11 ++- .../repository/mysql/player_repository.go | 19 +++- .../service/repository/player_repository.go | 5 ++ Backend/service/service/game_service.go | 46 +++++++++- Backend/service/service/player_service.go | 89 +++++++++++++------ Backend/tests/e2e/player_api_test.go | 5 +- Backend/tests/e2e/player_card_api_test.go | 12 --- Backend/tests/e2e/suite_test.go | 13 ++- 25 files changed, 315 insertions(+), 130 deletions(-) create mode 100644 Backend/database/migrations/000006_create_game_progresses_table.down.sql create mode 100644 Backend/database/migrations/000006_create_game_progresses_table.up.sql create mode 100644 Backend/enums/game_status.go create mode 100644 Backend/enums/player_status.go diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index 07dbd23..e79f8c3 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -42,6 +42,7 @@ func main() { playerService := service.NewPlayerService(&service.PlayerServiceOptions{ PlayerRepo: playerRepo, PlayerCardRepo: playerCardRepo, + GameRepo: gameRepo, }) gameService := service.NewGameService( @@ -52,6 +53,7 @@ func main() { DeckService: deckService, }, ) + playerService.GameServ = &gameService http.RegisterGameHandler( &http.GameHandlerOptions{ @@ -76,8 +78,10 @@ func main() { http.RegisterPlayerHandler( &http.PlayerHandlerOptions{ - Engine: engine, - Service: playerService, + Engine: engine, + Service: playerService, + GameService: gameService, + SSE: sse, }, ) engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) diff --git a/Backend/config/migration.go b/Backend/config/migration.go index bfef445..5914a91 100644 --- a/Backend/config/migration.go +++ b/Backend/config/migration.go @@ -70,6 +70,7 @@ func RunRefresh() { func GetSourceURL() string { dir, _ := os.Getwd() + dir = strings.SplitAfter(dir, "Backend")[0] dir = strings.ReplaceAll(dir, "\\", "/") sourceURL := "file://" + dir + "/database/migrations" diff --git a/Backend/database/migrations/000001_create_games_table.up.sql b/Backend/database/migrations/000001_create_games_table.up.sql index e3b9a27..7d8ab74 100644 --- a/Backend/database/migrations/000001_create_games_table.up.sql +++ b/Backend/database/migrations/000001_create_games_table.up.sql @@ -2,11 +2,13 @@ BEGIN; CREATE TABLE games ( - id INT AUTO_INCREMENT PRIMARY KEY, - token LONGTEXT NOT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - deleted_at DATETIME -) ENGINE=InnoDB; + id INT AUTO_INCREMENT PRIMARY KEY, + token LONGTEXT NOT NULL, + status VARCHAR(10) NOT NULL, + current_player_id INT, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/database/migrations/000002_create_players_table.up.sql b/Backend/database/migrations/000002_create_players_table.up.sql index bbbf9db..3566df0 100644 --- a/Backend/database/migrations/000002_create_players_table.up.sql +++ b/Backend/database/migrations/000002_create_players_table.up.sql @@ -2,14 +2,16 @@ BEGIN; CREATE TABLE players ( - id INT AUTO_INCREMENT PRIMARY KEY, - game_id INT NOT NULL, - name VARCHAR(255) NOT NULL, - identity_card VARCHAR(255) NOT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - deleted_at DATETIME, - FOREIGN KEY (game_id) REFERENCES games (id) -) ENGINE=InnoDB; + id INT AUTO_INCREMENT PRIMARY KEY, + game_id INT NOT NULL, + name VARCHAR(255) NOT NULL, + order_number INT NOT NULL, + identity_card VARCHAR(255) NOT NULL, + status VARCHAR(10) NOT NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME, + FOREIGN KEY (game_id) REFERENCES games (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/database/migrations/000003_create_cards_table.up.sql b/Backend/database/migrations/000003_create_cards_table.up.sql index 4f1411b..9ce71a1 100644 --- a/Backend/database/migrations/000003_create_cards_table.up.sql +++ b/Backend/database/migrations/000003_create_cards_table.up.sql @@ -2,12 +2,12 @@ BEGIN; CREATE TABLE cards ( - id INT AUTO_INCREMENT PRIMARY KEY, - name VARCHAR(255) NOT NULL, - color VARCHAR(255) NOT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - deleted_at DATETIME -) ENGINE=InnoDB; + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + color VARCHAR(255) NOT NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/database/migrations/000004_create_decks_table.up.sql b/Backend/database/migrations/000004_create_decks_table.up.sql index 9af2164..3179477 100644 --- a/Backend/database/migrations/000004_create_decks_table.up.sql +++ b/Backend/database/migrations/000004_create_decks_table.up.sql @@ -2,12 +2,12 @@ BEGIN; CREATE TABLE decks ( - id INT AUTO_INCREMENT PRIMARY KEY, - game_id INT NOT NULL, - card_id INT NOT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - deleted_at DATETIME -) ENGINE=InnoDB; + id INT AUTO_INCREMENT PRIMARY KEY, + game_id INT NOT NULL, + card_id INT NOT NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/database/migrations/000005_create_player_cards_table.up.sql b/Backend/database/migrations/000005_create_player_cards_table.up.sql index 54fd5f8..f5f6ef1 100644 --- a/Backend/database/migrations/000005_create_player_cards_table.up.sql +++ b/Backend/database/migrations/000005_create_player_cards_table.up.sql @@ -2,17 +2,17 @@ BEGIN; CREATE TABLE player_cards ( - id INT AUTO_INCREMENT PRIMARY KEY, - player_id INT NOT NULL, - game_id INT NOT NULL, - card_id INT NOT NULL, - type VARCHAR(255) NOT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - deleted_at DATETIME, - FOREIGN KEY (player_id) REFERENCES players (id), - FOREIGN KEY (game_id) REFERENCES games (id), - FOREIGN KEY (card_id) REFERENCES cards (id) -) ENGINE=InnoDB; + id INT AUTO_INCREMENT PRIMARY KEY, + player_id INT NOT NULL, + game_id INT NOT NULL, + card_id INT NOT NULL, + type VARCHAR(255) NOT NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME, + FOREIGN KEY (player_id) REFERENCES players (id), + FOREIGN KEY (game_id) REFERENCES games (id), + FOREIGN KEY (card_id) REFERENCES cards (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; COMMIT; diff --git a/Backend/database/migrations/000006_create_game_progresses_table.down.sql b/Backend/database/migrations/000006_create_game_progresses_table.down.sql new file mode 100644 index 0000000..9b0ebbb --- /dev/null +++ b/Backend/database/migrations/000006_create_game_progresses_table.down.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP TABLE IF EXISTS game_progresses; + +COMMIT; diff --git a/Backend/database/migrations/000006_create_game_progresses_table.up.sql b/Backend/database/migrations/000006_create_game_progresses_table.up.sql new file mode 100644 index 0000000..5551949 --- /dev/null +++ b/Backend/database/migrations/000006_create_game_progresses_table.up.sql @@ -0,0 +1,19 @@ +BEGIN; + +CREATE TABLE game_progresses +( + id INT AUTO_INCREMENT PRIMARY KEY, + player_id INT NOT NULL, + game_id INT NOT NULL, + card_id INT NOT NULL, + action VARCHAR(255) NOT NULL, + target_player_id INT, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME, + FOREIGN KEY (player_id) REFERENCES players (id), + FOREIGN KEY (game_id) REFERENCES games (id), + FOREIGN KEY (card_id) REFERENCES cards (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +COMMIT; diff --git a/Backend/enums/game_status.go b/Backend/enums/game_status.go new file mode 100644 index 0000000..c8fa67f --- /dev/null +++ b/Backend/enums/game_status.go @@ -0,0 +1,8 @@ +package enums + +const ( + GameStart = "開始遊戲" + ActionCardStage = "功能牌階段" + TransmitIntelligenceStage = "情報牌階段" + GameEnd = "結束遊戲" +) diff --git a/Backend/enums/player_status.go b/Backend/enums/player_status.go new file mode 100644 index 0000000..ae650a9 --- /dev/null +++ b/Backend/enums/player_status.go @@ -0,0 +1,6 @@ +package enums + +const ( + PlayerStatusAlive = "生存" + PlayerStatusDead = "死亡" +) diff --git a/Backend/service/delivery/http/v1/card_handler.go b/Backend/service/delivery/http/v1/card_handler.go index 5f335d8..bb34d69 100644 --- a/Backend/service/delivery/http/v1/card_handler.go +++ b/Backend/service/delivery/http/v1/card_handler.go @@ -1,8 +1,6 @@ package http import ( - "encoding/json" - "fmt" "net/http" "strconv" @@ -53,12 +51,6 @@ func (p *CardHandler) GetPlayerCards(c *gin.Context) { } playerCardsInfo = append(playerCardsInfo, dict) } - jsonData, err := json.Marshal(playerCardsInfo) - fmt.Println(jsonData) - if err != nil { - fmt.Println("Error encoding JSON:", err) - return - } c.JSON(http.StatusOK, gin.H{"player_cards": playerCardsInfo}) } diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index 798ed8b..b599900 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -2,6 +2,7 @@ package http import ( "encoding/json" + "github.com/Game-as-a-Service/The-Message/enums" "io" "log" "net/http" @@ -56,11 +57,16 @@ func (g *GameHandler) StartGame(c *gin.Context) { return } + // TODO 這邊可以優化 https://gorm.io/zh_CN/docs/associations.html if err := g.gameService.PlayerService.InitPlayers(c, game, req); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } + game, _ = g.gameService.GetGameById(c, game.Id) + g.gameService.UpdateCurrentPlayer(c, game, game.Players[0].Id) + g.gameService.UpdateStatus(c, game, enums.ActionCardStage) + if err := g.gameService.InitDeck(c, game); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return @@ -71,11 +77,16 @@ func (g *GameHandler) StartGame(c *gin.Context) { return } + game, err = g.gameService.GetGameById(c, game.Id) + if err != nil { + return + } + g.SSE.Message <- gin.H{ - "message": "Game started", - "status": "started", - "gameId": strconv.Itoa(game.Id), - //"game": game, + "message": "Game started", + "status": "started", + "game_id": game.Id, + "next_player": game.Players[0].Id, } c.JSON(http.StatusOK, gin.H{ @@ -138,8 +149,21 @@ func (g *GameHandler) GameEvent(c *gin.Context) { return } + game, err := g.gameService.GetGameById(c, gameId) + if err != nil { + return + } + + g.SSE.Message <- gin.H{ + "message": game.Status, + "status": game.Status, + "game_id": gameId, + "current_player": game.CurrentPlayerId, + } + c.Stream(func(w io.Writer) bool { if msg, ok := <-clientChan; ok { + log.Printf("msg: %+v", msg) data := GameSSERequest{} err := json.Unmarshal([]byte(msg), &data) if err != nil { @@ -156,7 +180,7 @@ func (g *GameHandler) GameEvent(c *gin.Context) { } type GameSSERequest struct { - GameId int `json:"gameId,string"` + GameId int `json:"game_id,int"` Message string `json:"message"` Status string `json:"status"` } diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 80f9bda..f89258c 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -1,6 +1,7 @@ package http import ( + "fmt" "net/http" "strconv" @@ -12,20 +13,26 @@ import ( type PlayerHandler struct { playerService service.PlayerService + gameService service.GameService + SSE *Event } type PlayerHandlerOptions struct { - Engine *gin.Engine - Service service.PlayerService + Engine *gin.Engine + Service service.PlayerService + GameService service.GameService + SSE *Event } func RegisterPlayerHandler(opts *PlayerHandlerOptions) { handler := &PlayerHandler{ playerService: opts.Service, + gameService: opts.GameService, + SSE: opts.SSE, } opts.Engine.POST("/api/v1/players/:playerId/player-cards", handler.PlayCard) - opts.Engine.POST("/api/v1/players/:playerId/accept", handler.AcceptCard) + // opts.Engine.POST("/api/v1/players/:playerId/accept", handler.AcceptCard) } // PlayCard godoc @@ -41,33 +48,41 @@ func RegisterPlayerHandler(opts *PlayerHandlerOptions) { func (p *PlayerHandler) PlayCard(c *gin.Context) { playerId, _ := strconv.Atoi(c.Param("playerId")) var req request.PlayCardRequest - if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - result, err := p.playerService.PlayCard(c, playerId, req.CardID) + game, card, err := p.playerService.PlayCard(c, playerId, req.CardID) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()}) return } + // TODO to Service + p.SSE.Message <- gin.H{ + "game_id": game.Id, + "status": game.Status, + "message": fmt.Sprintf("玩家: %d 已出牌", playerId), + "card": card.Name, + "next_player": game.CurrentPlayerId, + } + c.JSON(http.StatusOK, gin.H{ - "result": result, + "result": true, }) } -func (p *PlayerHandler) AcceptCard(c *gin.Context) { - playerId, _ := strconv.Atoi(c.Param("playerId")) +// func (p *PlayerHandler) AcceptCard(c *gin.Context) { +// playerId, _ := strconv.Atoi(c.Param("playerId")) - result, err := p.playerService.AcceptCard(c, playerId) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) - return - } +// result, err := p.playerService.AcceptCard(c, playerId) +// if err != nil { +// c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) +// return +// } - c.JSON(http.StatusOK, gin.H{ - "message": "success", - }) -} +// c.JSON(http.StatusOK, gin.H{ +// "message": "success", +// }) +// } diff --git a/Backend/service/delivery/http/v1/sse_handler.go b/Backend/service/delivery/http/v1/sse_handler.go index 67b4fd0..1c74a6d 100644 --- a/Backend/service/delivery/http/v1/sse_handler.go +++ b/Backend/service/delivery/http/v1/sse_handler.go @@ -43,7 +43,6 @@ func (stream *Event) listen() { case eventMsg := <-stream.Message: jsonMsg, err := json.Marshal(eventMsg) if err != nil { - // 处理错误 continue } diff --git a/Backend/service/repository/deck_repository.go b/Backend/service/repository/deck_repository.go index b1a67f0..580c249 100644 --- a/Backend/service/repository/deck_repository.go +++ b/Backend/service/repository/deck_repository.go @@ -2,8 +2,9 @@ package repository import ( "context" - "gorm.io/gorm" "time" + + "gorm.io/gorm" ) type Deck struct { diff --git a/Backend/service/repository/game_repository.go b/Backend/service/repository/game_repository.go index 924e9f0..9e1c506 100644 --- a/Backend/service/repository/game_repository.go +++ b/Backend/service/repository/game_repository.go @@ -9,12 +9,14 @@ import ( type Game struct { gorm.Model - Id int `gorm:"primaryKey;auto_increment"` - Token string - Players []Player - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoCreateTime"` - DeletedAt gorm.DeletedAt + Id int `gorm:"primaryKey;auto_increment"` + Token string + Status string + CurrentPlayerId int + Players []Player + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoCreateTime"` + DeletedAt gorm.DeletedAt } type GameRepository interface { @@ -22,4 +24,5 @@ type GameRepository interface { CreateGame(ctx context.Context, game *Game) (*Game, error) DeleteGame(ctx context.Context, id int) error GetGameWithPlayers(ctx context.Context, id int) (*Game, error) + UpdateGame(ctx context.Context, game *Game) error } diff --git a/Backend/service/repository/mysql/game_repository.go b/Backend/service/repository/mysql/game_repository.go index 8c0e143..a8b8fbc 100644 --- a/Backend/service/repository/mysql/game_repository.go +++ b/Backend/service/repository/mysql/game_repository.go @@ -2,7 +2,6 @@ package mysql import ( "context" - "github.com/Game-as-a-Service/The-Message/service/repository" "gorm.io/gorm" ) @@ -59,3 +58,13 @@ func (g *GameRepository) GetGameWithPlayers(ctx context.Context, id int) (*repos } return &game, nil } + +func (g *GameRepository) UpdateGame(ctx context.Context, game *repository.Game) error { + result := g.db.Save(&game) + + if result.Error != nil { + return result.Error + } + + return nil +} diff --git a/Backend/service/repository/mysql/player_repository.go b/Backend/service/repository/mysql/player_repository.go index b7ac24e..56fec6c 100644 --- a/Backend/service/repository/mysql/player_repository.go +++ b/Backend/service/repository/mysql/player_repository.go @@ -48,8 +48,25 @@ func (p *PlayerRepository) GetPlayersByGameId(ctx context.Context, id int) ([]*r func (p *PlayerRepository) GetPlayerWithPlayerCards(ctx context.Context, playerId int) (*repository.Player, error) { var player repository.Player - if err := p.db.Debug().Preload("PlayerCards").Preload("PlayerCards.Card").First(&player, playerId).Error; err != nil { + if err := p.db.Preload("PlayerCards").Preload("PlayerCards.Card").First(&player, playerId).Error; err != nil { return nil, err } return &player, nil } + +func (p *PlayerRepository) GetPlayerWithGame(ctx context.Context, playerId int) (*repository.Player, error) { + var player repository.Player + if err := p.db.Preload("Game").First(&player, playerId).Error; err != nil { + return nil, err + } + return &player, nil +} + +func (p *PlayerRepository) GetPlayerWithGamePlayersAndPlayerCardsCard(ctx context.Context, playerId int) (*repository.Player, error) { + var player repository.Player + if err := p.db.Preload("Game.Players").Preload("PlayerCards.Card").First(&player, playerId).Error; err != nil { + return nil, err + } + + return &player, nil +} diff --git a/Backend/service/repository/player_repository.go b/Backend/service/repository/player_repository.go index d5f963d..76751c3 100644 --- a/Backend/service/repository/player_repository.go +++ b/Backend/service/repository/player_repository.go @@ -13,7 +13,10 @@ type Player struct { Name string GameId int `gorm:"foreignKey:GameId;references:Id"` IdentityCard string + Status string + OrderNumber int PlayerCards []PlayerCard + Game *Game `gorm:"foreignKey:GameId;references:Id"` CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoCreateTime"` DeletedAt gorm.DeletedAt @@ -24,4 +27,6 @@ type PlayerRepository interface { GetPlayer(ctx context.Context, playerId int) (*Player, error) GetPlayersByGameId(ctx context.Context, id int) ([]*Player, error) GetPlayerWithPlayerCards(ctx context.Context, playerId int) (*Player, error) + GetPlayerWithGame(ctx context.Context, playerId int) (*Player, error) + GetPlayerWithGamePlayersAndPlayerCardsCard(ctx context.Context, playerId int) (*Player, error) } diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index c1f3ac5..f6b4a00 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -4,7 +4,9 @@ import ( "context" "crypto/rand" "encoding/hex" + "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" + "github.com/gin-gonic/gin" ) type GameService struct { @@ -37,14 +39,13 @@ func (g *GameService) InitGame(c context.Context) (*repository.Game, error) { } game, err := g.CreateGame(c, &repository.Game{ - Token: token, + Token: token, + Status: enums.GameStart, }) if err != nil { return nil, err } - // sent sse - return game, nil } @@ -109,7 +110,7 @@ func (g *GameService) CreateGame(c context.Context, game *repository.Game) (*rep } func (g *GameService) GetGameById(c context.Context, id int) (*repository.Game, error) { - game, err := g.GameRepo.GetGameById(c, id) + game, err := g.GameRepo.GetGameWithPlayers(c, id) if err != nil { return nil, err } @@ -122,5 +123,42 @@ func (g *GameService) DeleteGame(c context.Context, id int) error { return err } return nil +} + +func (g *GameService) UpdateCurrentPlayer(c context.Context, game *repository.Game, playerId int) { + game.CurrentPlayerId = playerId + err := g.GameRepo.UpdateGame(c, game) + if err != nil { + panic(err) + } +} + +func (g *GameService) NextPlayer(c *gin.Context, player *repository.Player) (*repository.Game, error) { + players := player.Game.Players + + currentPlayerId := player.Id + var currentPlayerIndex int + for index, gPlayer := range players { + if gPlayer.Id == currentPlayerId { + currentPlayerIndex = index + break + } + } + + if currentPlayerIndex+1 >= len(players) { + player.Game.CurrentPlayerId = players[0].Id + player.Game.Status = enums.TransmitIntelligenceStage + } else { + player.Game.CurrentPlayerId = players[currentPlayerIndex+1].Id + } + return player.Game, nil +} + +func (g *GameService) UpdateStatus(c *gin.Context, game *repository.Game, stage string) { + game.Status = stage + err := g.GameRepo.UpdateGame(c, game) + if err != nil { + panic(err) + } } diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index f2f3cb3..9656140 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -4,6 +4,8 @@ import ( "context" "math/rand" + "errors" + "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" "github.com/Game-as-a-Service/The-Message/service/request" @@ -14,12 +16,14 @@ type PlayerService struct { PlayerRepo repository.PlayerRepository PlayerCardRepo repository.PlayerCardRepository GameRepo repository.GameRepository + GameServ *GameService } type PlayerServiceOptions struct { PlayerRepo repository.PlayerRepository PlayerCardRepo repository.PlayerCardRepository GameRepo repository.GameRepository + GameServ *GameService } func NewPlayerService(opts *PlayerServiceOptions) PlayerService { @@ -27,6 +31,7 @@ func NewPlayerService(opts *PlayerServiceOptions) PlayerService { PlayerRepo: opts.PlayerRepo, PlayerCardRepo: opts.PlayerCardRepo, GameRepo: opts.GameRepo, + GameServ: opts.GameServ, } } @@ -37,6 +42,8 @@ func (p *PlayerService) InitPlayers(c context.Context, game *repository.Game, re Name: reqPlayer.Name, GameId: game.Id, IdentityCard: identityCards[i], + OrderNumber: i + 1, + Status: enums.PlayerStatusAlive, }) if err != nil { return err @@ -89,48 +96,78 @@ func (p *PlayerService) CreatePlayerCard(c context.Context, card *repository.Pla return nil } -func (p *PlayerService) PlayCard(c *gin.Context, playerId int, cardId int) (bool, error) { - player, err := p.PlayerRepo.GetPlayer(c, playerId) +func (p *PlayerService) PlayCard(c *gin.Context, playerId int, cardId int) (*repository.Game, *repository.Card, error) { + player, err := p.PlayerRepo.GetPlayerWithGamePlayersAndPlayerCardsCard(c, playerId) + if err != nil { + return nil, nil, err + } + + result, err := p.CanPlayCard(c, player, cardId) + if !result || err != nil { + return nil, nil, err + } + + handCard, err := p.GetHandCardId(player, cardId) if err != nil { - return false, err + return nil, nil, err } - cards, err := p.PlayerCardRepo.GetPlayerCards(c, &repository.PlayerCard{ - PlayerId: player.Id, - GameId: player.GameId, - Type: "hand", - CardId: cardId, - }) + game, err := p.GameServ.NextPlayer(c, player) if err != nil { - return false, err + return nil, nil, err } - if len(*cards) == 0 { - return false, nil + err = p.PlayerCardRepo.DeletePlayerCard(c, handCard.Id) + if err != nil { + return nil, nil, err } - err = p.PlayerCardRepo.DeletePlayerCard(c, (*cards)[0].Id) + err = p.GameRepo.UpdateGame(c, game) if err != nil { - return false, err + return nil, nil, err } - return true, nil + return game, &handCard.Card, nil } -func (p *PlayerService) AcceptCard(c *gin.Context, playerId int) (bool, error) { - player, err := p.PlayerRepo.GetPlayer(c, playerId) - if err != nil { - return false, err +func (p *PlayerService) CanPlayCard(c *gin.Context, player *repository.Player, cardId int) (bool, error) { + if player.Game.Status == enums.GameEnd { + return false, errors.New("遊戲已結束") } - cards, err := p.PlayerCardRepo.GetPlayerCards(c, &repository.PlayerCard{ - PlayerId: player.Id, - GameId: player.GameId, - Type: "hand", - }) - if err != nil { - return false, err + if player.Status == enums.PlayerStatusDead { + return false, errors.New("你已死亡") + } + + if player.Game.CurrentPlayerId != player.Id { + return false, errors.New("尚未輪到你出牌") } return true, nil } + +// func (p *PlayerService) AcceptCard(c *gin.Context, playerId int) (bool, error) { +// player, err := p.PlayerRepo.GetPlayer(c, playerId) +// if err != nil { +// return false, err +// } + +// cards, err := p.PlayerCardRepo.GetPlayerCards(c, &repository.PlayerCard{ +// PlayerId: player.Id, +// GameId: player.GameId, +// Type: "hand", +// }) +// if err != nil { +// return false, err +// } +// return true, nil +// } + +func (p *PlayerService) GetHandCardId(player *repository.Player, cardId int) (*repository.PlayerCard, error) { + for _, card := range player.PlayerCards { + if card.CardId == cardId && card.Type == "hand" { + return &card, nil + } + } + return nil, errors.New("找不到手牌") +} diff --git a/Backend/tests/e2e/player_api_test.go b/Backend/tests/e2e/player_api_test.go index e5c0858..76b752b 100644 --- a/Backend/tests/e2e/player_api_test.go +++ b/Backend/tests/e2e/player_api_test.go @@ -6,6 +6,7 @@ import ( "github.com/Game-as-a-Service/The-Message/service/request" "github.com/stretchr/testify/assert" "net/http" + "strconv" ) func (suite *IntegrationTestSuite) TestPlayCardE2E() { @@ -21,11 +22,13 @@ func (suite *IntegrationTestSuite) TestPlayCardE2E() { _ = suite.playerServ.InitPlayers(context.TODO(), game, createGameRequest) _ = suite.gameServ.InitDeck(context.TODO(), game) _ = suite.gameServ.DrawCardsForAllPlayers(context.TODO(), game) + game, _ = suite.gameServ.GetGameById(context.TODO(), game.Id) + suite.gameServ.UpdateCurrentPlayer(context.TODO(), game, game.Players[0].Id) cards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), 2) playCardId := cards.PlayerCards[0].CardId // when - api := "/api/v1/players/2/player-cards" + api := "/api/v1/players/" + strconv.Itoa(game.Players[0].Id) + "/player-cards" playCardRequest := PlayCardRequest{CardId: playCardId} jsonBody, err := json.Marshal(playCardRequest) if err != nil { diff --git a/Backend/tests/e2e/player_card_api_test.go b/Backend/tests/e2e/player_card_api_test.go index 30d3143..a9af03f 100644 --- a/Backend/tests/e2e/player_card_api_test.go +++ b/Backend/tests/e2e/player_card_api_test.go @@ -67,15 +67,3 @@ func (suite *IntegrationTestSuite) TestGetPlayerCards() { } } } - -type PlayerCard struct { - ID string `json:"id"` - PlayerID string `json:"player_id"` - GameID string `json:"game_id"` - CardID string `json:"card_id"` - Type string `json:"type"` -} - -type Request_p struct { - Player Player `json:"player"` -} diff --git a/Backend/tests/e2e/suite_test.go b/Backend/tests/e2e/suite_test.go index 886e32e..b50fa0a 100644 --- a/Backend/tests/e2e/suite_test.go +++ b/Backend/tests/e2e/suite_test.go @@ -100,6 +100,7 @@ func (suite *IntegrationTestSuite) SetupSuite() { playerService := service.NewPlayerService(&service.PlayerServiceOptions{ PlayerRepo: playerRepo, PlayerCardRepo: playerCardRepo, + GameRepo: gameRepo, }) gameService := service.NewGameService( @@ -110,6 +111,7 @@ func (suite *IntegrationTestSuite) SetupSuite() { DeckService: deckService, }, ) + playerService.GameServ = &gameService v1.RegisterGameHandler( &v1.GameHandlerOptions{ @@ -128,8 +130,10 @@ func (suite *IntegrationTestSuite) SetupSuite() { v1.RegisterPlayerHandler( &v1.PlayerHandlerOptions{ - Engine: engine, - Service: playerService, + Engine: engine, + Service: playerService, + GameService: gameService, + SSE: sse, }, ) @@ -146,7 +150,10 @@ func (suite *IntegrationTestSuite) SetupSuite() { func (suite *IntegrationTestSuite) TearDownSuite() { sqlDB, _ := suite.db.DB() - sqlDB.Close() + err := sqlDB.Close() + if err != nil { + return + } suite.server.Close() } From e5032fb17563dcde723285c867cf6b20e27f6028 Mon Sep 17 00:00:00 2001 From: Nicole Date: Tue, 23 Jan 2024 22:35:25 +0800 Subject: [PATCH 76/89] add mock accept api --- .../delivery/http/v1/player_handler.go | 34 +++++++------------ .../mysql/player_card_repository.go | 2 +- Backend/service/service/player_service.go | 33 +++++++++--------- 3 files changed, 31 insertions(+), 38 deletions(-) diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 6262004..9e55def 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -33,7 +33,7 @@ func RegisterPlayerHandler(opts *PlayerHandlerOptions) { opts.Engine.POST("/api/v1/players/:playerId/player-cards", handler.PlayCard) opts.Engine.POST("/api/v1/player/:playerId/transmit-intelligence", handler.TransmitIntelligence) - // opts.Engine.POST("/api/v1/players/:playerId/accept", handler.AcceptCard) + opts.Engine.POST("/api/v1/players/:playerId/accept", handler.AcceptCard) } // PlayCard godoc @@ -121,31 +121,23 @@ func (p *PlayerHandler) TransmitIntelligence(c *gin.Context) { return } - // TODO to Service - p.SSE.Message <- gin.H{ - "game_id": game.Id, - "status": game.Status, - "message": fmt.Sprintf("玩家: %d 已出牌", playerId), - "card": card.Name, - "next_player": game.CurrentPlayerId, - } - c.JSON(http.StatusOK, gin.H{ "result": ret, "message": enums.ToString(req.IntelligenceType) + " intelligence transmitted", }) } -// func (p *PlayerHandler) AcceptCard(c *gin.Context) { -// playerId, _ := strconv.Atoi(c.Param("playerId")) +func (p *PlayerHandler) AcceptCard(c *gin.Context) { + playerId, _ := strconv.Atoi(c.Param("playerId")) -// result, err := p.playerService.AcceptCard(c, playerId) -// if err != nil { -// c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) -// return -// } + result, err := p.playerService.AcceptCard(c, playerId) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } -// c.JSON(http.StatusOK, gin.H{ -// "message": "success", -// }) -// } + c.JSON(http.StatusOK, gin.H{ + "result": result, + "message": "success", + }) +} diff --git a/Backend/service/repository/mysql/player_card_repository.go b/Backend/service/repository/mysql/player_card_repository.go index a7f3b72..390ce2c 100644 --- a/Backend/service/repository/mysql/player_card_repository.go +++ b/Backend/service/repository/mysql/player_card_repository.go @@ -39,7 +39,7 @@ func (p PlayerCardRepository) DeletePlayerCard(ctx context.Context, id int) erro return nil } -func (p PlayerCardRepository) DeletePlayerCardByPlayerIdAndCardId(ctx context.Context, playerId int, gameId int, cardId int) (bool, error) { +func (p PlayerCardRepository) (ctx context.Context, playerId int, gameId int, cardId int) (bool, error) { card := new(repository.PlayerCard) result := p.db.Delete(&card, "player_id = ? AND game_id = ? AND card_id = ?", playerId, gameId, cardId) diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index b4e0480..18fdca7 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -213,19 +213,20 @@ func (p *PlayerService) TransmitIntelligenceCard(c *gin.Context, playerId int, g return ret, nil } -// func (p *PlayerService) AcceptCard(c *gin.Context, playerId int) (bool, error) { -// player, err := p.PlayerRepo.GetPlayer(c, playerId) -// if err != nil { -// return false, err -// } - -// cards, err := p.PlayerCardRepo.GetPlayerCards(c, &repository.PlayerCard{ -// PlayerId: player.Id, -// GameId: player.GameId, -// Type: "hand", -// }) -// if err != nil { -// return false, err -// } -// return true, nil -// } +func (p *PlayerService) AcceptCard(c *gin.Context, playerId int) (bool, error) { + + // get target player id from game process + + // assume the type is SecretTelegram + + // check if the player is the next player + + // decide whether accpet or not + + // + // player, err := p.PlayerRepo.GetPlayer(c, playerId) + // if err != nil { + // return false, err + // } + return true, nil +} From b86a2abddef1a622e6a32a2e3b4ec8f21449e99c Mon Sep 17 00:00:00 2001 From: Nicole Date: Wed, 24 Jan 2024 21:40:27 +0800 Subject: [PATCH 77/89] add GetGameProgresses --- .../repository/game_progresses_repository.go | 4 +++- .../mysql/game_process_repository.go | 12 ++++++++++ .../mysql/player_card_repository.go | 3 ++- Backend/service/service/player_service.go | 23 +++++++++++++------ 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/Backend/service/repository/game_progresses_repository.go b/Backend/service/repository/game_progresses_repository.go index fd30996..726c7ca 100644 --- a/Backend/service/repository/game_progresses_repository.go +++ b/Backend/service/repository/game_progresses_repository.go @@ -2,8 +2,9 @@ package repository import ( "context" - "gorm.io/gorm" "time" + + "gorm.io/gorm" ) type GameProgresses struct { @@ -20,4 +21,5 @@ type GameProgresses struct { type GameProgressesRepository interface { CreateGameProgress(c context.Context, gameProgress *GameProgresses) (*GameProgresses, error) + GetGameProgresses(c context.Context, playerId int, gameId int) (*GameProgresses, error) } diff --git a/Backend/service/repository/mysql/game_process_repository.go b/Backend/service/repository/mysql/game_process_repository.go index 012b266..4b088b8 100644 --- a/Backend/service/repository/mysql/game_process_repository.go +++ b/Backend/service/repository/mysql/game_process_repository.go @@ -2,6 +2,7 @@ package mysql import ( "context" + "github.com/Game-as-a-Service/The-Message/service/repository" "gorm.io/gorm" ) @@ -25,3 +26,14 @@ func (g *GameProgressRepository) CreateGameProgress(c context.Context, gameProgr return gameProgress, nil } + +func (g *GameProgressRepository) GetGameProgresses(c context.Context, playerId int, gameId int) (*repository.GameProgresses, error) { + var gameProgress *repository.GameProgresses + + result := g.db.First(&gameProgress, "player_id = ? AND game_id = ?", playerId, gameId) + if result.Error != nil { + return nil, result.Error + } + + return gameProgress, nil +} diff --git a/Backend/service/repository/mysql/player_card_repository.go b/Backend/service/repository/mysql/player_card_repository.go index 390ce2c..ebfd84b 100644 --- a/Backend/service/repository/mysql/player_card_repository.go +++ b/Backend/service/repository/mysql/player_card_repository.go @@ -3,6 +3,7 @@ package mysql import ( "context" "errors" + "github.com/Game-as-a-Service/The-Message/service/repository" "gorm.io/gorm" ) @@ -39,7 +40,7 @@ func (p PlayerCardRepository) DeletePlayerCard(ctx context.Context, id int) erro return nil } -func (p PlayerCardRepository) (ctx context.Context, playerId int, gameId int, cardId int) (bool, error) { +func (p PlayerCardRepository) DeletePlayerCardByPlayerIdAndCardId(ctx context.Context, playerId int, gameId int, cardId int) (bool, error) { card := new(repository.PlayerCard) result := p.db.Delete(&card, "player_id = ? AND game_id = ? AND card_id = ?", playerId, gameId, cardId) diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 18fdca7..4613d9a 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -210,23 +210,32 @@ func (p *PlayerService) TransmitIntelligenceCard(c *gin.Context, playerId int, g TargetPlayerId: game.CurrentPlayerId, }) + if err != nil { + return false, err + } + return ret, nil } func (p *PlayerService) AcceptCard(c *gin.Context, playerId int) (bool, error) { + player, err := p.PlayerRepo.GetPlayerWithGamePlayersAndPlayerCardsCard(c, playerId) + if err != nil { + return false, err + } + + result, err := p.CanPlayCard(c, player) + if !result || err != nil { + return false, err + } // get target player id from game process + gameId := player.Game.Id + gameProgress, err := p.GameProgressRepo.GetGameProgresses(c, playerId, gameId) + cardId := gameProgress.CardId // assume the type is SecretTelegram - // check if the player is the next player - // decide whether accpet or not - // - // player, err := p.PlayerRepo.GetPlayer(c, playerId) - // if err != nil { - // return false, err - // } return true, nil } From ede5f9bacd7ce69d3ff8ac5432857ec99dbb9b12 Mon Sep 17 00:00:00 2001 From: Nicole Date: Wed, 24 Jan 2024 22:49:07 +0800 Subject: [PATCH 78/89] add UpdateGameProgress and implement accept card --- Backend/cmd/app/docs/docs.go | 107 +++++++++++++++++- .../delivery/http/v1/player_handler.go | 21 +++- .../repository/game_progresses_repository.go | 3 +- .../mysql/game_process_repository.go | 18 ++- Backend/service/request/game_request.go | 4 + Backend/service/service/player_service.go | 38 ++++++- 6 files changed, 176 insertions(+), 15 deletions(-) diff --git a/Backend/cmd/app/docs/docs.go b/Backend/cmd/app/docs/docs.go index 240b875..1a72e82 100644 --- a/Backend/cmd/app/docs/docs.go +++ b/Backend/cmd/app/docs/docs.go @@ -130,6 +130,97 @@ const docTemplate = `{ } } }, + "/api/v1/player/{playerId}/transmit-intelligence": { + "post": { + "description": "Transmit an intelligence card", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "players" + ], + "summary": "Transmit intelligence", + "parameters": [ + { + "type": "integer", + "description": "Player ID", + "name": "playerId", + "in": "path", + "required": true + }, + { + "description": "Card ID", + "name": "card_id", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PlayCardRequest" + } + }, + { + "description": "Intelligence Type", + "name": "intelligence_type", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PlayCardRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/request.PlayCardResponse" + } + } + } + } + }, + "/api/v1/players/{playerId}/accept": { + "post": { + "description": "Decide accept card or not", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "players" + ], + "summary": "Accept Card", + "parameters": [ + { + "type": "integer", + "description": "Player ID", + "name": "playerId", + "in": "path", + "required": true + }, + { + "description": "Accept", + "name": "accept", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AcceptCardRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/request.PlayCardResponse" + } + } + } + } + }, "/api/v1/players/{playerId}/player-cards": { "post": { "description": "Play a card", @@ -176,9 +267,8 @@ const docTemplate = `{ "http.GameSSERequest": { "type": "object", "properties": { - "gameId": { - "type": "string", - "example": "0" + "game_id": { + "type": "integer" }, "message": { "type": "string" @@ -188,6 +278,14 @@ const docTemplate = `{ } } }, + "request.AcceptCardRequest": { + "type": "object", + "properties": { + "accept": { + "type": "boolean" + } + } + }, "request.CreateGameRequest": { "type": "object", "properties": { @@ -215,6 +313,9 @@ const docTemplate = `{ "properties": { "card_id": { "type": "integer" + }, + "intelligence_type": { + "type": "integer" } } }, diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 9e55def..9bfafae 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -127,17 +127,32 @@ func (p *PlayerHandler) TransmitIntelligence(c *gin.Context) { }) } +// AcceptCard godoc +// @Summary Accept Card +// @Description Decide accept card or not +// @Tags players +// @Accept json +// @Produce json +// @Param playerId path int true "Player ID" +// @Param accept body request.AcceptCardRequest true "Accept" +// @Success 200 {object} request.PlayCardResponse +// @Router /api/v1/players/{playerId}/accept [post] func (p *PlayerHandler) AcceptCard(c *gin.Context) { playerId, _ := strconv.Atoi(c.Param("playerId")) + var req request.AcceptCardRequest + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()}) + return + } - result, err := p.playerService.AcceptCard(c, playerId) + result, err := p.playerService.AcceptCard(c, playerId, req.Accept) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ - "result": result, - "message": "success", + "result": result }) } diff --git a/Backend/service/repository/game_progresses_repository.go b/Backend/service/repository/game_progresses_repository.go index 726c7ca..77630f9 100644 --- a/Backend/service/repository/game_progresses_repository.go +++ b/Backend/service/repository/game_progresses_repository.go @@ -21,5 +21,6 @@ type GameProgresses struct { type GameProgressesRepository interface { CreateGameProgress(c context.Context, gameProgress *GameProgresses) (*GameProgresses, error) - GetGameProgresses(c context.Context, playerId int, gameId int) (*GameProgresses, error) + GetGameProgresses(c context.Context, targetPlayerId int, gameId int) (*GameProgresses, error) + UpdateGameProgress(c context.Context, gameProgress *GameProgresses, next_playerId int) (*GameProgresses, error) } diff --git a/Backend/service/repository/mysql/game_process_repository.go b/Backend/service/repository/mysql/game_process_repository.go index 4b088b8..04cc433 100644 --- a/Backend/service/repository/mysql/game_process_repository.go +++ b/Backend/service/repository/mysql/game_process_repository.go @@ -27,10 +27,24 @@ func (g *GameProgressRepository) CreateGameProgress(c context.Context, gameProgr return gameProgress, nil } -func (g *GameProgressRepository) GetGameProgresses(c context.Context, playerId int, gameId int) (*repository.GameProgresses, error) { +func (g *GameProgressRepository) GetGameProgresses(c context.Context, targetPlayerId int, gameId int) (*repository.GameProgresses, error) { var gameProgress *repository.GameProgresses - result := g.db.First(&gameProgress, "player_id = ? AND game_id = ?", playerId, gameId) + result := g.db.First(&gameProgress, "target_player_id = ? AND game_id = ?", targetPlayerId, gameId) + if result.Error != nil { + return nil, result.Error + } + + return gameProgress, nil +} + +func (g *GameProgressRepository) UpdateGameProgress(c context.Context, gameProgress *repository.GameProgresses, next_playerId int) (*repository.GameProgresses, error) { + // result := g.db.Update(&gameProgress) + result := g.db.First(&gameProgress) + + gameProgress.TargetPlayerId = next_playerId + g.db.Save(&gameProgress) + if result.Error != nil { return nil, result.Error } diff --git a/Backend/service/request/game_request.go b/Backend/service/request/game_request.go index e17d5c8..d2f4623 100644 --- a/Backend/service/request/game_request.go +++ b/Backend/service/request/game_request.go @@ -22,3 +22,7 @@ type PlayCardRequest struct { CardID int `json:"card_id"` IntelligenceType int `json:"intelligence_type"` } + +type AcceptCardRequest struct { + Accept bool `json:"accept"` +} diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 4613d9a..898be12 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -217,7 +217,7 @@ func (p *PlayerService) TransmitIntelligenceCard(c *gin.Context, playerId int, g return ret, nil } -func (p *PlayerService) AcceptCard(c *gin.Context, playerId int) (bool, error) { +func (p *PlayerService) AcceptCard(c *gin.Context, playerId int, accept bool) (bool, error) { player, err := p.PlayerRepo.GetPlayerWithGamePlayersAndPlayerCardsCard(c, playerId) if err != nil { return false, err @@ -228,14 +228,40 @@ func (p *PlayerService) AcceptCard(c *gin.Context, playerId int) (bool, error) { return false, err } - // get target player id from game process - gameId := player.Game.Id - gameProgress, err := p.GameProgressRepo.GetGameProgresses(c, playerId, gameId) + game, err := p.GameServ.NextPlayer(c, player) + if err != nil { + return false, err + } + gameId := game.Id + gameProgress, err := p.GameProgressRepo.GetGameProgresses(c, playerId, gameId) + if err != nil { + return false, err + } cardId := gameProgress.CardId // assume the type is SecretTelegram + res := accept + if accept { + _, err := p.PlayerCardRepo.CreatePlayerCard(c, &repository.PlayerCard{ + PlayerId: playerId, + GameId: gameId, + CardId: cardId, + Type: "intelligence", + }) + if err != nil { + return false, err + } + } else { + _, err := p.GameProgressRepo.UpdateGameProgress(c, gameProgress, game.CurrentPlayerId) + if err != nil { + return false, err + } + } - // decide whether accpet or not + err = p.GameRepo.UpdateGame(c, game) + if err != nil { + return false, err + } - return true, nil + return res, nil } From fdff65d25cee90bf2c560b60f18137035441f09c Mon Sep 17 00:00:00 2001 From: Nicole Date: Thu, 25 Jan 2024 15:41:00 +0800 Subject: [PATCH 79/89] use UpdateStatus to change game state if accepted --- Backend/service/delivery/http/v1/player_handler.go | 2 +- Backend/service/service/player_service.go | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 9bfafae..d37b106 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -153,6 +153,6 @@ func (p *PlayerHandler) AcceptCard(c *gin.Context) { } c.JSON(http.StatusOK, gin.H{ - "result": result + "result": result, }) } diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 898be12..6224d18 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -251,16 +251,18 @@ func (p *PlayerService) AcceptCard(c *gin.Context, playerId int, accept bool) (b if err != nil { return false, err } + p.GameServ.UpdateStatus(c, game, enums.ActionCardStage) + } else { _, err := p.GameProgressRepo.UpdateGameProgress(c, gameProgress, game.CurrentPlayerId) if err != nil { return false, err } - } - err = p.GameRepo.UpdateGame(c, game) - if err != nil { - return false, err + err = p.GameRepo.UpdateGame(c, game) + if err != nil { + return false, err + } } return res, nil From 06fdc6c5047f1e580dfff03d8747ef4d66fd0f6f Mon Sep 17 00:00:00 2001 From: Nicole Date: Sun, 28 Jan 2024 09:40:27 +0800 Subject: [PATCH 80/89] fix conflict issue --- Backend/.DS_Store | Bin 8196 -> 0 bytes .../service/delivery/http/v1/player_handler.go | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 Backend/.DS_Store diff --git a/Backend/.DS_Store b/Backend/.DS_Store deleted file mode 100644 index a78e606470c7454fdecc5d6bf2b0082816e3db06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHMOHLa>5Us}M6ImdZUiLC86mo*GarQ_o*${vDNS+J|HjzM)X3sqk2VljD4cvnh z(a?&9(7a))Pbr47E>)6QBLFONxXmGIb25aVlWs^iy>zC&us7K?Ncv~j^Bu$pT=i( zF9+7ZSI{>)rZYN`tesn&SHxrP-p#}2{hi}Za+V63a-@z7G`N;AeWe-t5dDHCiI$hT zoLCRy-D~efZNJZvSMx~Z=a^P{dxv>h$OtP&*UO1IFqR0>wV(u zeQmtdT6D%NVCj*kcwX0kHbR>^@D3eldCgtk|9@$I|NjoJOeL!W>cF2mVA_L&K_A&} yRj;YyRop>yNB(xWTEApQ}+=fw|(IJQXTnk-=H5i@A|BEZ?8gF5h69rz8f_3O3( diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index d37b106..04c309f 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -117,7 +117,7 @@ func (p *PlayerHandler) TransmitIntelligence(c *gin.Context) { ret, err := p.playerService.TransmitIntelligenceCard(c, playerId, player.GameId, req.CardID) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()}) + c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } From 84bb03ff94aeb9d347812ed71204a020df255df1 Mon Sep 17 00:00:00 2001 From: KenLin Date: Sun, 4 Feb 2024 20:47:45 +0800 Subject: [PATCH 81/89] feat: Add SSE to return the winner information after receiving the game card if the victory condition is met --- Backend/enums/card_colors.go | 7 +++ Backend/enums/hand_card_types.go | 6 +++ .../delivery/http/v1/player_handler.go | 11 +++++ .../repository/mysql/player_repository.go | 2 +- Backend/service/service/game_service.go | 3 +- Backend/service/service/player_service.go | 46 +++++++++++++++++-- 6 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 Backend/enums/card_colors.go create mode 100644 Backend/enums/hand_card_types.go diff --git a/Backend/enums/card_colors.go b/Backend/enums/card_colors.go new file mode 100644 index 0000000..791d847 --- /dev/null +++ b/Backend/enums/card_colors.go @@ -0,0 +1,7 @@ +package enums + +const ( + Red = "紅" + Blue = "藍" + Black = "黑" +) diff --git a/Backend/enums/hand_card_types.go b/Backend/enums/hand_card_types.go new file mode 100644 index 0000000..32b3490 --- /dev/null +++ b/Backend/enums/hand_card_types.go @@ -0,0 +1,6 @@ +package enums + +const ( + Intelligence = "intelligence" + Hand = "hand" +) diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index 04c309f..c65ce83 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -152,7 +152,18 @@ func (p *PlayerHandler) AcceptCard(c *gin.Context) { return } + winner, err := p.playerService.CheckWin(c, playerId) + if winner != nil { + p.SSE.Message <- gin.H{ + "game_id": winner.Game.Id, + "status": winner.Game.Status, + "message": fmt.Sprintf("玩家: %d 已贏得遊戲", playerId), + "winner": winner.Name, + } + } + c.JSON(http.StatusOK, gin.H{ "result": result, }) + } diff --git a/Backend/service/repository/mysql/player_repository.go b/Backend/service/repository/mysql/player_repository.go index 56fec6c..b439d4a 100644 --- a/Backend/service/repository/mysql/player_repository.go +++ b/Backend/service/repository/mysql/player_repository.go @@ -64,7 +64,7 @@ func (p *PlayerRepository) GetPlayerWithGame(ctx context.Context, playerId int) func (p *PlayerRepository) GetPlayerWithGamePlayersAndPlayerCardsCard(ctx context.Context, playerId int) (*repository.Player, error) { var player repository.Player - if err := p.db.Preload("Game.Players").Preload("PlayerCards.Card").First(&player, playerId).Error; err != nil { + if err := p.db.Preload("Game.Players.PlayerCards.Card").Preload("Game.Players.Game").Preload("PlayerCards.Card").First(&player, playerId).Error; err != nil { return nil, err } diff --git a/Backend/service/service/game_service.go b/Backend/service/service/game_service.go index cf050e9..3fe72c8 100644 --- a/Backend/service/service/game_service.go +++ b/Backend/service/service/game_service.go @@ -7,7 +7,6 @@ import ( "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" - "github.com/gin-gonic/gin" ) type GameService struct { @@ -134,7 +133,7 @@ func (g *GameService) UpdateCurrentPlayer(c context.Context, game *repository.Ga } } -func (g *GameService) NextPlayer(c *gin.Context, player *repository.Player) (*repository.Game, error) { +func (g *GameService) NextPlayer(c context.Context, player *repository.Player) (*repository.Game, error) { players := player.Game.Players currentPlayerId := player.Id diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 6224d18..7ad85da 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -9,7 +9,6 @@ import ( "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" "github.com/Game-as-a-Service/The-Message/service/request" - "github.com/gin-gonic/gin" ) type PlayerService struct { @@ -142,7 +141,7 @@ func (p *PlayerService) GetHandCardId(player *repository.Player, cardId int) (*r return nil, errors.New("找不到手牌") } -func (p *PlayerService) PlayCard(c *gin.Context, playerId int, cardId int) (*repository.Game, *repository.Card, error) { +func (p *PlayerService) PlayCard(c context.Context, playerId int, cardId int) (*repository.Game, *repository.Card, error) { player, err := p.PlayerRepo.GetPlayerWithGamePlayersAndPlayerCardsCard(c, playerId) if err != nil { return nil, nil, err @@ -176,7 +175,7 @@ func (p *PlayerService) PlayCard(c *gin.Context, playerId int, cardId int) (*rep return game, &handCard.Card, nil } -func (p *PlayerService) TransmitIntelligenceCard(c *gin.Context, playerId int, gameId int, cardId int) (bool, error) { +func (p *PlayerService) TransmitIntelligenceCard(c context.Context, playerId int, gameId int, cardId int) (bool, error) { player, err := p.PlayerRepo.GetPlayerWithGamePlayersAndPlayerCardsCard(c, playerId) if err != nil { return false, err @@ -217,7 +216,7 @@ func (p *PlayerService) TransmitIntelligenceCard(c *gin.Context, playerId int, g return ret, nil } -func (p *PlayerService) AcceptCard(c *gin.Context, playerId int, accept bool) (bool, error) { +func (p *PlayerService) AcceptCard(c context.Context, playerId int, accept bool) (bool, error) { player, err := p.PlayerRepo.GetPlayerWithGamePlayersAndPlayerCardsCard(c, playerId) if err != nil { return false, err @@ -267,3 +266,42 @@ func (p *PlayerService) AcceptCard(c *gin.Context, playerId int, accept bool) (b return res, nil } + +func (p *PlayerService) CheckWin(c context.Context, playerId int) (*repository.Player, error) { + player, err := p.PlayerRepo.GetPlayerWithGamePlayersAndPlayerCardsCard(c, playerId) + if err != nil { + return nil, err + } + + win := 0 + var winPlayer *repository.Player + for _, player := range player.Game.Players { + win = 0 + for _, card := range player.PlayerCards { + if card.Type == enums.Intelligence && player.IdentityCard == enums.MilitaryAgency && card.Card.Color == enums.Red { + win++ + if win == 3 { + winPlayer = &player + break + } + } + + if card.Type == enums.Intelligence && player.IdentityCard == enums.UndercoverFront && card.Card.Color == enums.Blue { + win++ + if win == 3 { + winPlayer = &player + break + } + } + + if card.Type == enums.Intelligence && player.IdentityCard == enums.MilitaryAgency && card.Card.Color == enums.Red || card.Card.Color == enums.Blue { + win++ + if win == 5 { + winPlayer = &player + break + } + } + } + } + return winPlayer, nil +} From d96102c38b13f25d68a7e04e4831a3cc7e13fa77 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Thu, 8 Feb 2024 20:38:31 +0800 Subject: [PATCH 82/89] Feat: Add intelligence_type field to the cards table as the type of intelligence card and add seeder data --- ..._add_intelligence_type_to_cards_table.down.sql | 5 +++++ ...07_add_intelligence_type_to_cards_table.up.sql | 5 +++++ Backend/database/seeders/game_card_seeder.go | 6 ++++-- Backend/enums/game_cards.go | 15 ++++++++++++++- Backend/enums/intelligence_types.go | 8 ++++---- Backend/service/repository/card_repository.go | 15 ++++++++------- 6 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 Backend/database/migrations/000007_add_intelligence_type_to_cards_table.down.sql create mode 100644 Backend/database/migrations/000007_add_intelligence_type_to_cards_table.up.sql diff --git a/Backend/database/migrations/000007_add_intelligence_type_to_cards_table.down.sql b/Backend/database/migrations/000007_add_intelligence_type_to_cards_table.down.sql new file mode 100644 index 0000000..56a1574 --- /dev/null +++ b/Backend/database/migrations/000007_add_intelligence_type_to_cards_table.down.sql @@ -0,0 +1,5 @@ +BEGIN; + +ALTER TABLE cards DROP COLUMN intelligence_type; + +COMMIT; diff --git a/Backend/database/migrations/000007_add_intelligence_type_to_cards_table.up.sql b/Backend/database/migrations/000007_add_intelligence_type_to_cards_table.up.sql new file mode 100644 index 0000000..0c9aea3 --- /dev/null +++ b/Backend/database/migrations/000007_add_intelligence_type_to_cards_table.up.sql @@ -0,0 +1,5 @@ +BEGIN; + +ALTER TABLE cards ADD COLUMN intelligence_type TINYINT COMMENT '1 密電, 2 直達, 3 文件' AFTER color; + +COMMIT; diff --git a/Backend/database/seeders/game_card_seeder.go b/Backend/database/seeders/game_card_seeder.go index a6e0084..d6a1461 100644 --- a/Backend/database/seeders/game_card_seeder.go +++ b/Backend/database/seeders/game_card_seeder.go @@ -2,6 +2,7 @@ package seeders import ( "context" + "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" "github.com/Game-as-a-Service/The-Message/service/repository/mysql" @@ -25,8 +26,9 @@ func SeederCards(db *gorm.DB) { for color, count := range colors { for i := 0; i < count; i++ { mysql.NewCardRepository(db).CreateCard(context.TODO(), &repository.Card{ - Name: actionType, - Color: color, + Name: actionType, + Color: color, + IntelligenceType: enums.ToIntelligenceType(actionType), }) } } diff --git a/Backend/enums/game_cards.go b/Backend/enums/game_cards.go index d1edb18..cf9ab15 100644 --- a/Backend/enums/game_cards.go +++ b/Backend/enums/game_cards.go @@ -6,8 +6,21 @@ const ( Probe = "試探" Intercept = "截獲" Decipher = "破譯" - Diversion = "轉移" + Diversion = "退回" Burn = "燒毀" BlurOfTruth = "真偽莫辯" SeeThrough = "識破" ) + +func ToIntelligenceType(actionType string) int { + switch actionType { + case LockOn, LureAway, Probe, Decipher: + return SecretTelegram + case Intercept, Burn, SeeThrough: + return Direct + case Diversion, BlurOfTruth: + return Document + default: + return 0 + } +} diff --git a/Backend/enums/intelligence_types.go b/Backend/enums/intelligence_types.go index fe95fe3..ee90690 100644 --- a/Backend/enums/intelligence_types.go +++ b/Backend/enums/intelligence_types.go @@ -2,17 +2,17 @@ package enums const ( SecretTelegram = 1 - DIRECT = 2 - DOCUMENT = 3 + Direct = 2 + Document = 3 ) func ToString(secretTelegramType int) string { switch secretTelegramType { case SecretTelegram: return "密電" - case DIRECT: + case Direct: return "直達" - case DOCUMENT: + case Document: return "文件" default: return "" diff --git a/Backend/service/repository/card_repository.go b/Backend/service/repository/card_repository.go index 3711502..ab174b3 100644 --- a/Backend/service/repository/card_repository.go +++ b/Backend/service/repository/card_repository.go @@ -9,13 +9,14 @@ import ( type Card struct { gorm.Model - Id int `gorm:"primaryKey;auto_increment"` - Name string - Color string - PlayerCards []PlayerCard - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoCreateTime"` - DeletedAt gorm.DeletedAt + Id int `gorm:"primaryKey;auto_increment"` + Name string + Color string + IntelligenceType int + PlayerCards []PlayerCard + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoCreateTime"` + DeletedAt gorm.DeletedAt } type CardRepository interface { From d6a80c0b1494ec72dbfedb570225d1c0ed149324 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Thu, 8 Feb 2024 20:56:36 +0800 Subject: [PATCH 83/89] Feat: Removed the need to send the intelligenceType body parameter when transmitting the intelligence card --- .../delivery/http/v1/player_handler.go | 12 +--- .../repository/mysql/player_repository.go | 2 +- .../service/repository/player_repository.go | 2 +- Backend/service/request/game_request.go | 3 +- Backend/service/service/card_service.go | 2 +- Backend/service/service/player_service.go | 2 +- Backend/tests/e2e/player_api_test.go | 66 +++---------------- 7 files changed, 14 insertions(+), 75 deletions(-) diff --git a/Backend/service/delivery/http/v1/player_handler.go b/Backend/service/delivery/http/v1/player_handler.go index c65ce83..7f7b977 100644 --- a/Backend/service/delivery/http/v1/player_handler.go +++ b/Backend/service/delivery/http/v1/player_handler.go @@ -5,7 +5,6 @@ import ( "net/http" "strconv" - "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/request" "github.com/Game-as-a-Service/The-Message/service/service" "github.com/gin-gonic/gin" @@ -82,7 +81,6 @@ func (p *PlayerHandler) PlayCard(c *gin.Context) { // @Produce json // @Param playerId path int true "Player ID" // @Param card_id body request.PlayCardRequest true "Card ID" -// @Param intelligence_type body request.PlayCardRequest true "Intelligence Type" // @Success 200 {object} request.PlayCardResponse // @Router /api/v1/player/{playerId}/transmit-intelligence [post] func (p *PlayerHandler) TransmitIntelligence(c *gin.Context) { @@ -94,13 +92,6 @@ func (p *PlayerHandler) TransmitIntelligence(c *gin.Context) { return } - intelligenceType := enums.ToString(req.IntelligenceType) - - if intelligenceType == "" { - c.JSON(http.StatusBadRequest, gin.H{"message": "Invalid intelligence type"}) - return - } - player, err := p.playerService.GetPlayerById(c, playerId) if err != nil || player == nil { @@ -122,8 +113,7 @@ func (p *PlayerHandler) TransmitIntelligence(c *gin.Context) { } c.JSON(http.StatusOK, gin.H{ - "result": ret, - "message": enums.ToString(req.IntelligenceType) + " intelligence transmitted", + "result": ret, }) } diff --git a/Backend/service/repository/mysql/player_repository.go b/Backend/service/repository/mysql/player_repository.go index b439d4a..5fe8914 100644 --- a/Backend/service/repository/mysql/player_repository.go +++ b/Backend/service/repository/mysql/player_repository.go @@ -22,7 +22,7 @@ func (p *PlayerRepository) CreatePlayer(ctx context.Context, player *repository. return player, err } -func (p *PlayerRepository) GetPlayer(ctx context.Context, playerId int) (*repository.Player, error) { +func (p *PlayerRepository) GetPlayerById(ctx context.Context, playerId int) (*repository.Player, error) { player := new(repository.Player) result := p.db.First(&player, "id = ?", playerId) diff --git a/Backend/service/repository/player_repository.go b/Backend/service/repository/player_repository.go index 76751c3..9aceabf 100644 --- a/Backend/service/repository/player_repository.go +++ b/Backend/service/repository/player_repository.go @@ -24,7 +24,7 @@ type Player struct { type PlayerRepository interface { CreatePlayer(ctx context.Context, player *Player) (*Player, error) - GetPlayer(ctx context.Context, playerId int) (*Player, error) + GetPlayerById(ctx context.Context, playerId int) (*Player, error) GetPlayersByGameId(ctx context.Context, id int) ([]*Player, error) GetPlayerWithPlayerCards(ctx context.Context, playerId int) (*Player, error) GetPlayerWithGame(ctx context.Context, playerId int) (*Player, error) diff --git a/Backend/service/request/game_request.go b/Backend/service/request/game_request.go index d2f4623..710e060 100644 --- a/Backend/service/request/game_request.go +++ b/Backend/service/request/game_request.go @@ -19,8 +19,7 @@ type PlayCardResponse struct { } type PlayCardRequest struct { - CardID int `json:"card_id"` - IntelligenceType int `json:"intelligence_type"` + CardID int `json:"card_id"` } type AcceptCardRequest struct { diff --git a/Backend/service/service/card_service.go b/Backend/service/service/card_service.go index 9116abd..d703aa9 100644 --- a/Backend/service/service/card_service.go +++ b/Backend/service/service/card_service.go @@ -38,7 +38,7 @@ func (c *CardService) GetCards(ctx context.Context) ([]*repository.Card, error) } func (c *CardService) GetPlayerCardsByPlayerId(ctx context.Context, id int) ([]*repository.Card, error) { - player, err := c.PlayerRepo.GetPlayer(ctx, id) + player, err := c.PlayerRepo.GetPlayerById(ctx, id) game, err := c.GameRepo.GetGameWithPlayers(ctx, player.GameId) if err != nil { return nil, err diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 7ad85da..dc98c5c 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -117,7 +117,7 @@ func (p *PlayerService) CreatePlayerCard(c context.Context, card *repository.Pla } func (p *PlayerService) GetPlayerById(c context.Context, id int) (*repository.Player, error) { - player, err := p.PlayerRepo.GetPlayer(c, id) + player, err := p.PlayerRepo.GetPlayerById(c, id) if err != nil { return nil, err } diff --git a/Backend/tests/e2e/player_api_test.go b/Backend/tests/e2e/player_api_test.go index 703a441..b20b393 100644 --- a/Backend/tests/e2e/player_api_test.go +++ b/Backend/tests/e2e/player_api_test.go @@ -100,33 +100,12 @@ func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { _ = suite.gameServ.InitDeck(context.TODO(), game) _ = suite.gameServ.DrawCardsForAllPlayers(context.TODO(), game) - suite.T().Run("it can validate intelligence type", func(t *testing.T) { - playerId := rand.Intn(playerCount) + 1 - cardId := rand.Intn(playerCount) - - // Request only card id - url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) - req := PlayCardRequest{CardId: cardId} - reqBody, _ := json.Marshal(req) - - res := suite.requestJson(url, reqBody, http.MethodPost) - - // Convert response body from json to map - resBodyAsByteArray, _ := io.ReadAll(res.Body) - resBody := make(map[string]interface{}) - _ = json.Unmarshal(resBodyAsByteArray, &resBody) - - assert.Equal(t, http.StatusBadRequest, res.StatusCode) - assert.Equal(t, "Invalid intelligence type", resBody["message"]) - }) - suite.T().Run("it can validate card id", func(t *testing.T) { playerId := rand.Intn(playerCount) + 1 - intelligenceType := rand.Intn(3) + 1 // Request only intelligence type url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) - req := PlayCardRequest{IntelligenceType: intelligenceType} + req := PlayCardRequest{} reqBody, _ := json.Marshal(req) res := suite.requestJson(url, reqBody, http.MethodPost) @@ -143,10 +122,9 @@ func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { suite.T().Run("it can fail when player not found", func(t *testing.T) { playerId := math.MaxInt32 cardId := rand.Intn(playerCount) - intelligenceType := rand.Intn(3) + 1 url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) - req := PlayCardRequest{CardId: cardId, IntelligenceType: intelligenceType} + req := PlayCardRequest{CardId: cardId} reqBody, _ := json.Marshal(req) res := suite.requestJson(url, reqBody, http.MethodPost) @@ -160,34 +138,12 @@ func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { assert.Equal(t, "Player not found", resBody["message"]) }) - suite.T().Run("it can fail when intelligence type is not valid", func(t *testing.T) { - playerId := rand.Intn(playerCount) + 1 - cardId := rand.Intn(playerCount) - intelligenceType := math.MaxInt32 - - url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) - req := PlayCardRequest{CardId: cardId, IntelligenceType: intelligenceType} - reqBody, _ := json.Marshal(req) - - res := suite.requestJson(url, reqBody, http.MethodPost) - - // Convert response body from json to map - resBodyAsByteArray, _ := io.ReadAll(res.Body) - resBody := make(map[string]interface{}) - _ = json.Unmarshal(resBodyAsByteArray, &resBody) - - assert.Equal(t, http.StatusBadRequest, res.StatusCode) - assert.Equal(t, "Invalid intelligence type", resBody["message"]) - - }) - suite.T().Run("it can fail when player card not found", func(t *testing.T) { playerId := rand.Intn(playerCount) + 1 cardId := math.MaxInt32 - intelligenceType := rand.Intn(3) + 1 url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) - req := PlayCardRequest{CardId: cardId, IntelligenceType: intelligenceType} + req := PlayCardRequest{CardId: cardId} reqBody, _ := json.Marshal(req) res := suite.requestJson(url, reqBody, http.MethodPost) @@ -203,7 +159,6 @@ func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { suite.T().Run("it can fail when game is end", func(t *testing.T) { playerId := rand.Intn(playerCount) + 1 - intelligenceType := rand.Intn(3) + 1 // Get player's card cards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), playerId) @@ -219,7 +174,7 @@ func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { suite.gameServ.UpdateStatus(context.TODO(), game, enums.GameEnd) url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) - req := PlayCardRequest{CardId: cardId, IntelligenceType: intelligenceType} + req := PlayCardRequest{CardId: cardId} reqBody, _ := json.Marshal(req) res := suite.requestJson(url, reqBody, http.MethodPost) @@ -238,7 +193,6 @@ func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { suite.T().Run("it can fail when not player's turn", func(t *testing.T) { playerId := rand.Intn(playerCount) + 1 - intelligenceType := rand.Intn(3) + 1 // Get player's card cards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), playerId) @@ -251,7 +205,7 @@ func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { suite.gameServ.UpdateCurrentPlayer(context.TODO(), game, playerId-1) url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) - req := PlayCardRequest{CardId: cardId, IntelligenceType: intelligenceType} + req := PlayCardRequest{CardId: cardId} reqBody, _ := json.Marshal(req) res := suite.requestJson(url, reqBody, http.MethodPost) @@ -265,9 +219,8 @@ func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { assert.Equal(t, "尚未輪到你出牌", resBody["message"]) }) - suite.T().Run("it can success when valid card id and intelligence type", func(t *testing.T) { + suite.T().Run("it can success when valid card id", func(t *testing.T) { playerId := rand.Intn(playerCount) + 1 - intelligenceType := rand.Intn(3) + 1 // Get player's card cards, _ := suite.playerRepo.GetPlayerWithPlayerCards(context.TODO(), playerId) @@ -280,7 +233,7 @@ func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { suite.gameServ.UpdateCurrentPlayer(context.TODO(), game, playerId) url := strings.ReplaceAll(api, "{player_id}", strconv.Itoa(playerId)) - req := PlayCardRequest{CardId: cardId, IntelligenceType: intelligenceType} + req := PlayCardRequest{CardId: cardId} reqBody, _ := json.Marshal(req) res := suite.requestJson(url, reqBody, http.MethodPost) @@ -290,14 +243,11 @@ func (suite *IntegrationTestSuite) TestTransmitIntelligenceE2E() { resBody := make(map[string]interface{}) _ = json.Unmarshal(resBodyAsByteArray, &resBody) - msg := enums.ToString(intelligenceType) + " intelligence transmitted" assert.Equal(t, http.StatusOK, res.StatusCode) - assert.Equal(t, msg, resBody["message"]) assert.Equal(t, true, resBody["result"]) }) } type PlayCardRequest struct { - CardId int `json:"card_id"` - IntelligenceType int `json:"intelligence_type"` + CardId int `json:"card_id"` } From 5b13addf9611490edfd98b7e51a94c35fce8af2a Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Thu, 8 Feb 2024 21:40:14 +0800 Subject: [PATCH 84/89] Chore: Upgrade faker to v4 and regularly update project dependencies --- Backend/go.mod | 50 ++++++++++---------- Backend/go.sum | 121 +++++++++++++++++++++++++------------------------ 2 files changed, 87 insertions(+), 84 deletions(-) diff --git a/Backend/go.mod b/Backend/go.mod index 9258d5c..c561ef8 100644 --- a/Backend/go.mod +++ b/Backend/go.mod @@ -3,26 +3,29 @@ module github.com/Game-as-a-Service/The-Message go 1.21 require ( - github.com/bxcodec/faker/v3 v3.8.1 github.com/gin-gonic/gin v1.9.1 - github.com/golang-migrate/migrate/v4 v4.16.2 + github.com/go-faker/faker/v4 v4.2.1 + github.com/go-sql-driver/mysql v1.7.1 + github.com/golang-migrate/migrate/v4 v4.17.0 github.com/joho/godotenv v1.5.1 - github.com/stretchr/testify v1.8.3 + github.com/mattes/migrate v3.0.1+incompatible + github.com/stretchr/testify v1.8.4 github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.0 - github.com/swaggo/swag v1.16.2 - gorm.io/driver/mysql v1.5.1 - gorm.io/gorm v1.25.4 + github.com/swaggo/swag v1.16.3 + gorm.io/driver/mysql v1.5.4 + gorm.io/gorm v1.25.7 ) require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/bytedance/sonic v1.9.1 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/bytedance/sonic v1.10.2 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.6 // indirect @@ -30,34 +33,33 @@ require ( github.com/go-openapi/swag v0.19.15 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.14.0 // indirect - github.com/go-sql-driver/mysql v1.7.1 // indirect + github.com/go-playground/validator/v10 v10.17.0 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/leodido/go-urn v1.2.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/mailru/easyjson v0.7.6 // indirect - github.com/mattes/migrate v3.0.1+incompatible // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.11 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect go.uber.org/atomic v1.7.0 // indirect - golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - golang.org/x/tools v0.9.1 // indirect - google.golang.org/protobuf v1.30.0 // indirect + golang.org/x/arch v0.7.0 // indirect + golang.org/x/crypto v0.19.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.17.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/Backend/go.sum b/Backend/go.sum index 470bd1b..a850037 100644 --- a/Backend/go.sum +++ b/Backend/go.sum @@ -8,36 +8,41 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/bxcodec/faker/v3 v3.8.1 h1:qO/Xq19V6uHt2xujwpaetgKhraGCapqY2CRWGD/SqcM= -github.com/bxcodec/faker/v3 v3.8.1/go.mod h1:DdSDccxF5msjFo5aO4vrobRQ8nIApg8kq3QWPEQD6+o= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE= +github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= +github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dhui/dktest v0.3.16 h1:i6gq2YQEtcrjKbeJpBkWjE8MmLZPYllcjOFbTZuPDnw= -github.com/dhui/dktest v0.3.16/go.mod h1:gYaA3LRmM8Z4vJl2MA0THIigJoZrwOansEOsp+kqxp0= +github.com/dhui/dktest v0.4.0 h1:z05UmuXZHO/bgj/ds2bGMBu8FI4WA+Ag/m3ghL+om7M= +github.com/dhui/dktest v0.4.0/go.mod h1:v/Dbz1LgCBOi2Uki2nUqLBGa83hWBGFMu5MrgMDCc78= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE= -github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= -github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-faker/faker/v4 v4.2.1 h1:w+kvZMvVwgn1tfl7W35tC+mlqtr3CN3tnXdHJxOGGO8= +github.com/go-faker/faker/v4 v4.2.1/go.mod h1:F/bBy8GH9NxOxMInug5Gx4WYeG6fHJZ8Ol/dhcpRub4= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -54,8 +59,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74= +github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= @@ -63,12 +68,10 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-migrate/migrate/v4 v4.16.2 h1:8coYbMKUyInrFk1lfGfRovTLAW7PhWp8qQDT2iKfuoA= -github.com/golang-migrate/migrate/v4 v4.16.2/go.mod h1:pfcJX4nPHaVdc5nmdCikFBWtm+UBpiZjRNNsyBbp0/o= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/golang-migrate/migrate/v4 v4.17.0 h1:rd40H3QXU0AA4IoLllFcEAEo9dYKRHYND2gB4p7xcaU= +github.com/golang-migrate/migrate/v4 v4.17.0/go.mod h1:+Cp2mtLP4/aXDTKb9wmXYitdrNx2HGs45rbWAo6OsKM= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= @@ -86,23 +89,26 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattes/migrate v3.0.1+incompatible h1:PhAZP82Vqejw8JZLF4U5UkLGzEVaCnbtJpB6DONcDow= github.com/mattes/migrate v3.0.1+incompatible/go.mod h1:LJcqgpj1jQoxv3m2VXd3drv0suK5CbN/RCX7MXwgnVI= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -118,14 +124,12 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= -github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -135,39 +139,38 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= -github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04= -github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E= +github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= +github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= +golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -175,12 +178,11 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -189,18 +191,16 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= @@ -212,9 +212,10 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw= -gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o= -gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= -gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw= -gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/driver/mysql v1.5.4 h1:igQmHfKcbaTVyAIHNhhB888vvxh8EdQ2uSUT0LPcBso= +gorm.io/driver/mysql v1.5.4/go.mod h1:9rYxJph/u9SWkWc9yY4XJ1F/+xO0S/ChOmbk3+Z5Tvs= +gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= From 0828324db08608e0b00372bed1509ded6b78dc1d Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Thu, 8 Feb 2024 21:42:48 +0800 Subject: [PATCH 85/89] Style: Use goimports to optimize the project code import style --- Backend/cmd/app/main.go | 2 +- Backend/cmd/migrate/migrate.go | 3 ++- Backend/cmd/migrate/rollback.go | 3 ++- Backend/config/config.go | 2 -- Backend/config/database.go | 5 +++-- Backend/config/migration.go | 7 ++++--- Backend/config/test_database.go | 5 +++-- Backend/service/delivery/http/v1/game_handler.go | 2 +- Backend/service/delivery/http/v1/heartbeat_handler.go | 3 ++- Backend/service/delivery/http/v1/sse_handler.go | 3 ++- Backend/service/repository/mysql/deck_repository.go | 1 + Backend/service/repository/mysql/game_repository.go | 1 + Backend/service/repository/player_card_repository.go | 3 ++- Backend/service/service/deck_service.go | 3 ++- Backend/service/service/player_service.go | 3 +-- Backend/tests/e2e/hearbeat_api_test.go | 2 +- Backend/tests/e2e/player_api_test.go | 2 +- 17 files changed, 29 insertions(+), 21 deletions(-) diff --git a/Backend/cmd/app/main.go b/Backend/cmd/app/main.go index 9da22ca..715110f 100644 --- a/Backend/cmd/app/main.go +++ b/Backend/cmd/app/main.go @@ -3,7 +3,7 @@ package main import ( _ "github.com/Game-as-a-Service/The-Message/cmd/app/docs" "github.com/Game-as-a-Service/The-Message/config" - http "github.com/Game-as-a-Service/The-Message/service/delivery/http/v1" + "github.com/Game-as-a-Service/The-Message/service/delivery/http/v1" mysqlRepo "github.com/Game-as-a-Service/The-Message/service/repository/mysql" "github.com/Game-as-a-Service/The-Message/service/service" "github.com/gin-gonic/gin" diff --git a/Backend/cmd/migrate/migrate.go b/Backend/cmd/migrate/migrate.go index 0e261f2..a182bef 100644 --- a/Backend/cmd/migrate/migrate.go +++ b/Backend/cmd/migrate/migrate.go @@ -4,8 +4,9 @@ package main import ( "fmt" - "github.com/Game-as-a-Service/The-Message/config" "net/url" + + "github.com/Game-as-a-Service/The-Message/config" ) func main() { diff --git a/Backend/cmd/migrate/rollback.go b/Backend/cmd/migrate/rollback.go index 3347b21..76b187c 100644 --- a/Backend/cmd/migrate/rollback.go +++ b/Backend/cmd/migrate/rollback.go @@ -4,8 +4,9 @@ package main import ( "fmt" - "github.com/Game-as-a-Service/The-Message/config" "net/url" + + "github.com/Game-as-a-Service/The-Message/config" ) func main() { diff --git a/Backend/config/config.go b/Backend/config/config.go index b663f62..d912156 100644 --- a/Backend/config/config.go +++ b/Backend/config/config.go @@ -1,3 +1 @@ package config - - diff --git a/Backend/config/database.go b/Backend/config/database.go index 48ff591..8ec30c9 100644 --- a/Backend/config/database.go +++ b/Backend/config/database.go @@ -2,11 +2,12 @@ package config import ( "fmt" - "gorm.io/driver/mysql" - "gorm.io/gorm" "log" "net/url" "os" + + "gorm.io/driver/mysql" + "gorm.io/gorm" ) func NewDatabase() *gorm.DB { diff --git a/Backend/config/migration.go b/Backend/config/migration.go index 5914a91..c9a0f4f 100644 --- a/Backend/config/migration.go +++ b/Backend/config/migration.go @@ -3,14 +3,15 @@ package config import ( "database/sql" "fmt" + "net/url" + "os" + "strings" + _ "github.com/go-sql-driver/mysql" "github.com/golang-migrate/migrate/v4" "github.com/golang-migrate/migrate/v4/database/mysql" _ "github.com/golang-migrate/migrate/v4/source/file" _ "github.com/joho/godotenv/autoload" - "net/url" - "os" - "strings" ) func NewMigration(dsn string, sourceURL string) (*migrate.Migrate, error) { diff --git a/Backend/config/test_database.go b/Backend/config/test_database.go index 7bc8419..82bc76b 100644 --- a/Backend/config/test_database.go +++ b/Backend/config/test_database.go @@ -2,10 +2,11 @@ package config import ( "fmt" - "github.com/joho/godotenv" - "gorm.io/gorm" "log" "os" + + "github.com/joho/godotenv" + "gorm.io/gorm" ) func InitTestDB() *gorm.DB { diff --git a/Backend/service/delivery/http/v1/game_handler.go b/Backend/service/delivery/http/v1/game_handler.go index b599900..91d9cb2 100644 --- a/Backend/service/delivery/http/v1/game_handler.go +++ b/Backend/service/delivery/http/v1/game_handler.go @@ -2,12 +2,12 @@ package http import ( "encoding/json" - "github.com/Game-as-a-Service/The-Message/enums" "io" "log" "net/http" "strconv" + "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/request" "github.com/Game-as-a-Service/The-Message/service/service" "github.com/gin-gonic/gin" diff --git a/Backend/service/delivery/http/v1/heartbeat_handler.go b/Backend/service/delivery/http/v1/heartbeat_handler.go index faa2d29..a3a07fb 100644 --- a/Backend/service/delivery/http/v1/heartbeat_handler.go +++ b/Backend/service/delivery/http/v1/heartbeat_handler.go @@ -1,8 +1,9 @@ package http import ( - "github.com/gin-gonic/gin" "net/http" + + "github.com/gin-gonic/gin" ) type HeartbeatHandler struct { diff --git a/Backend/service/delivery/http/v1/sse_handler.go b/Backend/service/delivery/http/v1/sse_handler.go index 1c74a6d..e603ca4 100644 --- a/Backend/service/delivery/http/v1/sse_handler.go +++ b/Backend/service/delivery/http/v1/sse_handler.go @@ -2,8 +2,9 @@ package http import ( "encoding/json" - "github.com/gin-gonic/gin" "log" + + "github.com/gin-gonic/gin" ) type Event struct { diff --git a/Backend/service/repository/mysql/deck_repository.go b/Backend/service/repository/mysql/deck_repository.go index 1f6e011..01e880f 100644 --- a/Backend/service/repository/mysql/deck_repository.go +++ b/Backend/service/repository/mysql/deck_repository.go @@ -2,6 +2,7 @@ package mysql import ( "context" + "github.com/Game-as-a-Service/The-Message/service/repository" "gorm.io/gorm" ) diff --git a/Backend/service/repository/mysql/game_repository.go b/Backend/service/repository/mysql/game_repository.go index a8b8fbc..aae3fcc 100644 --- a/Backend/service/repository/mysql/game_repository.go +++ b/Backend/service/repository/mysql/game_repository.go @@ -2,6 +2,7 @@ package mysql import ( "context" + "github.com/Game-as-a-Service/The-Message/service/repository" "gorm.io/gorm" ) diff --git a/Backend/service/repository/player_card_repository.go b/Backend/service/repository/player_card_repository.go index c3aa657..e833bd2 100644 --- a/Backend/service/repository/player_card_repository.go +++ b/Backend/service/repository/player_card_repository.go @@ -2,8 +2,9 @@ package repository import ( "context" - "gorm.io/gorm" "time" + + "gorm.io/gorm" ) type PlayerCard struct { diff --git a/Backend/service/service/deck_service.go b/Backend/service/service/deck_service.go index f5ee452..1768edb 100644 --- a/Backend/service/service/deck_service.go +++ b/Backend/service/service/deck_service.go @@ -2,9 +2,10 @@ package service import ( "context" - "github.com/Game-as-a-Service/The-Message/service/repository" "math/rand" "time" + + "github.com/Game-as-a-Service/The-Message/service/repository" ) type DeckService struct { diff --git a/Backend/service/service/player_service.go b/Backend/service/service/player_service.go index 7ad85da..d8d06e7 100644 --- a/Backend/service/service/player_service.go +++ b/Backend/service/service/player_service.go @@ -2,9 +2,8 @@ package service import ( "context" - "math/rand" - "errors" + "math/rand" "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/repository" diff --git a/Backend/tests/e2e/hearbeat_api_test.go b/Backend/tests/e2e/hearbeat_api_test.go index 764801d..90c660d 100644 --- a/Backend/tests/e2e/hearbeat_api_test.go +++ b/Backend/tests/e2e/hearbeat_api_test.go @@ -1,12 +1,12 @@ package e2e import ( - "github.com/stretchr/testify/assert" "net/http" "net/http/httptest" "testing" "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" ) func TestHeartbeatEndpoint(t *testing.T) { diff --git a/Backend/tests/e2e/player_api_test.go b/Backend/tests/e2e/player_api_test.go index 703a441..b181c54 100644 --- a/Backend/tests/e2e/player_api_test.go +++ b/Backend/tests/e2e/player_api_test.go @@ -13,7 +13,7 @@ import ( "github.com/Game-as-a-Service/The-Message/enums" "github.com/Game-as-a-Service/The-Message/service/request" - "github.com/bxcodec/faker/v3" + "github.com/go-faker/faker/v4" "github.com/stretchr/testify/assert" ) From 37bf05b2d21c4f369de414ef59ab3bb4d8e98402 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Thu, 8 Feb 2024 21:47:32 +0800 Subject: [PATCH 86/89] Docs: Added usage instructions for goimports and golangci-lint in README.md and added the command for add missing and remove unused modules --- README.md | 53 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 711562b..ac41d03 100644 --- a/README.md +++ b/README.md @@ -5,42 +5,42 @@ - 進入 Backend 資料夾 ```bash -cd Backend + cd Backend ``` - docker 建置出 MySQL 環境 ```bash -docker-compose up -d + docker-compose up -d ``` - 進行 DB 資料表rollback ```bash -go run ./cmd/migrate/rollback.go + go run ./cmd/migrate/rollback.go ``` - 進行 DB Migration ```bash -go run ./cmd/migrate/migrate.go + go run ./cmd/migrate/migrate.go ``` - 進行 DB 資料表清空與重新建置 ```bash -go run ./cmd/migrate/refresh.go + go run ./cmd/migrate/refresh.go ``` - 進行 DB Seeder ```bash -go run ./cmd/migrate/game_card_seeder.go + go run ./cmd/migrate/game_card_seeder.go ``` - 自動產生 Swagger API 文件 ```bash -swag init -g ./cmd/app/main.go -output ./cmd/app/docs + swag init -g ./cmd/app/main.go -output ./cmd/app/docs ``` - 開啟 Go Web Server ```bash -go run cmd/app/main.go + go run cmd/app/main.go ``` - 進行第一個 Request 呼叫 @@ -117,3 +117,40 @@ classDiagram + getCanShowCard(heads : MissionCard[]) : MissionCard[] } ``` + +## Usage + +### Development + +#### Add missing and remove unused modules +```bash + go mod tidy +``` + +### Goimports + +#### Install + +```bash + go get golang.org/x/tools/cmd/goimports +``` + +#### Run + +```bash + goimports -l -w . +``` + +### GoLangCI-Lint + +#### Install + +```bash + go get github.com/golangci/golangci-lint/cmd/golangci-lint +``` + +#### Run + +```bash + golangci-lint run ./... +``` From 665abd3d771415a27a94e64849bd39d9cadcfbdc Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Fri, 16 Feb 2024 20:39:02 +0800 Subject: [PATCH 87/89] Chore: Remove deprecated mattes/migrate package --- Backend/go.mod | 1 - Backend/tests/e2e/suite_test.go | 1 - 2 files changed, 2 deletions(-) diff --git a/Backend/go.mod b/Backend/go.mod index c561ef8..128c69e 100644 --- a/Backend/go.mod +++ b/Backend/go.mod @@ -8,7 +8,6 @@ require ( github.com/go-sql-driver/mysql v1.7.1 github.com/golang-migrate/migrate/v4 v4.17.0 github.com/joho/godotenv v1.5.1 - github.com/mattes/migrate v3.0.1+incompatible github.com/stretchr/testify v1.8.4 github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.0 diff --git a/Backend/tests/e2e/suite_test.go b/Backend/tests/e2e/suite_test.go index f66db31..a2f70d9 100644 --- a/Backend/tests/e2e/suite_test.go +++ b/Backend/tests/e2e/suite_test.go @@ -21,7 +21,6 @@ import ( _ "github.com/golang-migrate/migrate/v4/source/file" "github.com/joho/godotenv" _ "github.com/joho/godotenv/autoload" - _ "github.com/mattes/migrate/source/file" "github.com/stretchr/testify/suite" "gorm.io/gorm" ) From bd4d478b8387e57974934df507041880dc9e386e Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Fri, 16 Feb 2024 20:43:04 +0800 Subject: [PATCH 88/89] Chore: Upgrade go version from v1.21 to v1.22 and regularly update project dependencies --- Backend/go.mod | 23 +++++++--------- Backend/go.sum | 71 ++++++++++++++++++-------------------------------- 2 files changed, 36 insertions(+), 58 deletions(-) diff --git a/Backend/go.mod b/Backend/go.mod index 128c69e..2d1ac81 100644 --- a/Backend/go.mod +++ b/Backend/go.mod @@ -1,10 +1,10 @@ module github.com/Game-as-a-Service/The-Message -go 1.21 +go 1.22 require ( github.com/gin-gonic/gin v1.9.1 - github.com/go-faker/faker/v4 v4.2.1 + github.com/go-faker/faker/v4 v4.3.0 github.com/go-sql-driver/mysql v1.7.1 github.com/golang-migrate/migrate/v4 v4.17.0 github.com/joho/godotenv v1.5.1 @@ -18,21 +18,19 @@ require ( require ( github.com/KyleBanks/depth v1.2.1 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/bytedance/sonic v1.10.2 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.6 // indirect - github.com/go-openapi/spec v0.20.4 // indirect - github.com/go-openapi/swag v0.19.15 // indirect + github.com/go-openapi/jsonpointer v0.20.2 // indirect + github.com/go-openapi/jsonreference v0.20.4 // indirect + github.com/go-openapi/spec v0.20.14 // indirect + github.com/go-openapi/swag v0.22.9 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.17.0 // indirect + github.com/go-playground/validator/v10 v10.18.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect @@ -43,7 +41,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/mailru/easyjson v0.7.6 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -51,14 +49,13 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect - go.uber.org/atomic v1.7.0 // indirect + go.uber.org/atomic v1.11.0 // indirect golang.org/x/arch v0.7.0 // indirect golang.org/x/crypto v0.19.0 // indirect golang.org/x/net v0.21.0 // indirect golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.17.0 // indirect + golang.org/x/tools v0.18.0 // indirect google.golang.org/protobuf v1.32.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/Backend/go.sum b/Backend/go.sum index a850037..7e7bd96 100644 --- a/Backend/go.sum +++ b/Backend/go.sum @@ -4,10 +4,6 @@ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE= @@ -19,7 +15,6 @@ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpV github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -41,26 +36,24 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-faker/faker/v4 v4.2.1 h1:w+kvZMvVwgn1tfl7W35tC+mlqtr3CN3tnXdHJxOGGO8= -github.com/go-faker/faker/v4 v4.2.1/go.mod h1:F/bBy8GH9NxOxMInug5Gx4WYeG6fHJZ8Ol/dhcpRub4= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= -github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= -github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-faker/faker/v4 v4.3.0 h1:UXOW7kn/Mwd0u6MR30JjUKVzguT20EB/hBOddAAO+DY= +github.com/go-faker/faker/v4 v4.3.0/go.mod h1:F/bBy8GH9NxOxMInug5Gx4WYeG6fHJZ8Ol/dhcpRub4= +github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= +github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= +github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= +github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= +github.com/go-openapi/spec v0.20.14 h1:7CBlRnw+mtjFGlPDRZmAMnq35cRzI91xj03HVyUi/Do= +github.com/go-openapi/spec v0.20.14/go.mod h1:8EOhTpBoFiask8rrgwbLC3zmJfz4zsCUueRuPM6GNkw= +github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= +github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74= -github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= +github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= @@ -92,21 +85,16 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattes/migrate v3.0.1+incompatible h1:PhAZP82Vqejw8JZLF4U5UkLGzEVaCnbtJpB6DONcDow= -github.com/mattes/migrate v3.0.1+incompatible/go.mod h1:LJcqgpj1jQoxv3m2VXd3drv0suK5CbN/RCX7MXwgnVI= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= @@ -118,8 +106,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= @@ -130,11 +116,12 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -154,6 +141,8 @@ github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= @@ -162,11 +151,10 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= @@ -175,7 +163,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -188,7 +175,6 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= @@ -196,20 +182,15 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= -golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.5.4 h1:igQmHfKcbaTVyAIHNhhB888vvxh8EdQ2uSUT0LPcBso= From f1f4728aa84ec29e34610e195c10161e61338ad4 Mon Sep 17 00:00:00 2001 From: KazeNoYumeX Date: Fri, 16 Feb 2024 20:47:34 +0800 Subject: [PATCH 89/89] Chore: Upgrade the ci go version in github actions from v1.21 to v1.22 --- .github/workflows/test-go-unit.yml | 2 +- Backend/go.sum | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/test-go-unit.yml b/.github/workflows/test-go-unit.yml index 4bcd5d2..5035750 100644 --- a/.github/workflows/test-go-unit.yml +++ b/.github/workflows/test-go-unit.yml @@ -11,7 +11,7 @@ on: - .github/workflows/test-go-unit.yml env: - GO_VERSION: 1.21 + GO_VERSION: 1.22 jobs: build: diff --git a/Backend/go.sum b/Backend/go.sum index 7e7bd96..f6be5e2 100644 --- a/Backend/go.sum +++ b/Backend/go.sum @@ -139,8 +139,6 @@ github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2 github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=