1use std::{
2 fmt::{Display, Formatter},
3 str::FromStr,
4};
5
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8use serde_with::{serde_as, skip_serializing_none};
9use validator::Validate;
10
11use crate::{
12 program::ProgramId, resource::Resource, Event, Identifier, IdentifierError, ObjectType,
13 Program, Report, Ven,
14};
15
16#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Validate)]
18#[serde(rename_all = "camelCase")]
19pub struct Subscription {
20 pub id: SubscriptionId,
22 #[serde(with = "crate::serde_rfc3339")]
24 pub created_date_time: DateTime<Utc>,
25 #[serde(with = "crate::serde_rfc3339")]
27 pub modification_date_time: DateTime<Utc>,
28 #[serde(flatten)]
29 #[validate(nested)]
30 pub content: SubscriptionRequest,
31}
32
33#[skip_serializing_none]
37#[serde_as]
38#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Validate)]
39#[serde(rename_all = "camelCase")]
40pub struct SubscriptionRequest {
41 pub client_name: String,
43
44 #[serde(rename = "programID")]
46 pub program_id: Option<ProgramId>,
47
48 pub object_operations: Vec<SubscriptionObjectOperation>,
50 }
55
56#[skip_serializing_none]
57#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Validate)]
58#[serde(rename_all = "camelCase")]
59pub struct SubscriptionObjectOperation {
60 pub objects: Vec<ObjectType>,
62
63 pub operations: Vec<Operation>,
65
66 #[serde(default)]
68 pub mechanism: NotificationMechanism,
69
70 pub callback_url: Option<String>,
72
73 pub bearer_token: Option<String>,
77}
78
79#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
80#[serde(rename_all = "UPPERCASE")]
81pub enum Operation {
82 Create,
83 Update,
84 Delete,
85}
86
87#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)]
88#[serde(rename_all = "UPPERCASE")]
89pub enum NotificationMechanism {
90 #[default]
91 Webhook,
92 Websocket,
93}
94
95#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Hash, Eq)]
97pub struct SubscriptionId(pub(crate) Identifier);
98
99impl Display for SubscriptionId {
100 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
101 write!(f, "{}", self.0)
102 }
103}
104
105impl SubscriptionId {
106 pub fn as_str(&self) -> &str {
107 self.0.as_str()
108 }
109}
110
111impl FromStr for SubscriptionId {
112 type Err = IdentifierError;
113
114 fn from_str(s: &str) -> Result<Self, Self::Err> {
115 Ok(Self(s.parse()?))
116 }
117}
118
119#[skip_serializing_none]
121#[serde_as]
122#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Validate)]
123#[serde(rename_all = "camelCase")]
124pub struct Notification {
125 pub id: Identifier,
135
136 pub operation: Operation,
138
139 #[serde(flatten)]
141 pub object: AnyObject,
142 }
147
148#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
149#[serde(tag = "objectType", content = "object", rename_all = "UPPERCASE")]
150pub enum AnyObject {
151 Program(Program),
152 Report(Report),
153 Event(Event),
154 Subscription(Subscription),
155 Ven(Ven),
156 Resource(Resource),
157}
158
159impl AnyObject {
160 pub fn id(&self) -> Identifier {
161 match self {
162 AnyObject::Program(program) => program.id.0.clone(),
163 AnyObject::Report(report) => report.id.0.clone(),
164 AnyObject::Event(event) => event.id.0.clone(),
165 AnyObject::Subscription(subscription) => subscription.id.0.clone(),
166 AnyObject::Ven(ven) => ven.id.0.clone(),
167 AnyObject::Resource(resource) => resource.id.0.clone(),
168 }
169 }
170}
171
172#[skip_serializing_none]
174#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Validate)]
175#[serde(rename_all = "UPPERCASE")]
176pub struct NotifiersResponse {
177 pub websocket: bool,
178}
179
180#[cfg(test)]
181mod tests {
182 use crate::program::ProgramRequest;
183
184 use super::*;
185
186 #[test]
187 fn parse_subscription_request() {
188 let example = r#"{
189 "clientName": "myClient",
190 "programID": "44",
191 "objectOperations": [
192 {
193 "callbackUrl": "https://myserver.com/event_callbacks",
194 "operations": [
195 "CREATE",
196 "UPDATE"
197 ],
198 "objects": [
199 "EVENT"
200 ]
201 },
202 {
203 "callbackUrl": "https://myserver.com/program_callbacks",
204 "operations": [
205 "CREATE",
206 "UPDATE"
207 ],
208 "objects": [
209 "PROGRAM"
210 ]
211 }
212 ]
213}"#;
214 assert_eq!(
215 serde_json::from_str::<SubscriptionRequest>(example).unwrap(),
216 SubscriptionRequest {
217 client_name: "myClient".to_owned(),
218 program_id: Some("44".parse().unwrap()),
219 object_operations: vec![
220 SubscriptionObjectOperation {
221 objects: vec![ObjectType::Event],
222 operations: vec![Operation::Create, Operation::Update],
223 mechanism: NotificationMechanism::Webhook,
224 callback_url: Some("https://myserver.com/event_callbacks".to_owned()),
225 bearer_token: None,
226 },
227 SubscriptionObjectOperation {
228 objects: vec![ObjectType::Program],
229 operations: vec![Operation::Create, Operation::Update],
230 mechanism: NotificationMechanism::Webhook,
231 callback_url: Some("https://myserver.com/program_callbacks".to_owned()),
232 bearer_token: None,
233 }
234 ],
235 }
237 );
238 }
239
240 #[test]
241 fn parse_notification() {
242 let example = r#"{
243 "id": "100",
244 "objectType": "PROGRAM",
245 "operation": "UPDATE",
246 "object": {
247 "bindingEvents": false,
248 "createdDateTime": "2023-06-15T15:51:29.000Z",
249 "modificationDateTime": "2023-06-15T15:51:29.000Z",
250 "id": "0",
251 "localPrice": false,
252 "objectType": "PROGRAM",
253 "programName": "myProgram"
254 }
255}"#;
256 assert_eq!(
257 serde_json::from_str::<Notification>(example).unwrap(),
258 Notification {
259 id: "100".parse().unwrap(),
260 operation: Operation::Update,
261 object: AnyObject::Program(Program {
262 id: "0".parse().unwrap(),
263 created_date_time: "2023-06-15T15:51:29.000Z".parse().unwrap(),
264 modification_date_time: "2023-06-15T15:51:29.000Z".parse().unwrap(),
265 content: ProgramRequest {
266 program_name: "myProgram".to_owned(),
267 interval_period: None,
268 program_descriptions: None,
269 payload_descriptors: None,
270 attributes: None,
271 targets: vec![],
272 }
273 }),
274 }
276 );
277 }
278}