Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat #71 GET /submissions/{submissionId} を実装する #76

Merged
merged 4 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)]
PonponJuice marked this conversation as resolved.
Show resolved Hide resolved
#[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)]
PonponJuice marked this conversation as resolved.
Show resolved Hide resolved
#[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)
}
}
Loading