Skip to content

Commit

Permalink
feat(problem): support delete problem and assets (#43)
Browse files Browse the repository at this point in the history
* feat(problem): support delete problem and assets

* chore: fix test db port

* chore: remove serde_json
  • Loading branch information
fu050409 authored Dec 3, 2024
1 parent 9acee94 commit 6abc5bc
Show file tree
Hide file tree
Showing 15 changed files with 264 additions and 24 deletions.
5 changes: 5 additions & 0 deletions .changes/problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"algohub-server": patch:feat
---

Add endpoint to delete a problem by specified problem ID.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/cors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub struct CORS;
impl Fairing for CORS {
fn info(&self) -> Info {
Info {
name: "Add CORS headers to responses",
name: "CORS Policy",
kind: Kind::Response,
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
use algohub_server::routes::index::init_db;
use rocket::launch;

#[launch]
async fn rocket() -> _ {
algohub_server::rocket().await
algohub_server::rocket(
init_db("localhost:5177")
.await
.expect("Failed to initialize database, shutting down..."),
)
.await
}
6 changes: 6 additions & 0 deletions src/models/problem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,9 @@ impl From<Problem> for UserProblem {
}
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerTestCase {
pub input: String,
pub output: String,
}
10 changes: 8 additions & 2 deletions src/routes/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use super::submission;
use crate::{cors::CORS, routes::account};
use anyhow::Result;
use rocket::fs::NamedFile;
use surrealdb::engine::remote::ws::Client;
use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, Surreal};

#[get("/")]
Expand All @@ -21,10 +22,11 @@ async fn files(file: PathBuf) -> Option<NamedFile> {
NamedFile::open(Path::new("dist/").join(file)).await.ok()
}

pub async fn rocket() -> rocket::Rocket<rocket::Build> {
let db = Surreal::new::<Ws>("127.0.0.1:5176")
pub async fn init_db(db_addr: &str) -> Result<Surreal<Client>> {
let db = Surreal::new::<Ws>(db_addr)
.await
.expect("Failed to connect to database");

db.use_ns("main")
.use_db("acm")
.await
Expand All @@ -36,6 +38,10 @@ pub async fn rocket() -> rocket::Rocket<rocket::Build> {
.await
.expect("Failed to authenticate");

Ok(db)
}

pub async fn rocket(db: Surreal<Client>) -> rocket::Rocket<rocket::Build> {
rocket::build()
.attach(CORS)
.mount("/", routes![index, files])
Expand Down
33 changes: 30 additions & 3 deletions src/routes/problem.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rocket::{serde::json::Json, State};
use rocket::{serde::json::Json, tokio::fs::remove_file, State};
use serde::{Deserialize, Serialize};
use surrealdb::{engine::remote::ws::Client, Surreal};

Expand All @@ -7,7 +7,7 @@ use crate::{
account::Account,
error::Error,
problem::{CreateProblem, Problem, ProblemVisibility, UserProblem},
response::Response,
response::{Empty, Response},
Credentials, OwnedCredentials, OwnedId,
},
utils::{account, problem, session},
Expand Down Expand Up @@ -148,7 +148,34 @@ pub async fn list(
}))
}

#[delete("/delete/<id>", data = "<auth>")]
pub async fn delete(
db: &State<Surreal<Client>>,
id: &str,
auth: Json<Credentials<'_>>,
) -> Result<Empty> {
if !session::verify(db, auth.id, auth.token).await {
return Err(Error::Unauthorized(Json("Invalid credentials".into())));
}

for test_case in problem::get_test_cases_by_id(db, id).await? {
remove_file(test_case.input).await?;
remove_file(test_case.output).await?;
}
println!("Down");

problem::delete(db, id).await?.ok_or(Error::NotFound(Json(
"Problem with specified id not found".into(),
)))?;

Ok(Json(Response {
success: true,
message: "Problem deleted successfully".to_string(),
data: None,
}))
}

pub fn routes() -> Vec<rocket::Route> {
use rocket::routes;
routes![create, get, list]
routes![create, get, list, delete]
}
33 changes: 30 additions & 3 deletions src/utils/problem.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use anyhow::Result;
use serde::Deserialize;
use surrealdb::{engine::remote::ws::Client, Surreal};
use surrealdb::{engine::remote::ws::Client, sql::Thing, Surreal};

use crate::models::problem::{CreateProblem, Problem};
use crate::models::problem::{CreateProblem, Problem, ServerTestCase};

pub async fn create(db: &Surreal<Client>, problem: CreateProblem<'_>) -> Result<Option<Problem>> {
Ok(db
Expand All @@ -21,8 +21,20 @@ pub async fn update(db: &Surreal<Client>, problem: Problem) -> Result<Option<Pro
.await?)
}

const DELETE_ALL_ASSETS_QUERY: &str = r#"
IF $problem.exists() {
FOR $asset IN (SELECT VALUE test_cases FROM $problem) {
DELETE $asset.input, $asset.output;
};
(DELETE ONLY $problem RETURN BEFORE)
}
"#;
pub async fn delete(db: &Surreal<Client>, id: &str) -> Result<Option<Problem>> {
Ok(db.delete(("problem", id)).await?)
Ok(db
.query(DELETE_ALL_ASSETS_QUERY)
.bind(("problem", Thing::from(("problem", id))))
.await?
.take(0)?)
}

pub async fn get<M>(db: &Surreal<Client>, id: &str) -> Result<Option<M>>
Expand Down Expand Up @@ -68,3 +80,18 @@ where

Ok(response.take(0)?)
}

const SELECT_TEST_CASES_QUERY: &str = r#"
IF $problem.exists() THEN
SELECT input.path AS input, output.path AS output FROM $problem.test_cases
ELSE
[]
END;
"#;
pub async fn get_test_cases_by_id(db: &Surreal<Client>, id: &str) -> Result<Vec<ServerTestCase>> {
Ok(db
.query(SELECT_TEST_CASES_QUERY)
.bind(("problem", Thing::from(("problem", id))))
.await?
.take(0)?)
}
1 change: 1 addition & 0 deletions tests/1.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1 2
1 change: 1 addition & 0 deletions tests/1.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3
4 changes: 2 additions & 2 deletions tests/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ use algohub_server::models::{
};
use anyhow::Result;
use rocket::{http::ContentType, local::asynchronous::Client};
use utils::Upload;
use utils::{rocket, Upload};

#[rocket::async_test]
async fn test_register() -> Result<()> {
let rocket = algohub_server::rocket().await;
let rocket = rocket().await;

let client = Client::tracked(rocket).await?;

Expand Down
5 changes: 4 additions & 1 deletion tests/category.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod utils;

use algohub_server::models::{
account::Register,
category::{Category, CategoryData, CreateCategory, ListCategories},
Expand All @@ -6,10 +8,11 @@ use algohub_server::models::{
};
use anyhow::Result;
use rocket::local::asynchronous::Client;
use utils::rocket;

#[rocket::async_test]
async fn test_category() -> Result<()> {
let rocket = algohub_server::rocket().await;
let rocket = rocket().await;
let client = Client::tracked(rocket).await?;

println!("Testing category...");
Expand Down
5 changes: 4 additions & 1 deletion tests/organization.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod utils;

use std::path::Path;

use algohub_server::models::{
Expand All @@ -8,10 +10,11 @@ use algohub_server::models::{
};
use anyhow::Result;
use rocket::local::asynchronous::Client;
use utils::rocket;

#[rocket::async_test]
async fn test_organization() -> Result<()> {
let rocket = algohub_server::rocket().await;
let rocket = rocket().await;

let client = Client::tracked(rocket).await?;

Expand Down
Loading

0 comments on commit 6abc5bc

Please sign in to comment.