Commit 97b49cf1 authored by Julien Schröter's avatar Julien Schröter Committed by Frederik Wegner

Resolve "Send details about why duel could not be created"

parent c4936092
Pipeline #2260 failed with stages
in 4 minutes and 12 seconds
...@@ -543,25 +543,42 @@ paths: ...@@ -543,25 +543,42 @@ paths:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/duel' $ref: '#/components/schemas/duel'
'200': '409':
description: Query was correct but no new duel could not be created. Often due to no intersection in pool selections of players. description: Did not create duel since the current user and the challenged user don't have common question pools.
content: content:
text/plain: application/json:
schema: schema:
type: string type: object
description: Details on why no duel was created. properties:
pools:
type: object
required:
- self
- opponent
properties:
self:
type: array
items:
type: integer
format: uint32
description: ID's of the requesting user's pools.
opponent:
type: array
items:
type: integer
format: uint32
description: ID's of the given user's pools.
'401': '401':
description: Authentication failure. description: Authentication failure.
content: content:
text/plain: text/plain:
schema: schema:
type: string type: string
'400': '403':
description: Query was invalid. description: There already is a running duel with the current and the challenged user.
'404': '404':
description: User with `id` does not exist. description: User with `id` does not exist.
'500':
description: The server cannot deliver due to an internal error.
'/duel/pool': '/duel/pool':
get: get:
summary: Get pools to choose from. summary: Get pools to choose from.
......
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"firebase.google.com/go/messaging" "firebase.google.com/go/messaging"
"gitlab.akamu.de/akamu/game-server-go/endpoint/pool"
"gitlab.akamu.de/akamu/game-server-go/endpoint/refreshjwt" "gitlab.akamu.de/akamu/game-server-go/endpoint/refreshjwt"
"github.com/getsentry/raven-go" "github.com/getsentry/raven-go"
...@@ -22,7 +23,7 @@ import ( ...@@ -22,7 +23,7 @@ import (
var errUserForbidsNotifications = errors.New("user does not want to receive notifications") var errUserForbidsNotifications = errors.New("user does not want to receive notifications")
func SetupDuelRoutes(group *gin.RouterGroup, repository DuelQuery, titleRepository title.TitleQuery, sessionRepository refreshjwt.RefreshJWTQuery) { func SetupDuelRoutes(group *gin.RouterGroup, repository DuelQuery, titleRepository title.TitleQuery, sessionRepository refreshjwt.RefreshJWTQuery, poolRepository pool.PoolQuery) {
group.OPTIONS("", func(ctx *gin.Context) { group.OPTIONS("", func(ctx *gin.Context) {
ctx.Header("Allow", "GET,POST,PATCH,PUT,DELETE") ctx.Header("Allow", "GET,POST,PATCH,PUT,DELETE")
ctx.Status(200) ctx.Status(200)
...@@ -32,7 +33,7 @@ func SetupDuelRoutes(group *gin.RouterGroup, repository DuelQuery, titleReposito ...@@ -32,7 +33,7 @@ func SetupDuelRoutes(group *gin.RouterGroup, repository DuelQuery, titleReposito
group.GET("", getDuel(repository)) group.GET("", getDuel(repository))
// Endpoint to create new duel // Endpoint to create new duel
group.POST("", postDuel(repository, sessionRepository)) group.POST("", postDuel(repository, sessionRepository, poolRepository))
// Endpoint to fetch all playable pools // Endpoint to fetch all playable pools
group.GET("/pool", getPools(repository)) group.GET("/pool", getPools(repository))
...@@ -96,7 +97,7 @@ func getDuel(repository DuelQuery) gin.HandlerFunc { ...@@ -96,7 +97,7 @@ func getDuel(repository DuelQuery) gin.HandlerFunc {
} }
} }
func postDuel(repository DuelQuery, sessionRepository refreshjwt.RefreshJWTQuery) gin.HandlerFunc { func postDuel(repository DuelQuery, sessionRepository refreshjwt.RefreshJWTQuery, poolRepository pool.PoolQuery) gin.HandlerFunc {
return func(ctx *gin.Context) { return func(ctx *gin.Context) {
// Fetch challenged user // Fetch challenged user
var challenged struct { var challenged struct {
...@@ -123,7 +124,21 @@ func postDuel(repository DuelQuery, sessionRepository refreshjwt.RefreshJWTQuery ...@@ -123,7 +124,21 @@ func postDuel(repository DuelQuery, sessionRepository refreshjwt.RefreshJWTQuery
case ErrUserNotFound: case ErrUserNotFound:
ctx.AbortWithError(http.StatusNotFound, err) ctx.AbortWithError(http.StatusNotFound, err)
case ErrNoCommonPool: case ErrNoCommonPool:
ctx.AbortWithError(http.StatusConflict, err) // Get pools of user and challenged user for the app
poolsChallenger, errPoolsChallenger := poolRepository.SelectUserPools(challenger)
poolsChallenged, errPoolsChallanged := poolRepository.SelectUserPools(challenged.ID)
if errPoolsChallenger != nil {
raven.CaptureError(errPoolsChallenger, nil)
ctx.AbortWithError(http.StatusConflict, errPoolsChallenger)
return
} else if errPoolsChallanged != nil {
raven.CaptureError(errPoolsChallanged, nil)
ctx.AbortWithError(http.StatusConflict, errPoolsChallanged)
return
}
ctx.AbortWithStatusJSON(http.StatusConflict, gin.H{"pools": gin.H{"self": poolsChallenger, "opponent": poolsChallenged}})
default: default:
raven.CaptureError(err, nil) raven.CaptureError(err, nil)
ctx.AbortWithError(http.StatusInternalServerError, err) ctx.AbortWithError(http.StatusInternalServerError, err)
......
...@@ -238,6 +238,8 @@ var ErrQuestionNotFound = errors.New("question not found") ...@@ -238,6 +238,8 @@ var ErrQuestionNotFound = errors.New("question not found")
var ErrBadRequest = errors.New("The request was either formatted incorrectly or it was not allowed at the currect state of the database.") var ErrBadRequest = errors.New("The request was either formatted incorrectly or it was not allowed at the currect state of the database.")
const TotalRounds = 2 const TotalRounds = 2
// Must match the value in endpoints/pool/query.go
const QuestionsPerRound = 2 const QuestionsPerRound = 2
const queryDuelsByID = "SELECT `duel`.`idduel`, `duel`.`round_current`, `duel`.`round_max`, `duel`.`started_on`, `duel`.`modified_on`, `duel`.`finished_on`, `duel`.`running`, `duel`.`started`, `duel`.`next`, `duel_player_score`.`score`, `participant`.`iduser`, `participant`.`username`, idavatar, avatar.image, avatar.level, title.idtitle, title.name, subject.idsubject, subject.name, subject.shortform, subject.department, subject.description, title.unlock_score, title.unlock_win, `round`.`idround`, `round`.`number`, `round`.`started`, `pool`.`idpool`, `pool`.`name`, `pool`.`shortform`, `pool`.`image`" + const queryDuelsByID = "SELECT `duel`.`idduel`, `duel`.`round_current`, `duel`.`round_max`, `duel`.`started_on`, `duel`.`modified_on`, `duel`.`finished_on`, `duel`.`running`, `duel`.`started`, `duel`.`next`, `duel_player_score`.`score`, `participant`.`iduser`, `participant`.`username`, idavatar, avatar.image, avatar.level, title.idtitle, title.name, subject.idsubject, subject.name, subject.shortform, subject.department, subject.description, title.unlock_score, title.unlock_win, `round`.`idround`, `round`.`number`, `round`.`started`, `pool`.`idpool`, `pool`.`name`, `pool`.`shortform`, `pool`.`image`" +
......
...@@ -6,14 +6,17 @@ import ( ...@@ -6,14 +6,17 @@ import (
"fmt" "fmt"
"gitlab.akamu.de/akamu/game-server-go/dbhandler" "gitlab.akamu.de/akamu/game-server-go/dbhandler"
"gitlab.akamu.de/akamu/game-server-go/endpoint/duel"
"gitlab.akamu.de/akamu/game-server-go/schemas" "gitlab.akamu.de/akamu/game-server-go/schemas"
) )
var ErrorInvalidPools = errors.New("The selection of pools was invalid.") var ErrorInvalidPools = errors.New("The selection of pools was invalid.")
// Must match the value in endpoint/duel/query.go
const QuestionsPerRound = 2
type PoolQuery interface { type PoolQuery interface {
Select(userID uint32) ([]schemas.PoolSchema, error) Select(userID uint32) ([]schemas.PoolSchema, error)
SelectUserPools(userID uint32) ([]uint32, error)
PatchUserPools(userID uint32, selection []uint32) error PatchUserPools(userID uint32, selection []uint32) error
} }
...@@ -36,7 +39,7 @@ func (m MySQLPoolQuery) Select(userID uint32) ([]schemas.PoolSchema, error) { ...@@ -36,7 +39,7 @@ func (m MySQLPoolQuery) Select(userID uint32) ([]schemas.PoolSchema, error) {
} }
// Fetch pools from database // Fetch pools from database
rows, errRows := db.Query(queryString, userID, duel.QuestionsPerRound) rows, errRows := db.Query(queryString, userID, QuestionsPerRound)
// Check whether query failed // Check whether query failed
if errRows != nil { if errRows != nil {
...@@ -76,6 +79,38 @@ func (m MySQLPoolQuery) Select(userID uint32) ([]schemas.PoolSchema, error) { ...@@ -76,6 +79,38 @@ func (m MySQLPoolQuery) Select(userID uint32) ([]schemas.PoolSchema, error) {
return pools, nil return pools, nil
} }
// Select returns a list of all available playable pools.
func (m MySQLPoolQuery) SelectUserPools(userID uint32) ([]uint32, error) {
db, err := dbhandler.GetDBConnection()
if err != nil {
return nil, fmt.Errorf("failed to get database instance: %v", err)
}
// Fetch pools from database
rows, err := db.Query("SELECT DISTINCT `pool` FROM `user_pool` WHERE `user`=? AND `pool` IN (SELECT `pool` FROM `pool_question` GROUP BY `pool` HAVING COUNT(DISTINCT `question`) >= ?)", userID, QuestionsPerRound)
// Check whether query failed
if err != nil {
return nil, fmt.Errorf("failed fetching pools from database: %v", err)
}
defer rows.Close()
// Generate output
var pools []uint32
for rows.Next() {
var pool uint32
if err := rows.Scan(&pool); err != nil {
return nil, fmt.Errorf("failed aggregating pools from database: %v", err)
}
pools = append(pools, pool)
}
return pools, nil
}
// PatchUserPools insert relations into user_pool for all pools in the array `selection`. // PatchUserPools insert relations into user_pool for all pools in the array `selection`.
// Relations to pools that are not in `selection` are deleted. // Relations to pools that are not in `selection` are deleted.
func (m MySQLPoolQuery) PatchUserPools(userID uint32, selection []uint32) error { func (m MySQLPoolQuery) PatchUserPools(userID uint32, selection []uint32) error {
......
...@@ -313,7 +313,7 @@ func SetupRoutes(courseRepository course.CourseQuery, flashcardRepository flashc ...@@ -313,7 +313,7 @@ func SetupRoutes(courseRepository course.CourseQuery, flashcardRepository flashc
pool.SetupPoolRoutes(authGroup.Group("/pool"), poolRepository) pool.SetupPoolRoutes(authGroup.Group("/pool"), poolRepository)
friend.SetupFriendRoutes(authGroup.Group("/friend"), friendRepository) friend.SetupFriendRoutes(authGroup.Group("/friend"), friendRepository)
avatar.SetupAvatarRoutes(authGroup.Group("/avatar"), avatarRepository) avatar.SetupAvatarRoutes(authGroup.Group("/avatar"), avatarRepository)
duel.SetupDuelRoutes(authGroup.Group("/duel"), duelRepository, titleRepository, refreshjwtRepository) duel.SetupDuelRoutes(authGroup.Group("/duel"), duelRepository, titleRepository, refreshjwtRepository, poolRepository)
resource.SetupResourceRoutes(authGroup.Group("/resource"), resourceRepository, resourceServerConfig) resource.SetupResourceRoutes(authGroup.Group("/resource"), resourceRepository, resourceServerConfig)
refreshjwt.SetupRefreshJWTRoutes(authGroup.Group("/session"), refreshjwtRepository) refreshjwt.SetupRefreshJWTRoutes(authGroup.Group("/session"), refreshjwtRepository)
......
...@@ -21,6 +21,10 @@ func (p *mockPool) Select(userID uint32) ([]schemas.PoolSchema, error) { ...@@ -21,6 +21,10 @@ func (p *mockPool) Select(userID uint32) ([]schemas.PoolSchema, error) {
return pools, nil return pools, nil
} }
func (*mockPool) SelectUserPools(userId uint32) ([]uint32, error) {
return nil, nil
}
// just to match interface doesnt do anything actually // just to match interface doesnt do anything actually
func (p *mockPool) PatchUserPools(userID uint32, selection []uint32) error { func (p *mockPool) PatchUserPools(userID uint32, selection []uint32) error {
return nil return nil
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment