Skip to content

Commit

Permalink
Merge pull request #76 from traP-jp/feat/#71-get-submissions-submissi…
Browse files Browse the repository at this point in the history
…onid

Feat #71 GET /submissions/{submissionId} を実装する
  • Loading branch information
kavos113 authored Jan 10, 2025
2 parents 90ac17a + 82216f3 commit 02e44e8
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use axum::{
use super::Repository;

mod authentication;
mod submissions;
mod users;

pub fn make_router(app_state: Repository) -> Router {
Expand All @@ -26,8 +27,12 @@ pub fn make_router(app_state: Repository) -> Router {
.route("/me/password", put(users::put_me_password))
.route("/:userId", get(users::get_user));

let submissions_router =
Router::new().route("/:submissionId", get(submissions::get_submission));

Router::new()
.nest("/", authentication_router)
.nest("/users", users_router)
.nest("/submissions", submissions_router)
.with_state(app_state)
}
103 changes: 103 additions & 0 deletions src/handler/submissions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use axum::{
extract::{Path, State},
response::IntoResponse,
Json,
};
use axum_extra::{headers::Cookie, TypedHeader};
use reqwest::StatusCode;
use serde::Serialize;
use sqlx::types::chrono;

use super::Repository;

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct SubmissionResponse {
id: String,
user_id: i32,
user_name: String,
problem_id: i32,
submitted_at: chrono::DateTime<chrono::Utc>,
language_id: i32,
total_score: i64,
max_time: i32,
max_memory: i32,
code_length: i32,
overall_judge_status: String,
judge_results: Vec<TestcaseResponse>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct TestcaseResponse {
testcase_id: i32,
testcase_name: String,
judge_status: String,
score: i64,
time: i32,
memory: i32,
}

pub async fn get_submission(
State(state): State<Repository>,
TypedHeader(cookie): TypedHeader<Cookie>,
Path(path): Path<i64>,
) -> anyhow::Result<impl IntoResponse, StatusCode> {
let submission = state
.get_submission_by_id(path)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
.ok_or(StatusCode::NOT_FOUND)?;

let problem = state
.get_normal_problem_by_id(submission.problem_id)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
.ok_or(StatusCode::NOT_FOUND)?;

if !problem.is_public {
let session_id = cookie.get("session_id").ok_or(StatusCode::NOT_FOUND)?;

let display_id = state
.get_display_id_by_session_id(session_id)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
.ok_or(StatusCode::NOT_FOUND)?;

if display_id != problem.author_id {
return Err(StatusCode::NOT_FOUND);
}
}

let testcases = state
.get_testcases_by_submission_id(submission.id)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

let response = SubmissionResponse {
id: submission.id.to_string(),
user_id: submission.user_id,
user_name: submission.user_name,
problem_id: submission.problem_id,
submitted_at: submission.submitted_at,
language_id: submission.language_id,
total_score: submission.total_score,
max_time: submission.max_time,
max_memory: submission.max_memory,
code_length: submission.source.len() as i32,
overall_judge_status: submission.judge_status,
judge_results: testcases
.into_iter()
.map(|testcase| TestcaseResponse {
testcase_id: testcase.testcase_id,
testcase_name: testcase.testcase_name,
judge_status: testcase.judge_status,
score: testcase.score,
time: testcase.time,
memory: testcase.memory,
})
.collect(),
};

Ok(Json(response))
}
2 changes: 2 additions & 0 deletions src/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use sqlx::mysql::{MySqlConnectOptions, MySqlPoolOptions};
use super::Repository;

mod jwt;
mod normal_problems;
mod submissions;
mod user_password;
pub mod users;
mod users_session;
Expand Down
32 changes: 32 additions & 0 deletions src/repository/normal_problems.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use super::Repository;
use sqlx::types::chrono;

#[derive(sqlx::FromRow)]
pub struct NormalProblems {
pub id: i32,
pub author_id: i64,
pub title: String,
pub statement: String,
pub time_limit: i32,
pub memory_limit: i32,
pub difficulty: i32,
pub is_public: bool,
pub judgecode_path: String,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
}

impl Repository {
pub async fn get_normal_problem_by_id(
&self,
id: i32,
) -> anyhow::Result<Option<NormalProblems>> {
let problem =
sqlx::query_as::<_, NormalProblems>("SELECT * FROM normal_problems WHERE id = ?")
.bind(id)
.fetch_optional(&self.pool)
.await?;

Ok(problem)
}
}
53 changes: 53 additions & 0 deletions src/repository/submissions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use sqlx::{types::chrono, FromRow};

use super::Repository;

#[derive(FromRow)]
pub struct Submission {
pub id: i32,
pub problem_id: i32,
pub user_id: i32,
pub user_name: String,
pub language_id: i32,
pub source: String,
pub judge_status: String,
pub total_score: i64,
pub max_time: i32,
pub max_memory: i32,
pub submitted_at: chrono::DateTime<chrono::Utc>,
}

#[derive(FromRow)]
pub struct Testcase {
pub submission_id: i32,
pub testcase_id: i32,
pub testcase_name: String,
pub judge_status: String,
pub score: i64,
pub time: i32,
pub memory: i32,
}

impl Repository {
pub async fn get_submission_by_id(&self, id: i64) -> anyhow::Result<Option<Submission>> {
let submission = sqlx::query_as::<_, Submission>("SELECT * FROM submissions WHERE id = ?")
.bind(id)
.fetch_optional(&self.pool)
.await?;

Ok(submission)
}

pub async fn get_testcases_by_submission_id(
&self,
submission_id: i32,
) -> anyhow::Result<Vec<Testcase>> {
let testcases =
sqlx::query_as::<_, Testcase>("SELECT * FROM testcases WHERE submission_id = ?")
.bind(submission_id)
.fetch_all(&self.pool)
.await?;

Ok(testcases)
}
}

0 comments on commit 02e44e8

Please sign in to comment.