hypercall_admin/
competition.rs1use axum::{
10 extract::{Path, State},
11 http::StatusCode,
12 response::IntoResponse,
13 Json,
14};
15use chrono::Utc;
16
17use hypercall_competition::competition_to_api;
18use hypercall_db::{CompetitionUpdateInput, CompetitionUpsertInput};
19use hypercall_runtime_api::error::ApiError;
20use hypercall_types::api_models::{
21 CompetitionResponse, CompetitionUpdateRequest, CompetitionUpsertRequest,
22};
23
24use crate::state::AdminState;
25
26#[utoipa::path(
28 post,
29 path = "/monitoring/competitions",
30 request_body = CompetitionUpsertRequest,
31 responses((status = 200, body = CompetitionResponse)),
32 tag = "Monitoring",
33 security(("admin_key" = []))
34)]
35pub async fn create_competition(
36 State(state): State<AdminState>,
37 Json(request): Json<CompetitionUpsertRequest>,
38) -> Result<Json<CompetitionResponse>, ApiError> {
39 let now_ts_ms = Utc::now().timestamp_millis();
40 let row = state
41 .competition
42 .create_competition(CompetitionUpsertInput {
43 name: request.name,
44 description: request.description,
45 rules_url: request.rules_url,
46 rules_content: request.rules_content,
47 win_conditions: request.win_conditions,
48 primary_win_condition: request.primary_win_condition,
49 start_ts_ms: request.start_ts_ms,
50 end_ts_ms: request.end_ts_ms,
51 })
52 .await
53 .map_err(ApiError::from)?;
54
55 Ok(Json(CompetitionResponse {
56 success: true,
57 data: competition_to_api(row, now_ts_ms),
58 }))
59}
60
61#[utoipa::path(
63 put,
64 path = "/monitoring/competitions/{id}",
65 params(("id" = i64, Path, description = "Competition id")),
66 request_body = CompetitionUpdateRequest,
67 responses((status = 200, body = CompetitionResponse)),
68 tag = "Monitoring",
69 security(("admin_key" = []))
70)]
71pub async fn update_competition(
72 State(state): State<AdminState>,
73 Path(id): Path<i64>,
74 Json(request): Json<CompetitionUpdateRequest>,
75) -> Result<Json<CompetitionResponse>, ApiError> {
76 let now_ts_ms = Utc::now().timestamp_millis();
77 let row = state
78 .competition
79 .update_competition(
80 id,
81 CompetitionUpdateInput {
82 name: request.name,
83 description: request.description,
84 rules_url: request.rules_url,
85 rules_content: request.rules_content,
86 win_conditions: request.win_conditions,
87 primary_win_condition: request.primary_win_condition,
88 start_ts_ms: request.start_ts_ms,
89 end_ts_ms: request.end_ts_ms,
90 },
91 now_ts_ms,
92 )
93 .await
94 .map_err(ApiError::from)?;
95
96 Ok(Json(CompetitionResponse {
97 success: true,
98 data: competition_to_api(row, now_ts_ms),
99 }))
100}
101
102#[utoipa::path(
104 delete,
105 path = "/monitoring/competitions/{id}",
106 params(("id" = i64, Path, description = "Competition id")),
107 responses((status = 204, description = "Competition deleted")),
108 tag = "Monitoring",
109 security(("admin_key" = []))
110)]
111pub async fn delete_competition(
112 State(state): State<AdminState>,
113 Path(id): Path<i64>,
114) -> Result<impl IntoResponse, ApiError> {
115 let now_ts_ms = Utc::now().timestamp_millis();
116 state
117 .competition
118 .delete_competition(id, now_ts_ms)
119 .await
120 .map_err(ApiError::from)?;
121 Ok(StatusCode::NO_CONTENT)
122}