Skip to content

Commit adc548c

Browse files
committed
feat: game report apis
1 parent 4cd9505 commit adc548c

2 files changed

Lines changed: 90 additions & 4 deletions

File tree

src/routes/games.rs

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use crate::{
22
config::Config,
3-
database::entities::players::PlayerRole,
3+
database::entities::{
4+
game_report::{self, GameReportModel},
5+
players::PlayerRole,
6+
},
47
middleware::auth::MaybeAuth,
58
services::game::{snapshot::GameSnapshot, store::Games},
69
utils::types::GameID,
@@ -11,6 +14,7 @@ use axum::{
1114
response::{IntoResponse, Response},
1215
Extension, Json,
1316
};
17+
use sea_orm::{DatabaseConnection, DbErr, EntityTrait, PaginatorTrait, QueryOrder};
1418
use serde::{Deserialize, Serialize};
1519
use std::sync::Arc;
1620
use thiserror::Error;
@@ -21,8 +25,14 @@ pub enum GamesError {
2125
/// The requested game could not be found (For specific game lookup)
2226
#[error("Game not found")]
2327
NotFound,
28+
29+
/// Permission not allowed
2430
#[error("Missing required access")]
2531
NoPermission,
32+
33+
/// Database error occurred
34+
#[error("Internal server error")]
35+
Database(#[from] DbErr),
2636
}
2737

2838
/// The query structure for a players query
@@ -37,18 +47,30 @@ pub struct GamesRequest {
3747
count: Option<u8>,
3848
}
3949

40-
/// Response from the players endpoint which contains a list of
41-
/// players and whether there is more players after
50+
/// Response from the games endpoint which contains a list of
51+
/// games and whether there is more games after
4252
#[derive(Serialize)]
4353
pub struct GamesResponse {
44-
/// The list of players retrieved
54+
/// The list of games retrieved
4555
games: Vec<GameSnapshot>,
4656
/// Whether there is more players left in the database
4757
more: bool,
4858
/// Total number of items available
4959
total_items: usize,
5060
}
5161

62+
#[derive(Serialize)]
63+
pub struct GameReportsResponse {
64+
/// The list of game reports retrieved
65+
games: Vec<GameReportModel>,
66+
/// Whether there is more reports left in the database
67+
more: bool,
68+
/// Total number of pages available
69+
total_pages: u64,
70+
/// Total number of items available
71+
total_items: u64,
72+
}
73+
5274
/// GET /api/games
5375
///
5476
/// Handles requests for a paginated list of games that
@@ -115,12 +137,69 @@ pub async fn get_game(
115137
Ok(Json(snapshot))
116138
}
117139

140+
/// GET /api/game-reports
141+
///
142+
/// Handles requests for a paginated list of games reports of games
143+
/// that have already ended. Query provides the start offset
144+
/// and the number of games to respond with.
145+
pub async fn get_game_reports(
146+
MaybeAuth(auth): MaybeAuth,
147+
Query(GamesRequest { offset, count }): Query<GamesRequest>,
148+
Extension(db): Extension<DatabaseConnection>,
149+
Extension(config): Extension<Arc<Config>>,
150+
) -> Result<Json<GameReportsResponse>, GamesError> {
151+
if let (None, false) = (&auth, config.api.public_games) {
152+
return Err(GamesError::NoPermission);
153+
}
154+
155+
const DEFAULT_COUNT: u8 = 20;
156+
157+
let count = count.unwrap_or(DEFAULT_COUNT);
158+
159+
let paginator = game_report::Entity::find()
160+
.order_by_asc(game_report::Column::CreatedAt)
161+
.paginate(&db, count as u64);
162+
let page = offset as u64;
163+
let totals = paginator.num_items_and_pages().await?;
164+
let more = page < totals.number_of_pages;
165+
let games = paginator.fetch_page(page).await?;
166+
167+
Ok(Json(GameReportsResponse {
168+
games,
169+
more,
170+
total_pages: totals.number_of_pages,
171+
total_items: totals.number_of_items,
172+
}))
173+
}
174+
175+
/// GET /api/game-reports/{id}
176+
///
177+
/// Get a specific game report
178+
pub async fn get_game_report(
179+
MaybeAuth(auth): MaybeAuth,
180+
Path(game_id): Path<GameID>,
181+
Extension(db): Extension<DatabaseConnection>,
182+
Extension(config): Extension<Arc<Config>>,
183+
) -> Result<Json<GameReportModel>, GamesError> {
184+
if let (None, false) = (&auth, config.api.public_games) {
185+
return Err(GamesError::NoPermission);
186+
}
187+
188+
let game = game_report::Entity::find_by_id(game_id)
189+
.one(&db)
190+
.await?
191+
.ok_or(GamesError::NotFound)?;
192+
193+
Ok(Json(game))
194+
}
195+
118196
/// Response implementation for games errors
119197
impl IntoResponse for GamesError {
120198
fn into_response(self) -> Response {
121199
let status_code = match &self {
122200
Self::NotFound => StatusCode::NOT_FOUND,
123201
Self::NoPermission => StatusCode::FORBIDDEN,
202+
Self::Database(_) => StatusCode::INTERNAL_SERVER_ERROR,
124203
};
125204

126205
(status_code, self.to_string()).into_response()

src/routes/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ pub fn router() -> Router {
5151
.route("/", get(games::get_games))
5252
.route("/{id}", get(games::get_game)),
5353
)
54+
// Games Report routing
55+
.nest(
56+
"/game-reports",
57+
Router::new()
58+
.route("/", get(games::get_game_reports))
59+
.route("/{id}", get(games::get_game_report)),
60+
)
5461
// Players routing
5562
.nest(
5663
"/players",

0 commit comments

Comments
 (0)