Skip to content
This repository was archived by the owner on Feb 24, 2025. It is now read-only.

Commit bee4bd6

Browse files
committed
feat: serialization for unexpected String / u64 type of timestamp
1 parent 553c3b2 commit bee4bd6

File tree

10 files changed

+145
-13
lines changed

10 files changed

+145
-13
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ tower = "0.4.13"
3333
tower-http = "0.5.2"
3434
tracing-subscriber = { version = "0.3.18", features = ["tracing", "time", "serde", "serde_json", "json", "regex"] }
3535
uuid = "1.8.0"
36+
zerocopy = "0.7.32"
3637

3738
[profile.release]
3839
lto = true

aes256.key

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
d765fcb8ae87f29c4de1645728ff57e7668e4c6be695fdf38d5cdd90a23f6456

src/models/activities.rs

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
use std::fmt;
2+
13
use bson::oid::ObjectId;
2-
use serde::{Deserialize, Serialize};
4+
use chrono::DateTime;
5+
use serde::{de::{self, Visitor}, Deserialize, Deserializer, Serialize};
36

47
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
58
#[serde(rename_all = "kebab-case")]
@@ -36,6 +39,17 @@ pub enum ActivityMode {
3639
SocialPractice,
3740
}
3841

42+
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
43+
#[serde(rename_all = "kebab-case")]
44+
pub enum SpecialActivityCategory {
45+
Prize,
46+
Import,
47+
Club,
48+
Deduction,
49+
Other,
50+
}
51+
52+
3953
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
4054
pub struct ActivityMember {
4155
pub _id: String, // ObjectId
@@ -56,7 +70,9 @@ pub struct ActivityMemberHistory {
5670
pub action: ActivityMemberStatus,
5771
}
5872

73+
5974
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
75+
#[serde(rename_all = "camelCase")]
6076
pub struct Activity {
6177
#[serde(rename = "_id")]
6278
pub _id: ObjectId,
@@ -65,10 +81,48 @@ pub struct Activity {
6581
pub name: String,
6682
pub description: Option<String>,
6783
pub duration: Option<f64>,
68-
pub date: u128,
69-
pub created_at: u128,
70-
pub updated_at: u128,
84+
#[serde(deserialize_with = "datetime_or_u64")]
85+
pub date: u64,
86+
#[serde(deserialize_with = "datetime_or_u64")]
87+
pub created_at: u64,
88+
#[serde(deserialize_with = "datetime_or_u64")]
89+
pub updated_at: u64,
7190
pub creator: ObjectId, // ObjectId
7291
pub status: ActivityStatus,
7392
pub members: Option<Vec<ActivityMember>>,
93+
pub location: Option<String>,
94+
pub category: Option<SpecialActivityCategory>,
95+
}
96+
97+
fn datetime_or_u64<'de, D>(deserializer: D) -> Result<u64, D::Error>
98+
where
99+
D: Deserializer<'de>,
100+
{
101+
struct DateTimeOrU64;
102+
103+
impl<'de> Visitor<'de> for DateTimeOrU64 {
104+
type Value = u64;
105+
106+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
107+
formatter.write_str("a UNIX timestamp as a u64 or a datetime string")
108+
}
109+
110+
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
111+
where
112+
E: de::Error,
113+
{
114+
Ok(value)
115+
}
116+
117+
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
118+
where
119+
E: de::Error,
120+
{
121+
DateTime::parse_from_rfc3339(value)
122+
.map_err(de::Error::custom)
123+
.and_then(|dt| Ok(dt.timestamp() as u64))
124+
}
125+
}
126+
127+
deserializer.deserialize_any(DateTimeOrU64)
74128
}

src/routers/activities/read.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ use tokio::sync::Mutex;
2020

2121
#[derive(Debug, Serialize, Deserialize)]
2222
pub struct ReadActivityQuery {
23-
page: Option<u32>,
24-
perpage: Option<u32>,
25-
query: Option<String>,
23+
pub page: Option<u32>,
24+
pub perpage: Option<u32>,
25+
pub query: Option<String>,
2626
}
2727

2828
pub async fn read_all(
@@ -63,10 +63,19 @@ pub async fn read_all(
6363
loop {
6464
let doc_result = cursor.try_next().await;
6565
if let Ok(Some(document)) = doc_result {
66-
if let Ok(activity) = from_document::<Activity>(document) {
67-
activities.push(activity);
68-
} else {
69-
break;
66+
match from_document::<Activity>(document) {
67+
Ok(activity) => activities.push(activity),
68+
Err(e) => {
69+
let response: ErrorResponse = ErrorResponse {
70+
status: ResponseStatus::Error,
71+
code: 500,
72+
message: "Failed to read activity: ".to_string() + &e.to_string(),
73+
}
74+
.into();
75+
println!("{:?}", response);
76+
let response = serde_json::to_string(&response).unwrap();
77+
return (StatusCode::INTERNAL_SERVER_ERROR, Json(response));
78+
}
7079
}
7180
} else {
7281
break;

src/tests/apis/activity.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#[cfg(test)]
2+
mod tests {
3+
use crate::{
4+
database,
5+
models::groups::GroupPermission,
6+
routers::activities::{self, read::ReadActivityQuery},
7+
utils::jwt::{TokenType, UserData},
8+
};
9+
use axum::{extract::Query, response::IntoResponse, Extension};
10+
use bson::oid::ObjectId;
11+
use std::sync::Arc;
12+
use tokio::sync::Mutex;
13+
#[tokio::test]
14+
async fn get_activities() {
15+
let id = ObjectId::new().to_hex();
16+
let token = UserData {
17+
id: id.clone(),
18+
perms: vec![GroupPermission::Student, GroupPermission::Admin],
19+
term: TokenType::LongTerm,
20+
};
21+
let client = database::create_client().await;
22+
if let Err(_) = client {
23+
assert!(false);
24+
}
25+
let client = client.unwrap();
26+
let extension = Arc::new(Mutex::new(client));
27+
let extension = Extension(extension);
28+
let result = activities::read::read_all(
29+
extension,
30+
token,
31+
Query(ReadActivityQuery {
32+
page: Some(1),
33+
perpage: Some(10),
34+
query: Some("".to_string()),
35+
}),
36+
)
37+
.await;
38+
let result = result.into_response();
39+
assert!(result.status().is_success());
40+
}
41+
}

src/tests/apis/auth.rs

Whitespace-only changes.

src/tests/apis/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub mod activity;
2+
pub mod auth;

src/tests/auth.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#[cfg(test)]
22
mod tests {
33
use std::time::{SystemTime, UNIX_EPOCH};
4+
use bson::oid::ObjectId;
5+
6+
use crate::models::groups::GroupPermission;
7+
use crate::utils::jwt::{generate_token, verify_token, TokenType};
48
use crate::utils::rsa::{generate_keypair, encrypt, decrypt};
59
use crate::routers::auth::LoginCredentials;
610
#[tokio::test]
@@ -28,14 +32,31 @@ mod tests {
2832
let credential = credential.unwrap();
2933
let (private_key, public_key) = generate_keypair().await;
3034
let encrypted = encrypt(&public_key, credential.as_str());
31-
println!("Encrypted: {:?}", &encrypted);
3235
let decrypted = decrypt(&private_key, &encrypted).await;
33-
println!("Decrypted: {:?}", &decrypted);
3436
let decrypted = serde_json::from_str(&decrypted.as_str());
3537
if let Err(_) = decrypted {
3638
assert!(false);
3739
}
3840
let decrypted = decrypted.unwrap();
41+
println!("Decrypted: {:#?}", &decrypted);
3942
assert_eq!(payload, decrypted);
4043
}
44+
#[tokio::test]
45+
async fn token_validation() {
46+
let sub = ObjectId::new().to_hex();
47+
let perms = vec![
48+
GroupPermission::Student,
49+
GroupPermission::Admin
50+
];
51+
let token = generate_token(sub.as_str(), TokenType::LongTerm, perms.clone());
52+
let token = token.as_str();
53+
let result = verify_token(token.to_string());
54+
if let Err(_) = result {
55+
assert!(false);
56+
}
57+
let result = result.unwrap();
58+
assert_eq!(result.sub, sub);
59+
assert_eq!(result.perms, perms);
60+
assert_eq!(result.term, TokenType::LongTerm);
61+
}
4162
}

src/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
mod auth;
2+
mod apis;

0 commit comments

Comments
 (0)