diff --git a/app/start.go b/app/start.go index 63eeef3..7283283 100644 --- a/app/start.go +++ b/app/start.go @@ -40,6 +40,7 @@ func Start(conf common.Conf, dbO *sql.DB) *gin.Engine { gv1.GET("/users/scores/best", Method(v1.UserScoresBestGET, common.PrivilegeRead)) gv1.GET("/users/scores/recent", Method(v1.UserScoresRecentGET, common.PrivilegeRead)) gv1.GET("/badges", Method(v1.BadgesGET, common.PrivilegeRead)) + gv1.GET("/beatmaps", Method(v1.BeatmapGET, common.PrivilegeRead)) // ReadConfidential privilege required gv1.GET("/friends", Method(v1.FriendsGET, common.PrivilegeReadConfidential)) diff --git a/app/v1/beatmap.go b/app/v1/beatmap.go index eb444c8..704b64a 100644 --- a/app/v1/beatmap.go +++ b/app/v1/beatmap.go @@ -1,20 +1,26 @@ package v1 -import "git.zxq.co/ripple/rippleapi/common" +import ( + "database/sql" + "strconv" + "time" + + "git.zxq.co/ripple/rippleapi/common" +) type beatmap struct { - BeatmapID int `json:"beatmap_id"` - BeatmapsetID int `json:"beatmapset_id"` - BeatmapMD5 string `json:"beatmap_md5"` - SongName string `json:"song_name"` - AR float32 `json:"ar"` - OD float32 `json:"od"` - Difficulty float64 `json:"difficulty"` - MaxCombo int `json:"max_combo"` - HitLength int `json:"hit_length"` - Ranked int `json:"ranked"` - RankedStatusFrozen int `json:"ranked_status_frozen"` - LatestUpdate int `json:"latest_update"` + BeatmapID int `json:"beatmap_id"` + BeatmapsetID int `json:"beatmapset_id"` + BeatmapMD5 string `json:"beatmap_md5"` + SongName string `json:"song_name"` + AR float32 `json:"ar"` + OD float32 `json:"od"` + Difficulty float64 `json:"difficulty"` + MaxCombo int `json:"max_combo"` + HitLength int `json:"hit_length"` + Ranked int `json:"ranked"` + RankedStatusFrozen int `json:"ranked_status_frozen"` + LatestUpdate time.Time `json:"latest_update"` } type beatmapMayOrMayNotExist struct { @@ -29,11 +35,11 @@ type beatmapMayOrMayNotExist struct { HitLength *int Ranked *int RankedStatusFrozen *int - LatestUpdate *int + LatestUpdate *time.Time } func (b *beatmapMayOrMayNotExist) toBeatmap() *beatmap { - if b.BeatmapID == nil { + if b == nil || b.BeatmapID == nil { return nil } return &beatmap{ @@ -62,7 +68,7 @@ type beatmapSetResponse struct { } type beatmapSetStatusData struct { - BeatmapSetID int `json:"beatmapset_id"` + BeatmapsetID int `json:"beatmapset_id"` BeatmapID int `json:"beatmap_id"` RankedStatus int `json:"ranked_status"` Frozen int `json:"frozen"` @@ -75,7 +81,7 @@ func BeatmapSetStatusPOST(md common.MethodData) common.CodeMessager { md.RequestData.Unmarshal(&req) var miss []string - if req.BeatmapSetID == 0 && req.BeatmapID == 0 { + if req.BeatmapsetID == 0 && req.BeatmapID == 0 { miss = append(miss, "beatmapset_id or beatmap_id") } if len(miss) != 0 { @@ -91,7 +97,7 @@ func BeatmapSetStatusPOST(md common.MethodData) common.CodeMessager { var ( whereClause = "beatmapset_id" - param = req.BeatmapSetID + param = req.BeatmapsetID ) if req.BeatmapID != 0 { whereClause = "beatmap_id" @@ -102,8 +108,87 @@ func BeatmapSetStatusPOST(md common.MethodData) common.CodeMessager { SET ranked = ?, ranked_status_freezed = ? WHERE `+whereClause+` = ?`, req.RankedStatus, req.Frozen, param) - // TODO: replace with beatmapSetResponse when implemented - return common.ResponseBase{ - Code: 200, + if whereClause == "beatmapset_id" { + return getSet(md, param) } + return getBeatmap(md, param) +} + +// BeatmapGET retrieves a beatmap. +func BeatmapGET(md common.MethodData) common.CodeMessager { + if md.C.Query("s") == "" && md.C.Query("b") == "" { + return common.SimpleResponse(400, "Must pass either querystring param 'b' or 's'") + } + setID, _ := strconv.Atoi(md.C.Query("s")) + if setID != 0 { + return getSet(md, setID) + } + beatmapID, _ := strconv.Atoi(md.C.Query("b")) + if beatmapID != 0 { + return getBeatmap(md, beatmapID) + } + return common.SimpleResponse(400, "Please pass either a valid beatmapset ID or a valid beatmap ID") +} + +const baseBeatmapSelect = ` +SELECT + beatmap_id, beatmapset_id, beatmap_md5, + song_name, ar, od, difficulty, max_combo, + hit_length, ranked, ranked_status_freezed, + latest_update +FROM beatmaps +` + +func getSet(md common.MethodData, setID int) common.CodeMessager { + rows, err := md.DB.Query(baseBeatmapSelect+"WHERE beatmapset_id = ?", setID) + if err != nil { + md.Err(err) + return Err500 + } + var r beatmapSetResponse + for rows.Next() { + var ( + b beatmap + rawLatestUpdate int64 + ) + err = rows.Scan( + &b.BeatmapID, &b.BeatmapsetID, &b.BeatmapMD5, + &b.SongName, &b.AR, &b.OD, &b.Difficulty, &b.MaxCombo, + &b.HitLength, &b.Ranked, &b.RankedStatusFrozen, + &rawLatestUpdate, + ) + if err != nil { + md.Err(err) + continue + } + b.LatestUpdate = time.Unix(rawLatestUpdate, 0) + r.Beatmaps = append(r.Beatmaps, b) + } + r.Code = 200 + return r +} + +func getBeatmap(md common.MethodData, beatmapID int) common.CodeMessager { + var ( + b beatmap + rawLatestUpdate int64 + ) + err := md.DB.QueryRow(baseBeatmapSelect+"WHERE beatmap_id = ? LIMIT 1", beatmapID).Scan( + &b.BeatmapID, &b.BeatmapsetID, &b.BeatmapMD5, + &b.SongName, &b.AR, &b.OD, &b.Difficulty, &b.MaxCombo, + &b.HitLength, &b.Ranked, &b.RankedStatusFrozen, + &rawLatestUpdate, + ) + switch { + case err == sql.ErrNoRows: + return common.SimpleResponse(404, "That beatmap could not be found!") + case err != nil: + md.Err(err) + return Err500 + } + b.LatestUpdate = time.Unix(rawLatestUpdate, 0) + var r beatmapResponse + r.Code = 200 + r.beatmap = b + return r } diff --git a/app/v1/user_scores.go b/app/v1/user_scores.go index 31540e2..0843eee 100644 --- a/app/v1/user_scores.go +++ b/app/v1/user_scores.go @@ -128,9 +128,10 @@ func scoresPuts(md common.MethodData, whereClause string, params ...interface{}) var scores []userScore for rows.Next() { var ( - us userScore - t string - b beatmapMayOrMayNotExist + us userScore + t string + b beatmapMayOrMayNotExist + rawLatestUpdate *int64 ) err = rows.Scan( &us.ID, &us.BeatmapMD5, &us.Score, @@ -143,7 +144,7 @@ func scoresPuts(md common.MethodData, whereClause string, params ...interface{}) &b.BeatmapID, &b.BeatmapsetID, &b.BeatmapMD5, &b.SongName, &b.AR, &b.OD, &b.Difficulty, &b.MaxCombo, &b.HitLength, &b.Ranked, - &b.RankedStatusFrozen, &b.LatestUpdate, + &b.RankedStatusFrozen, &rawLatestUpdate, ) if err != nil { md.Err(err) @@ -155,6 +156,11 @@ func scoresPuts(md common.MethodData, whereClause string, params ...interface{}) md.Err(err) return Err500 } + if rawLatestUpdate != nil { + // fml i should have used an inner join + xd := time.Unix(*rawLatestUpdate, 0) + b.LatestUpdate = &xd + } us.Beatmap = b.toBeatmap() scores = append(scores, us) }