Skip to content

Commit

Permalink
refactor: use UserRecordId instead of Thing (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
fu050409 authored Nov 28, 2024
1 parent ed3831a commit 7a4e263
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 88 deletions.
9 changes: 9 additions & 0 deletions .changes/refactor-thing.md
Original file line number Diff line number Diff line change
@@ -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.
44 changes: 1 addition & 43 deletions src/models/account.rs
Original file line number Diff line number Diff line change
@@ -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<i8> for Role {
type Error = anyhow::Error;

fn try_from(value: i8) -> Result<Self, Self::Error> {
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::<i8>()?;
Ok(Role::try_from(value).map_err(|_| field.unexpected())?)
}

fn default() -> Option<Self> {
Some(Self::User)
}
}

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Account {
pub id: Option<Thing>,
Expand All @@ -61,14 +22,13 @@ pub struct Account {
pub major: Option<String>,

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<String>,
Expand Down Expand Up @@ -103,8 +63,6 @@ pub struct Profile {
#[serde(skip_serializing_if = "Option::is_none")]
pub rating: Option<i8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub role: Option<Role>,
#[serde(skip_serializing_if = "Option::is_none")]
pub active: Option<bool>,
}

Expand Down
12 changes: 3 additions & 9 deletions src/models/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,15 @@ pub struct ErrorResponse {
pub message: String,
}

impl From<String> for ErrorResponse {
fn from(message: String) -> Self {
impl<T: ToString> From<T> 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")]
Expand Down
45 changes: 36 additions & 9 deletions src/models/problem.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -43,8 +45,8 @@ pub struct Problem {
pub updated_at: chrono::NaiveDateTime,
}

impl From<ProblemData<'_>> for Problem {
fn from(val: ProblemData<'_>) -> Self {
impl From<CreateProblem<'_>> for Problem {
fn from(val: CreateProblem<'_>) -> Self {
Problem {
id: None,
title: val.title.to_string(),
Expand All @@ -69,9 +71,9 @@ impl From<ProblemData<'_>> 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,
Expand All @@ -80,12 +82,12 @@ pub struct ProblemDetail {
pub samples: Vec<Sample>,
pub hint: Option<String>,

pub time_limit: i32,
pub memory_limit: i32,
pub time_limit: u64,
pub memory_limit: u64,
pub test_cases: Vec<Sample>,

pub creator: Thing,
pub owner: Thing,
pub creator: UserRecordId,
pub owner: UserRecordId,
pub categories: Vec<String>,
pub tags: Vec<String>,

Expand All @@ -95,3 +97,28 @@ pub struct ProblemDetail {
pub created_at: chrono::NaiveDateTime,
pub updated_at: chrono::NaiveDateTime,
}

impl From<Problem> 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,
}
}
}
21 changes: 21 additions & 0 deletions src/models/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,27 @@ pub struct Record {
id: Option<Thing>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserRecordId {
tb: String,
id: String,
}

impl From<Thing> for UserRecordId {
fn from(thing: Thing) -> Self {
UserRecordId {
tb: thing.tb,
id: thing.id.to_string(),
}
}
}

impl Into<Thing> for UserRecordId {
fn into(self) -> Thing {
Thing::from((self.tb, self.id))
}
}

#[derive(Serialize)]
pub struct UpdateAt {
pub updated_at: chrono::NaiveDateTime,
Expand Down
10 changes: 4 additions & 6 deletions src/routes/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "<profile>")]
pub async fn profile(db: &State<Surreal<Client>>, profile: Json<ProfileData<'_>>) -> Result<Empty> {
pub async fn profile(db: &State<Surreal<Client>>, profile: Json<MergeProfile<'_>>) -> Result<Empty> {
account::get_by_id::<Record>(db, profile.id)
.await
.map_err(|e| Error::ServerError(Json(e.to_string().into())))?
Expand All @@ -108,10 +108,8 @@ pub async fn profile(db: &State<Surreal<Client>>, profile: Json<ProfileData<'_>>
pub async fn get_profile(db: &State<Surreal<Client>>, id: &str) -> Result<Profile> {
let profile = account::get_by_id::<Profile>(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,
Expand Down
23 changes: 11 additions & 12 deletions src/routes/problem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down Expand Up @@ -60,7 +60,7 @@ pub struct ProblemResponse {
#[post("/create", data = "<problem>")]
pub async fn create(
db: &State<Surreal<Client>>,
problem: Json<ProblemData<'_>>,
problem: Json<CreateProblem<'_>>,
) -> Result<ProblemResponse> {
if !session::verify(db, problem.id, problem.token).await {
return Err(Error::Unauthorized(Json("Invalid token".into())));
Expand Down Expand Up @@ -95,22 +95,21 @@ pub async fn get(
id: &str,
auth: Json<Authenticate<'_>>,
) -> Result<ProblemDetail> {
let problem = problem::get::<ProblemDetail>(db, id)
let problem = problem::get::<Problem>(db, id)
.await
.map_err(|e| Error::ServerError(Json(e.to_string().into())))?
.ok_or(Error::NotFound(Json(
"Problem with specified id not found".into(),
)))?;

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
Expand All @@ -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()),
}))
}

Expand Down Expand Up @@ -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::<Problem>(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()),
}))
}

Expand Down
3 changes: 1 addition & 2 deletions src/utils/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -24,7 +24,6 @@ pub async fn create(db: &Surreal<Client>, register: RegisterData) -> Result<Opti
username: register.username,
password: register.password,
email: register.email,
role: Role::User,
created_at: chrono::Local::now().naive_local(),
..Default::default()
})
Expand Down
4 changes: 2 additions & 2 deletions src/utils/problem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use anyhow::Result;
use serde::Deserialize;
use surrealdb::{engine::remote::ws::Client, Surreal};

use crate::{models::problem::Problem, routes::problem::ProblemData};
use crate::{models::problem::Problem, routes::problem::CreateProblem};

pub async fn create(db: &Surreal<Client>, problem: ProblemData<'_>) -> Result<Option<Problem>> {
pub async fn create(db: &Surreal<Client>, problem: CreateProblem<'_>) -> Result<Option<Problem>> {
Ok(db
.create("problem")
.content(Into::<Problem>::into(problem))
Expand Down
5 changes: 2 additions & 3 deletions tests/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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 {
Expand All @@ -110,7 +110,6 @@ async fn test_register() -> Result<()> {
major: None,
rating: None,
active: None,
role: None,
},
})
.dispatch()
Expand Down
4 changes: 2 additions & 2 deletions tests/problem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use algohub_server::{
},
routes::{
account::{RegisterData, RegisterResponse},
problem::{ListProblem, ProblemData, ProblemResponse},
problem::{ListProblem, CreateProblem, ProblemResponse},
},
};
use anyhow::Result;
Expand Down Expand Up @@ -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),
Expand Down

0 comments on commit 7a4e263

Please sign in to comment.