nextcloud_notifications/
notification.rs1use serde::{Deserialize, Serialize};
4
5use crate::action::NotificationAction;
6use crate::wire::{lossy_string, lossy_u64};
7
8#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
14pub struct Notification {
15 #[serde(deserialize_with = "lossy_u64")]
17 pub notification_id: u64,
18
19 pub app: String,
21
22 pub user: String,
24
25 pub datetime: String,
27
28 pub object_type: String,
30
31 #[serde(deserialize_with = "lossy_string")]
33 pub object_id: String,
34
35 pub subject: String,
37
38 #[serde(default)]
40 pub message: String,
41
42 #[serde(default)]
44 pub link: String,
45
46 #[serde(default)]
48 pub actions: Vec<NotificationAction>,
49}
50
51impl Notification {
52 #[allow(clippy::too_many_arguments)]
54 pub fn new(
55 notification_id: u64,
56 app: String,
57 user: String,
58 datetime: String,
59 object_type: String,
60 object_id: String,
61 subject: String,
62 message: String,
63 link: String,
64 actions: Vec<NotificationAction>,
65 ) -> Self {
66 Self {
67 notification_id,
68 app,
69 user,
70 datetime,
71 object_type,
72 object_id,
73 subject,
74 message,
75 link,
76 actions,
77 }
78 }
79
80 pub const fn notification_id(&self) -> u64 {
82 self.notification_id
83 }
84
85 pub fn app(&self) -> &str {
87 &self.app
88 }
89
90 pub fn user(&self) -> &str {
92 &self.user
93 }
94
95 pub fn datetime(&self) -> &str {
97 &self.datetime
98 }
99
100 pub fn object_type(&self) -> &str {
102 &self.object_type
103 }
104
105 pub fn object_id(&self) -> &str {
107 &self.object_id
108 }
109
110 pub fn subject(&self) -> &str {
112 &self.subject
113 }
114
115 pub fn message(&self) -> &str {
117 &self.message
118 }
119
120 pub fn link(&self) -> &str {
122 &self.link
123 }
124
125 pub fn actions(&self) -> &[NotificationAction] {
127 &self.actions
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn parses_notification_from_json() {
137 let json = r#"{
138 "notification_id": 61,
139 "app": "files_sharing",
140 "user": "admin",
141 "datetime": "2026-10-24T12:00:00+00:00",
142 "object_type": "remote_share",
143 "object_id": "13",
144 "subject": "Share invitation received",
145 "message": "User shared a folder",
146 "link": "https://example.com/share/13",
147 "actions": [
148 {
149 "label": "Accept",
150 "link": "https://example.com/share/accept",
151 "type": "POST",
152 "primary": true
153 }
154 ]
155 }"#;
156
157 let n: Notification = serde_json::from_str(json).unwrap();
158 assert_eq!(n.notification_id(), 61);
159 assert_eq!(n.app(), "files_sharing");
160 assert_eq!(n.user(), "admin");
161 assert_eq!(n.datetime(), "2026-10-24T12:00:00+00:00");
162 assert_eq!(n.object_type(), "remote_share");
163 assert_eq!(n.object_id(), "13");
164 assert_eq!(n.subject(), "Share invitation received");
165 assert_eq!(n.message(), "User shared a folder");
166 assert_eq!(n.link(), "https://example.com/share/13");
167 assert_eq!(n.actions().len(), 1);
168 assert_eq!(n.actions()[0].label(), "Accept");
169 }
170
171 #[test]
172 fn parses_numeric_object_id_and_string_notification_id() {
173 let json = r#"{
174 "notification_id": "99",
175 "app": "core",
176 "user": "alice",
177 "datetime": "2026-10-24T12:00:00+00:00",
178 "object_type": "user",
179 "object_id": 1234,
180 "subject": "Password reset"
181 }"#;
182
183 let n: Notification = serde_json::from_str(json).unwrap();
184 assert_eq!(n.notification_id(), 99);
185 assert_eq!(n.object_id(), "1234");
186 assert_eq!(n.message(), "");
187 assert_eq!(n.link(), "");
188 assert!(n.actions().is_empty());
189 }
190}