distri_types/api/
notes.rs1use chrono::{DateTime, Utc};
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6use utoipa::ToSchema;
7use uuid::Uuid;
8
9#[derive(Debug, Clone, Default, Serialize, Deserialize, ToSchema, JsonSchema)]
11pub struct ListNotesQuery {
12 pub tag: Option<String>,
14 pub search: Option<String>,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
20#[schema(example = json!({"title": "My Note", "content": "Hello world", "tags": ["work", "ideas"]}))]
21pub struct CreateNoteRequest {
22 pub title: String,
23 pub content: String,
24 #[serde(default)]
25 pub tags: Vec<String>,
26}
27
28#[derive(Debug, Clone, Default, Serialize, Deserialize, ToSchema, JsonSchema)]
30pub struct UpdateNoteRequest {
31 pub title: Option<String>,
32 pub content: Option<String>,
33 pub tags: Option<Vec<String>>,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
38pub struct NoteRecord {
39 pub id: Uuid,
40 pub workspace_id: Uuid,
41 pub title: String,
42 pub content: String,
43 #[serde(default)]
44 pub tags: Vec<String>,
45 pub created_by: Option<Uuid>,
46 pub created_at: DateTime<Utc>,
47 pub updated_at: DateTime<Utc>,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
52pub struct ListNotesResponse {
53 pub notes: Vec<NoteRecord>,
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
64 fn note_record_round_trips_snake_case() {
65 let note = NoteRecord {
66 id: Uuid::nil(),
67 workspace_id: Uuid::nil(),
68 title: "t".into(),
69 content: "c".into(),
70 tags: vec!["a".into()],
71 created_by: None,
72 created_at: DateTime::<Utc>::from_timestamp(0, 0).unwrap(),
73 updated_at: DateTime::<Utc>::from_timestamp(0, 0).unwrap(),
74 };
75 let v = serde_json::to_value(¬e).unwrap();
76 let obj = v.as_object().unwrap();
77 for key in [
78 "id",
79 "workspace_id",
80 "created_by",
81 "created_at",
82 "updated_at",
83 ] {
84 assert!(obj.contains_key(key), "missing key `{key}`");
85 }
86 let back: NoteRecord = serde_json::from_value(v).unwrap();
87 assert_eq!(back.title, "t");
88 assert_eq!(back.tags, vec!["a".to_string()]);
89 }
90
91 #[test]
93 fn create_note_request_round_trips() {
94 let req = CreateNoteRequest {
95 title: "x".into(),
96 content: "y".into(),
97 tags: vec!["z".into()],
98 };
99 let back: CreateNoteRequest =
100 serde_json::from_value(serde_json::to_value(&req).unwrap()).unwrap();
101 assert_eq!(back.title, "x");
102 assert_eq!(back.tags, vec!["z".to_string()]);
103 }
104
105 #[test]
106 fn update_note_request_omits_none_friendly() {
107 let req: UpdateNoteRequest = serde_json::from_value(serde_json::json!({})).unwrap();
110 assert!(req.title.is_none() && req.content.is_none() && req.tags.is_none());
111 }
112}