From 7a4e263999adfb788636025376296c862db66c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E5=90=91=E5=A4=9C?= <46275354+fu050409@users.noreply.github.com> Date: Thu, 28 Nov 2024 21:38:59 +0800 Subject: [PATCH] refactor: use `UserRecordId` instead of `Thing` (#15) --- .changes/refactor-thing.md | 9 ++++++++ src/models/account.rs | 44 +------------------------------------ src/models/error.rs | 12 +++------- src/models/problem.rs | 45 ++++++++++++++++++++++++++++++-------- src/models/shared.rs | 21 ++++++++++++++++++ src/routes/account.rs | 10 ++++----- src/routes/problem.rs | 23 ++++++++++--------- src/utils/account.rs | 3 +-- src/utils/problem.rs | 4 ++-- tests/account.rs | 5 ++--- tests/problem.rs | 4 ++-- 11 files changed, 92 insertions(+), 88 deletions(-) create mode 100644 .changes/refactor-thing.md diff --git a/.changes/refactor-thing.md b/.changes/refactor-thing.md new file mode 100644 index 0000000..d5e747e --- /dev/null +++ b/.changes/refactor-thing.md @@ -0,0 +1,9 @@ +--- +"algohub-server": patch:refactor +--- + +Refactor `surrealdb::sql::Thing` in user interface data structures. + +- Use `UserRecordId` instead `surreal::sql::Thing` in structs. +- Remove `role` from `Account` definition. +- Refactor and re-export `ProblemDetail` in user interface. diff --git a/src/models/account.rs b/src/models/account.rs index c8a1a04..d97266e 100644 --- a/src/models/account.rs +++ b/src/models/account.rs @@ -1,45 +1,6 @@ -use rocket::{form::FromFormField, FromForm}; use serde::{Deserialize, Serialize}; use surrealdb::sql::Thing; -#[derive(Debug, Default, Clone, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum Role { - SuperAdmin = 0, - Admin = 1, - #[default] - User = 2, - Reserve = 3, - Inactive = 4, -} - -impl TryFrom for Role { - type Error = anyhow::Error; - - fn try_from(value: i8) -> Result { - match value { - 0 => Ok(Role::SuperAdmin), - 1 => Ok(Role::Admin), - 2 => Ok(Role::User), - 3 => Ok(Role::Reserve), - 4 => Ok(Role::Inactive), - _ => Err(anyhow::anyhow!("Invalid role: {}", value)), - } - } -} - -#[rocket::async_trait] -impl<'v> FromFormField<'v> for Role { - fn from_value(field: rocket::form::ValueField<'v>) -> rocket::form::Result<'v, Self> { - let value = field.value.parse::()?; - Ok(Role::try_from(value).map_err(|_| field.unexpected())?) - } - - fn default() -> Option { - Some(Self::User) - } -} - #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct Account { pub id: Option, @@ -61,14 +22,13 @@ pub struct Account { pub major: Option, pub rating: i8, - pub role: Role, pub active: bool, pub created_at: chrono::NaiveDateTime, pub updated_at: chrono::NaiveDateTime, } -#[derive(FromForm, Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct Profile { #[serde(skip_serializing_if = "Option::is_none")] pub username: Option, @@ -103,8 +63,6 @@ pub struct Profile { #[serde(skip_serializing_if = "Option::is_none")] pub rating: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub role: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub active: Option, } diff --git a/src/models/error.rs b/src/models/error.rs index 0beb48b..a5f5f44 100644 --- a/src/models/error.rs +++ b/src/models/error.rs @@ -7,21 +7,15 @@ pub struct ErrorResponse { pub message: String, } -impl From for ErrorResponse { - fn from(message: String) -> Self { +impl From for ErrorResponse { + fn from(message: T) -> Self { Self { success: false, - message, + message: message.to_string(), } } } -impl From<&str> for ErrorResponse { - fn from(message: &str) -> Self { - Self::from(message.to_string()) - } -} - #[derive(Responder)] pub enum Error { #[response(status = 500, content_type = "json")] diff --git a/src/models/problem.rs b/src/models/problem.rs index f5d16c0..db69342 100644 --- a/src/models/problem.rs +++ b/src/models/problem.rs @@ -1,7 +1,9 @@ use serde::{Deserialize, Serialize}; use surrealdb::sql::Thing; -use crate::routes::problem::ProblemData; +use crate::routes::problem::CreateProblem; + +use super::UserRecordId; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Sample { @@ -43,8 +45,8 @@ pub struct Problem { pub updated_at: chrono::NaiveDateTime, } -impl From> for Problem { - fn from(val: ProblemData<'_>) -> Self { +impl From> for Problem { + fn from(val: CreateProblem<'_>) -> Self { Problem { id: None, title: val.title.to_string(), @@ -69,9 +71,9 @@ impl From> for Problem { } } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug, Deserialize, Serialize)] pub struct ProblemDetail { - pub id: Thing, + pub id: String, pub title: String, pub description: String, @@ -80,12 +82,12 @@ pub struct ProblemDetail { pub samples: Vec, pub hint: Option, - pub time_limit: i32, - pub memory_limit: i32, + pub time_limit: u64, + pub memory_limit: u64, pub test_cases: Vec, - pub creator: Thing, - pub owner: Thing, + pub creator: UserRecordId, + pub owner: UserRecordId, pub categories: Vec, pub tags: Vec, @@ -95,3 +97,28 @@ pub struct ProblemDetail { pub created_at: chrono::NaiveDateTime, pub updated_at: chrono::NaiveDateTime, } + +impl From for ProblemDetail { + fn from(value: Problem) -> Self { + ProblemDetail { + id: value.id.unwrap().id.to_string(), + title: value.title, + description: value.description, + input: value.input, + output: value.output, + samples: value.samples, + hint: value.hint, + time_limit: value.time_limit, + memory_limit: value.memory_limit, + test_cases: value.test_cases, + creator: value.creator.into(), + owner: value.owner.into(), + categories: value.categories, + tags: value.tags, + mode: value.mode, + private: value.private, + created_at: value.created_at, + updated_at: value.updated_at, + } + } +} diff --git a/src/models/shared.rs b/src/models/shared.rs index 384f42d..8a657c7 100644 --- a/src/models/shared.rs +++ b/src/models/shared.rs @@ -6,6 +6,27 @@ pub struct Record { id: Option, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UserRecordId { + tb: String, + id: String, +} + +impl From for UserRecordId { + fn from(thing: Thing) -> Self { + UserRecordId { + tb: thing.tb, + id: thing.id.to_string(), + } + } +} + +impl Into for UserRecordId { + fn into(self) -> Thing { + Thing::from((self.tb, self.id)) + } +} + #[derive(Serialize)] pub struct UpdateAt { pub updated_at: chrono::NaiveDateTime, diff --git a/src/routes/account.rs b/src/routes/account.rs index 36c31af..f39a5d9 100644 --- a/src/routes/account.rs +++ b/src/routes/account.rs @@ -76,14 +76,14 @@ pub async fn register( #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(crate = "rocket::serde")] -pub struct ProfileData<'r> { +pub struct MergeProfile<'r> { pub id: &'r str, pub token: &'r str, pub profile: Profile, } #[post("/profile", data = "")] -pub async fn profile(db: &State>, profile: Json>) -> Result { +pub async fn profile(db: &State>, profile: Json>) -> Result { account::get_by_id::(db, profile.id) .await .map_err(|e| Error::ServerError(Json(e.to_string().into())))? @@ -108,10 +108,8 @@ pub async fn profile(db: &State>, profile: Json> pub async fn get_profile(db: &State>, id: &str) -> Result { let profile = account::get_by_id::(db, id) .await - .map_err(|e| Error::NotFound(Json(e.to_string().into())))? - .ok_or(Error::ServerError(Json( - "Filter returned None unexpectedly".into(), - )))?; + .map_err(|e| Error::ServerError(Json(e.to_string().into())))? + .ok_or(Error::NotFound(Json("Account not found".into())))?; Ok(Response { success: true, diff --git a/src/routes/problem.rs b/src/routes/problem.rs index 66e69f2..d50497c 100644 --- a/src/routes/problem.rs +++ b/src/routes/problem.rs @@ -25,7 +25,7 @@ use crate::{ #[derive(Serialize, Deserialize)] #[serde(crate = "rocket::serde")] -pub struct ProblemData<'r> { +pub struct CreateProblem<'r> { pub id: &'r str, pub token: &'r str, @@ -60,7 +60,7 @@ pub struct ProblemResponse { #[post("/create", data = "")] pub async fn create( db: &State>, - problem: Json>, + problem: Json>, ) -> Result { if !session::verify(db, problem.id, problem.token).await { return Err(Error::Unauthorized(Json("Invalid token".into()))); @@ -95,7 +95,7 @@ pub async fn get( id: &str, auth: Json>, ) -> Result { - let problem = problem::get::(db, id) + let problem = problem::get::(db, id) .await .map_err(|e| Error::ServerError(Json(e.to_string().into())))? .ok_or(Error::NotFound(Json( @@ -103,14 +103,13 @@ pub async fn get( )))?; let has_permission = if problem.private { - if auth.id.is_none() || auth.token.is_none() { + if auth.id.is_none() + || auth.token.is_none() + || !session::verify(db, auth.id.unwrap(), auth.token.unwrap()).await + { false } else { - if !session::verify(db, auth.id.unwrap(), auth.token.unwrap()).await { - false - } else { - auth.id.unwrap() == problem.owner.id.to_string() - } + auth.id.unwrap() == problem.owner.id.to_string() } } else { true @@ -125,7 +124,7 @@ pub async fn get( Ok(Json(Response { success: true, message: "Problem found".to_string(), - data: Some(problem), + data: Some(problem.into()), })) } @@ -153,14 +152,14 @@ pub async fn list( let data = data.into_inner(); - let problems = problem::list_for_account(db, data.id, authed_id, data.limit) + let problems = problem::list_for_account::(db, data.id, authed_id, data.limit) .await .map_err(|e| Error::ServerError(Json(e.to_string().into())))?; Ok(Json(Response { success: true, message: "Problems found".to_string(), - data: Some(problems), + data: Some(problems.into_iter().map(|p| p.into()).collect()), })) } diff --git a/src/utils/account.rs b/src/utils/account.rs index 297b3cc..0316a66 100644 --- a/src/utils/account.rs +++ b/src/utils/account.rs @@ -3,7 +3,7 @@ use serde::Deserialize; use surrealdb::engine::remote::ws::Client; use surrealdb::Surreal; -use crate::models::account::{Account, Profile, Role}; +use crate::models::account::{Account, Profile}; use crate::models::UpdateAt; use crate::routes::account::RegisterData; @@ -24,7 +24,6 @@ pub async fn create(db: &Surreal, register: RegisterData) -> Result, problem: ProblemData<'_>) -> Result> { +pub async fn create(db: &Surreal, problem: CreateProblem<'_>) -> Result> { Ok(db .create("problem") .content(Into::::into(problem)) diff --git a/tests/account.rs b/tests/account.rs index 72394db..c445c0f 100644 --- a/tests/account.rs +++ b/tests/account.rs @@ -6,7 +6,7 @@ use algohub_server::{ response::{Empty, Response}, Token, }, - routes::account::{ProfileData, RegisterData, RegisterResponse, UploadResponse}, + routes::account::{MergeProfile, RegisterData, RegisterResponse, UploadResponse}, }; use anyhow::Result; use rocket::{http::ContentType, local::asynchronous::Client}; @@ -91,7 +91,7 @@ async fn test_register() -> Result<()> { let response = client .post("/account/profile") - .json(&ProfileData { + .json(&MergeProfile { id: &data.id, token: &data.token, profile: Profile { @@ -110,7 +110,6 @@ async fn test_register() -> Result<()> { major: None, rating: None, active: None, - role: None, }, }) .dispatch() diff --git a/tests/problem.rs b/tests/problem.rs index 5899c25..b0319d2 100644 --- a/tests/problem.rs +++ b/tests/problem.rs @@ -6,7 +6,7 @@ use algohub_server::{ }, routes::{ account::{RegisterData, RegisterResponse}, - problem::{ListProblem, ProblemData, ProblemResponse}, + problem::{ListProblem, CreateProblem, ProblemResponse}, }, }; use anyhow::Result; @@ -47,7 +47,7 @@ async fn test_problem() -> Result<()> { for i in 0..10 { let response = client .post("/problem/create") - .json(&ProblemData { + .json(&CreateProblem { id: &id, token: &token, title: &format!("Test Problem #{}", i),