diff options
author | D. Scott Boggs <scott@tams.tech> | 2023-02-10 08:54:00 -0500 |
---|---|---|
committer | D. Scott Boggs <scott@tams.tech> | 2023-02-12 08:54:05 -0500 |
commit | 6287317fd5022a5cc215e9a0bd6eb64dc2746e33 (patch) | |
tree | e5431dd60e39dcf87ab590c22b47999ca690b305 | |
parent | fc826dd982eebbdfb25f56ff3eee8fb719f7d857 (diff) |
Comb web push subscription entitycomb-entities/small-fries
-rw-r--r-- | entities/src/push.rs | 119 | ||||
-rw-r--r-- | src/mastodon.rs | 2 | ||||
-rw-r--r-- | src/requests/push.rs | 471 |
3 files changed, 236 insertions, 356 deletions
diff --git a/entities/src/push.rs b/entities/src/push.rs index b1ba3d6..aafbe3f 100644 --- a/entities/src/push.rs +++ b/entities/src/push.rs @@ -1,31 +1,101 @@ +use derive_builder::Builder; use serde::{Deserialize, Serialize}; use crate::SubscriptionId; /// Represents the `alerts` key of the `Subscription` object -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, Builder)] +#[builder(build_fn(error = "crate::error::Error"), default)] pub struct Alerts { /// flag for follow alerts + #[serde(default)] + #[builder(setter(strip_option))] pub follow: Option<bool>, /// flag for favourite alerts + #[serde(default)] + #[builder(setter(strip_option))] pub favourite: Option<bool>, /// flag for reblog alerts + #[serde(default)] + #[builder(setter(strip_option))] pub reblog: Option<bool>, /// flag for mention alerts + #[serde(default)] + #[builder(setter(strip_option))] pub mention: Option<bool>, + /// Receive a push notification when a subscribed account posts a status? + #[serde(default)] + #[builder(setter(strip_option))] + pub status: Option<bool>, + /// Receive a push notification when someone has requested to followed you? + #[serde(default)] + #[builder(setter(strip_option))] + pub follow_request: Option<bool>, + /// Receive a push notification when a poll you voted in or created has ended? + #[serde(default)] + #[builder(setter(strip_option))] + pub poll: Option<bool>, + /// Receive a push notification when a status you interacted with has been edited? + #[serde(default)] + #[builder(setter(strip_option))] + pub update: Option<bool>, + /// Admin-related alerts settings + #[serde(flatten, default)] + #[builder(setter(custom))] + pub admin: AdminAlerts, +} +/// Admin-related alerts settings +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct AdminAlerts { + /// Receive a push notification when a new user has signed up? + #[serde(default, rename = "admin.sign_up")] + pub sign_up: Option<bool>, + /// Receive a push notification when a new report has been filed? + #[serde(default, rename = "admin.report")] + pub report: Option<bool>, } -/// Represents a new Push subscription +impl Alerts { + pub fn sign_up(&mut self, v: bool) -> &mut Self { + self.admin.sign_up = Some(v); + self + } + pub fn report(&mut self, v: bool) -> &mut Self { + self.admin.report = Some(v); + self + } + /// True if every field is `None`. + pub fn is_none(&self) -> bool { + self.admin.report.is_none() + && self.admin.sign_up.is_none() + && self.favourite.is_none() + && self.follow.is_none() + && self.follow_request.is_none() + && self.reblog.is_none() + && self.mention.is_none() + && self.status.is_none() + && self.poll.is_none() + && self.update.is_none() + } + + /// True if any field is set. + pub fn is_some(&self) -> bool { + !self.is_none() + } +} +/// Represents a subscription to the push streaming server. +/// +/// See also [the API documentation](https://docs.joinmastodon.org/entities/WebPushSubscription/) #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Subscription { - /// The `id` of the subscription + /// The ID of the Web Push subscription in the database. pub id: SubscriptionId, - /// The endpoint of the subscription + /// Where push alerts will be sent to. pub endpoint: String, - /// The server key of the subscription + /// The streaming server’s VAPID key. pub server_key: String, - /// The status of the alerts for this subscription - pub alerts: Option<Alerts>, + /// Which alerts should be delivered to the endpoint. + pub alerts: Alerts, } pub mod add_subscription { @@ -73,3 +143,38 @@ pub mod update_data { pub data: Data, } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_deserialize_subscription() { + let example = r#"{ + "id": "328183", + "endpoint": "https://yourdomain.example/listener", + "alerts": { + "follow": false, + "favourite": false, + "reblog": false, + "mention": true, + "poll": false + }, + "server_key": "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=" + }"#; + let subject: Subscription = serde_json::from_str(example).unwrap(); + assert_eq!(subject.id, SubscriptionId::new("328183")); + assert_eq!(subject.endpoint, "https://yourdomain.example/listener"); + assert!(!subject.alerts.follow.unwrap()); + assert!(!subject.alerts.favourite.unwrap()); + assert!(!subject.alerts.reblog.unwrap()); + assert!(subject.alerts.mention.unwrap()); + assert!(!subject.alerts.poll.unwrap()); + assert!(subject.alerts.admin.sign_up.is_none()); + assert!(subject.alerts.admin.report.is_none()); + assert!(subject.alerts.status.is_none()); + assert!(subject.alerts.follow_request.is_none()); + assert!(subject.alerts.update.is_none()); + assert_eq!(subject.server_key, "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M="); + } +} diff --git a/src/mastodon.rs b/src/mastodon.rs index d19e92c..749a5d8 100644 --- a/src/mastodon.rs +++ b/src/mastodon.rs @@ -291,7 +291,7 @@ impl Mastodon { /// Add a push notifications subscription pub async fn add_push_subscription(&self, request: &AddPushRequest) -> Result<Subscription> { let call_id = Uuid::new_v4(); - let request = request.build()?; + let request = request.build(); let url = &self.route("/api/v1/push/subscription"); debug!( url = url, method = stringify!($method), diff --git a/src/requests/push.rs b/src/requests/push.rs index 4392c08..18a6f6d 100644 --- a/src/requests/push.rs +++ b/src/requests/push.rs @@ -1,7 +1,6 @@ -use crate::{ - entities::push::{add_subscription, update_data}, - errors::Result, -}; +use mastodon_async_entities::push::Alerts; + +use crate::entities::push::{add_subscription, update_data}; /// Container for the key & auth strings for an AddPushRequest /// @@ -41,8 +40,12 @@ impl Keys { /// // Example /// /// ```no_run -/// use mastodon_async::{Mastodon, Data}; -/// use mastodon_async::requests::{AddPushRequest, Keys}; +/// use mastodon_async::{ +/// entities::push::AlertsBuilder, +/// Mastodon, +/// Data, +/// requests::{AddPushRequest, Keys} +/// }; /// /// tokio_test::block_on(async { /// let data = Data::default(); @@ -50,8 +53,13 @@ impl Keys { /// /// let keys = Keys::new("stahesuahoei293ise===", "tasecoa,nmeozka=="); /// let mut request = AddPushRequest::new("http://example.com/push/endpoint", &keys); -/// request.follow().reblog(); -/// +/// request.alerts( +/// AlertsBuilder::default() +/// .follow(true) +/// .reblog(true) +/// .build() +/// .unwrap() +/// ); /// client.add_push_subscription(&request).await.unwrap(); /// }); /// ``` @@ -62,10 +70,7 @@ pub struct AddPushRequest { p256dh: String, auth: String, - follow: Option<bool>, - favourite: Option<bool>, - reblog: Option<bool>, - mention: Option<bool>, + alerts: Alerts, } impl AddPushRequest { @@ -88,78 +93,15 @@ impl AddPushRequest { } } - /// A flag that indicates if you want follow notifications pushed - /// - /// // Example - /// ``` - /// use mastodon_async::requests::{AddPushRequest, Keys}; - /// let keys = Keys::new("abcdef===", "foobar=="); - /// let push_endpoint = "https://example.com/push/endpoint"; - /// let mut request = AddPushRequest::new(push_endpoint, &keys); - /// request.follow(); - /// ``` - pub fn follow(&mut self) -> &mut Self { - self.follow = Some(true); + /// Set the alerts which should be requested to be notified by this request. + pub fn alerts(&mut self, alerts: Alerts) -> &mut Self { + self.alerts = alerts; self } - /// A flag that indicates if you want favourite notifications pushed - /// - /// // Example - /// ``` - /// use mastodon_async::requests::{AddPushRequest, Keys}; - /// let keys = Keys::new("abcdef===", "foobar=="); - /// let push_endpoint = "https://example.com/push/endpoint"; - /// let mut request = AddPushRequest::new(push_endpoint, &keys); - /// request.favourite(); - /// ``` - pub fn favourite(&mut self) -> &mut Self { - self.favourite = Some(true); - self - } - - /// A flag that indicates if you want reblog notifications pushed - /// - /// // Example - /// ``` - /// use mastodon_async::requests::{AddPushRequest, Keys}; - /// let keys = Keys::new("abcdef===", "foobar=="); - /// let push_endpoint = "https://example.com/push/endpoint"; - /// let mut request = AddPushRequest::new(push_endpoint, &keys); - /// request.reblog(); - /// ``` - pub fn reblog(&mut self) -> &mut Self { - self.reblog = Some(true); - self - } - - /// A flag that indicates if you want mention notifications pushed - /// - /// // Example - /// ``` - /// use mastodon_async::requests::{AddPushRequest, Keys}; - /// let keys = Keys::new("abcdef===", "foobar=="); - /// let push_endpoint = "https://example.com/push/endpoint"; - /// let mut request = AddPushRequest::new(push_endpoint, &keys); - /// request.mention(); - /// ``` - pub fn mention(&mut self) -> &mut Self { - self.mention = Some(true); - self - } - - fn flags_present(&self) -> bool { - self.follow.is_some() - || self.favourite.is_some() - || self.reblog.is_some() - || self.mention.is_some() - } - - pub(crate) fn build(&self) -> Result<add_subscription::Form> { - use crate::entities::push::{ - add_subscription::{Data, Form, Keys, Subscription}, - Alerts, - }; + /// Build the form. + pub fn build(&self) -> add_subscription::Form { + use crate::entities::push::add_subscription::{Data, Form, Keys, Subscription}; let mut form = Form { subscription: Subscription { endpoint: self.endpoint.clone(), @@ -170,30 +112,14 @@ impl AddPushRequest { }, data: None, }; - if self.flags_present() { - let mut alerts = Alerts::default(); - - if let Some(follow) = self.follow { - alerts.follow = Some(follow); - } - - if let Some(favourite) = self.favourite { - alerts.favourite = Some(favourite); - } - - if let Some(reblog) = self.reblog { - alerts.reblog = Some(reblog); - } - - if let Some(mention) = self.mention { - alerts.mention = Some(mention); - } + if self.alerts.is_some() { form.data = Some(Data { - alerts: Some(alerts), + alerts: Some(self.alerts), }); } - Ok(form) + + form } } @@ -202,15 +128,24 @@ impl AddPushRequest { /// // Example /// /// ```no_run -/// use mastodon_async::{Mastodon, Data, requests::UpdatePushRequest}; +/// use mastodon_async::{ +/// entities::push::AlertsBuilder, +/// Mastodon, +/// Data, +/// requests::UpdatePushRequest +/// }; /// /// let data = Data::default(); /// let client = Mastodon::from(data); /// /// let mut request = UpdatePushRequest::new("foobar"); -/// request.follow(true) -/// .reblog(true); -/// +/// request.alerts( +/// AlertsBuilder::default() +/// .follow(true) +/// .reblog(true) +/// .build() +/// .unwrap() +/// ); /// tokio_test::block_on(async { /// client.update_push_data(&request).await.unwrap(); /// }); @@ -218,10 +153,7 @@ impl AddPushRequest { #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize)] pub struct UpdatePushRequest { id: String, - follow: Option<bool>, - favourite: Option<bool>, - reblog: Option<bool>, - mention: Option<bool>, + alerts: Alerts, } impl UpdatePushRequest { @@ -239,90 +171,24 @@ impl UpdatePushRequest { } } - /// A flag that indicates if you want follow notifications pushed - /// - /// // Example - /// ``` - /// let mut request = mastodon_async::requests::UpdatePushRequest::new("foobar"); - /// request.follow(true); - /// ``` - pub fn follow(&mut self, follow: bool) -> &mut Self { - self.follow = Some(follow); - self - } - - /// A flag that indicates if you want favourite notifications pushed - /// - /// // Example - /// ``` - /// let mut request = mastodon_async::requests::UpdatePushRequest::new("foobar"); - /// request.favourite(true); - /// ``` - pub fn favourite(&mut self, favourite: bool) -> &mut Self { - self.favourite = Some(favourite); - self - } - - /// A flag that indicates if you want reblog notifications pushed - /// - /// // Example - /// ``` - /// use mastodon_async::requests::UpdatePushRequest; - /// let mut request = UpdatePushRequest::new("foobar"); - /// request.reblog(true); - /// ``` - pub fn reblog(&mut self, reblog: bool) -> &mut Self { - self.reblog = Some(reblog); - self - } - - /// A flag that indicates if you want mention notifications pushed - /// - /// // Example - /// ``` - /// use mastodon_async::requests::UpdatePushRequest; - /// let mut request = UpdatePushRequest::new("foobar"); - /// request.mention(true); - /// ``` - pub fn mention(&mut self, mention: bool) -> &mut Self { - self.mention = Some(mention); + /// Set alerts which should be enabled or disabled by this update. + pub fn alerts(&mut self, alerts: Alerts) -> &mut Self { + self.alerts = alerts; self } - fn flags_present(&self) -> bool { - self.follow.is_some() - || self.favourite.is_some() - || self.reblog.is_some() - || self.mention.is_some() - } - - pub(crate) fn build(&self) -> update_data::Form { - use crate::entities::push::{ - update_data::{Data, Form}, - Alerts, - }; + /// Build the form from the update + pub fn build(&self) -> update_data::Form { + use crate::entities::push::update_data::{Data, Form}; let mut form = Form { id: self.id.clone(), ..Default::default() }; - if self.flags_present() { - let mut alerts = Alerts::default(); - if let Some(follow) = self.follow { - alerts.follow = Some(follow); - } - if let Some(favourite) = self.favourite { - alerts.favourite = Some(favourite); - } - if let Some(reblog) = self.reblog { - alerts.reblog = Some(reblog); - } - if let Some(mention) = self.mention { - alerts.mention = Some(mention); - } + if self.alerts.is_some() { form.data = Data { - alerts: Some(alerts), + alerts: Some(self.alerts), }; } form @@ -331,6 +197,8 @@ impl UpdatePushRequest { #[cfg(test)] mod tests { + use mastodon_async_entities::push::AlertsBuilder; + use super::*; use crate::entities::push::{add_subscription, update_data, Alerts}; @@ -357,118 +225,57 @@ mod tests { endpoint: "https://example.com/push/endpoint".to_string(), p256dh: "anetohias===".to_string(), auth: "oeatssah=".to_string(), - follow: None, - favourite: None, - reblog: None, - mention: None, - } - ); - } - #[test] - fn test_add_push_request_follow() { - let endpoint = "https://example.com/push/endpoint"; - let keys = Keys::new("anetohias===", "oeatssah="); - let mut req = AddPushRequest::new(endpoint, &keys); - req.follow(); - assert_eq!( - req, - AddPushRequest { - endpoint: "https://example.com/push/endpoint".to_string(), - p256dh: "anetohias===".to_string(), - auth: "oeatssah=".to_string(), - follow: Some(true), - favourite: None, - reblog: None, - mention: None, + ..Default::default() } ); } - #[test] - fn test_add_push_request_favourite() { - let endpoint = "https://example.com/push/endpoint"; - let keys = Keys::new("anetohias===", "oeatssah="); - let mut req = AddPushRequest::new(endpoint, &keys); - req.favourite(); - assert_eq!( - req, - AddPushRequest { - endpoint: "https://example.com/push/endpoint".to_string(), - p256dh: "anetohias===".to_string(), - auth: "oeatssah=".to_string(), - follow: None, - favourite: Some(true), - reblog: None, - mention: None, - } - ); - } - #[test] - fn test_add_push_request_reblog() { - let endpoint = "https://example.com/push/endpoint"; - let keys = Keys::new("anetohias===", "oeatssah="); - let mut req = AddPushRequest::new(endpoint, &keys); - req.reblog(); - assert_eq!( - req, - AddPushRequest { - endpoint: "https://example.com/push/endpoint".to_string(), - p256dh: "anetohias===".to_string(), - auth: "oeatssah=".to_string(), - follow: None, - favourite: None, - reblog: Some(true), - mention: None, - } - ); - } - #[test] - fn test_add_push_request_mention() { - let endpoint = "https://example.com/push/endpoint"; - let keys = Keys::new("anetohias===", "oeatssah="); - let mut req = AddPushRequest::new(endpoint, &keys); - req.mention(); - assert_eq!( - req, - AddPushRequest { - endpoint: "https://example.com/push/endpoint".to_string(), - p256dh: "anetohias===".to_string(), - auth: "oeatssah=".to_string(), - follow: None, - favourite: None, - reblog: None, - mention: Some(true), - } - ); - } - #[test] - fn test_add_push_request_build_no_flags() { - let endpoint = "https://example.com/push/endpoint"; - let keys = Keys::new("anetohias===", "oeatssah="); - let req = AddPushRequest::new(endpoint, &keys); - let form = req.build().expect("Couldn't build form"); - assert_eq!( - form, - add_subscription::Form { - subscription: add_subscription::Subscription { - endpoint: "https://example.com/push/endpoint".to_string(), - keys: add_subscription::Keys { + macro_rules! alerts_builder_test { + ($name:ident, $set:ident $(; $rest_names:ident, $rest_set:ident)*;) => { + #[test] + fn $name() { + let endpoint = "https://example.com/push/endpoint"; + let keys = Keys::new("anetohias===", "oeatssah="); + let mut req = AddPushRequest::new(endpoint, &keys); + req.alerts(AlertsBuilder::default().$set(true).build().unwrap()); + assert_eq!( + req, + AddPushRequest { + endpoint: "https://example.com/push/endpoint".to_string(), p256dh: "anetohias===".to_string(), auth: "oeatssah=".to_string(), - }, - }, - data: None, + alerts: Alerts { + $set: Some(true), + ..Default::default() + } + } + ); } - ); + + alerts_builder_test!($($rest_names, $rest_set;)*); + }; + () => {}; } + alerts_builder_test!( + test_add_push_request_follow, follow; + test_add_push_request_favourite, favourite; + test_add_push_request_reblog, reblog; + test_add_push_request_mention, mention; + ); #[test] fn test_add_push_request_build() { let endpoint = "https://example.com/push/endpoint"; let keys = Keys::new("anetohias===", "oeatssah="); let mut req = AddPushRequest::new(endpoint, &keys); - req.follow().reblog(); - let form = req.build().expect("Couldn't build form"); + req.alerts( + AlertsBuilder::default() + .follow(true) + .reblog(true) + .build() + .unwrap(), + ); + let form = req.build(); assert_eq!( form, add_subscription::Form { @@ -482,9 +289,8 @@ mod tests { data: Some(add_subscription::Data { alerts: Some(Alerts { follow: Some(true), - favourite: None, reblog: Some(true), - mention: None, + ..Default::default() }), }), } @@ -498,74 +304,42 @@ mod tests { req, UpdatePushRequest { id: "some-id".to_string(), - follow: None, - favourite: None, - reblog: None, - mention: None, + ..Default::default() } ); } - #[test] - fn test_update_push_request_follow() { - let mut req = UpdatePushRequest::new("some-id"); - req.follow(true); - assert_eq!( - req, - UpdatePushRequest { - id: "some-id".to_string(), - follow: Some(true), - favourite: None, - reblog: None, - mention: None, - } - ); - } - #[test] - fn test_update_push_request_favourite() { - let mut req = UpdatePushRequest::new("some-id"); - req.favourite(true); - assert_eq!( - req, - UpdatePushRequest { - id: "some-id".to_string(), - follow: None, - favourite: Some(true), - reblog: None, - mention: None, + macro_rules! test_update_push_request { + ($name:ident, $set:ident $(; $rest_names:ident, $rest_set:ident)*;) => { + #[test] + fn $name() { + let mut req = UpdatePushRequest::new("some-id"); + req.alerts(AlertsBuilder::default().$set(true).build().unwrap()); + assert_eq!( + req, + UpdatePushRequest { + id: "some-id".to_string(), + alerts: Alerts { + $set: Some(true), + ..Default::default() + } + } + ); } - ); - } - #[test] - fn test_update_push_request_reblog() { - let mut req = UpdatePushRequest::new("some-id"); - req.reblog(true); - assert_eq!( - req, - UpdatePushRequest { - id: "some-id".to_string(), - follow: None, - favourite: None, - reblog: Some(true), - mention: None, - } - ); + + test_update_push_request!($($rest_names, $rest_set;)*); + }; + () => {} } - #[test] - fn test_update_push_request_mention() { - let mut req = UpdatePushRequest::new("some-id"); - req.mention(true); - assert_eq!( - req, - UpdatePushRequest { - id: "some-id".to_string(), - follow: None, - favourite: None, - reblog: None, - mention: Some(true), - } - ); + + test_update_push_request! { + test_update_push_request_follow, follow; + test_update_push_request_favourite, favourite; + test_update_push_request_reblog, reblog; + test_update_push_request_mention, mention; + } + #[test] fn test_update_push_request_build_no_flags() { let req = UpdatePushRequest::new("some-id"); @@ -582,7 +356,10 @@ mod tests { #[test] fn test_update_push_request_build() { let mut req = UpdatePushRequest::new("some-id"); - req.favourite(false); + req.alerts(Alerts { + favourite: Some(false), + ..Default::default() + }); let form = req.build(); assert_eq!( form, @@ -590,10 +367,8 @@ mod tests { id: "some-id".to_string(), data: update_data::Data { alerts: Some(Alerts { - follow: None, favourite: Some(false), - reblog: None, - mention: None, + ..Default::default() }), }, } |