From 069246e247c64a61422c4b03287333913d0d7923 Mon Sep 17 00:00:00 2001 From: adnanjpg Date: Mon, 22 Jan 2024 16:10:52 +0300 Subject: [PATCH 1/7] feat: build new api --- a.sh | 1 + examples/simple_sender.rs | 37 ++-- src/android/android_config.rs | 73 ++++++++ src/android/android_fcm_options.rs | 21 +++ src/android/android_message_priority.rs | 10 + src/android/android_notification.rs | 234 ++++++++++++++++++++++++ src/android/color.rs | 43 +++++ src/android/light_settings.rs | 43 +++++ src/android/mod.rs | 229 +---------------------- src/android/notification_priority.rs | 14 ++ src/android/visibility.rs | 12 ++ src/apns/apns_config.rs | 37 ++++ src/apns/apns_fcm_options.rs | 29 +++ src/apns/mod.rs | 30 +-- src/client/mod.rs | 24 ++- src/lib.rs | 1 + src/message/fcm_options.rs | 21 +++ src/message/mod.rs | 128 ++++--------- src/message/target.rs | 9 + src/message/tests.rs | 136 ++++++++++---- src/notification/mod.rs | 45 ++--- src/notification/tests.rs | 50 +---- src/web/mod.rs | 35 +--- src/web/webpush_config.rs | 44 +++++ src/web/webpush_fcm_options.rs | 30 +++ 25 files changed, 838 insertions(+), 498 deletions(-) create mode 100644 a.sh create mode 100644 src/android/android_config.rs create mode 100644 src/android/android_fcm_options.rs create mode 100644 src/android/android_message_priority.rs create mode 100644 src/android/android_notification.rs create mode 100644 src/android/color.rs create mode 100644 src/android/light_settings.rs create mode 100644 src/android/notification_priority.rs create mode 100644 src/android/visibility.rs create mode 100644 src/apns/apns_config.rs create mode 100644 src/apns/apns_fcm_options.rs create mode 100644 src/message/fcm_options.rs create mode 100644 src/message/target.rs create mode 100644 src/web/webpush_config.rs create mode 100644 src/web/webpush_fcm_options.rs diff --git a/a.sh b/a.sh new file mode 100644 index 000000000..5d89ab970 --- /dev/null +++ b/a.sh @@ -0,0 +1 @@ +cargo run --example simple_sender -- -t fP0EXs_HQ4Gj1sE5xBP6LP:APA91bGxDOk4GzvIvvGFPa6kDWOOHm2zYA3tUN1jYwErSBq_s27NioYjYWfUpgUW3_LIsreSw1pbtTjEpCK_cKR6AHxo1PGZ1Yd3S8lyX148I_LgKaTNYorG2QtkcwfB9Nm5yKGJwLaG \ No newline at end of file diff --git a/examples/simple_sender.rs b/examples/simple_sender.rs index 6a634b801..fb7cedc28 100644 --- a/examples/simple_sender.rs +++ b/examples/simple_sender.rs @@ -1,11 +1,8 @@ -use argparse::{ArgumentParser, Store}; -use fcm::{Client, MessageBuilder, Target}; -use serde::Serialize; +// cargo run --example simple_sender -- -t -#[derive(Serialize)] -struct CustomData { - message: &'static str, -} +use argparse::{ArgumentParser, Store}; +use fcm::{fcm_options::FcmOptions, target::Target, Client, Message, Notification}; +use serde_json::json; #[tokio::main] async fn main() -> Result<(), Box> { @@ -22,12 +19,28 @@ async fn main() -> Result<(), Box> { } let client = Client::new(); - let data = CustomData { message: "howdy" }; - - let mut builder = MessageBuilder::new(Target::Token(device_token)); - builder.data(&data)?; - let response = client.send(builder.finalize()).await?; + let data = json!({ + "key": "value", + }); + + let builder = Message { + data: Some(data), + notification: Some(Notification { + title: Some("Hello".to_string()), + body: Some(format!("it's {}", chrono::Utc::now())), + image: None, + }), + target: Target::Token(device_token), + android: None, + webpush: None, + apns: None, + fcm_options: Some(FcmOptions { + analytics_label: "analytics_label".to_string(), + }), + }; + + let response = client.send(builder).await?; println!("Sent: {:?}", response); Ok(()) diff --git a/src/android/android_config.rs b/src/android/android_config.rs new file mode 100644 index 000000000..800a840e5 --- /dev/null +++ b/src/android/android_config.rs @@ -0,0 +1,73 @@ +use serde::Serialize; +use serde_json::Value; + +use super::{ + android_fcm_options::{AndroidFcmOptions, AndroidFcmOptionsInternal}, + android_message_priority::AndroidMessagePriority, + android_notification::{AndroidNotification, AndroidNotificationInternal}, +}; + +#[derive(Serialize, Debug)] +//https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidconfig +pub struct AndroidConfigInternal { + // An identifier of a group of messages that can be collapsed, so that only the last message gets + // sent when delivery can be resumed. + #[serde(skip_serializing_if = "Option::is_none")] + collapse_key: Option, + + // Message priority. + #[serde(skip_serializing_if = "Option::is_none")] + priority: Option, + + // How long (in seconds) the message should be kept in FCM storage if the device is offline. + // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration + #[serde(skip_serializing_if = "Option::is_none")] + ttl: Option, + + // Package name of the application where the registration token must match in order to receive the message. + #[serde(skip_serializing_if = "Option::is_none")] + restricted_package_name: Option, + + // Arbitrary key/value payload. + #[serde(skip_serializing_if = "Option::is_none")] + data: Option, + + // Notification to send to android devices. + #[serde(skip_serializing_if = "Option::is_none")] + notification: Option, + + // Options for features provided by the FCM SDK for Android. + #[serde(skip_serializing_if = "Option::is_none")] + fcm_options: Option, + + // If set to true, messages will be allowed to be delivered to the app while the device is in direct boot mode. + #[serde(skip_serializing_if = "Option::is_none")] + direct_boot_ok: Option, +} + +#[derive(Debug)] +pub struct AndroidConfig { + pub collapse_key: Option, + pub priority: Option, + pub ttl: Option, + pub restricted_package_name: Option, + pub data: Option, + pub notification: Option, + pub fcm_options: Option, + pub direct_boot_ok: Option, +} + +impl AndroidConfig { + pub fn finalize(self) -> AndroidConfigInternal { + AndroidConfigInternal { + collapse_key: self.collapse_key, + priority: self.priority, + ttl: self.ttl, + restricted_package_name: self.restricted_package_name, + data: self.data, + notification: self.notification.map(|n| n.finalize()), + fcm_options: self.fcm_options.map(|f| f.finalize()), + direct_boot_ok: self.direct_boot_ok, + } + } +} diff --git a/src/android/android_fcm_options.rs b/src/android/android_fcm_options.rs new file mode 100644 index 000000000..81a32de7a --- /dev/null +++ b/src/android/android_fcm_options.rs @@ -0,0 +1,21 @@ +use serde::Serialize; + +#[derive(Serialize, Debug)] +//https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidconfig +pub struct AndroidFcmOptionsInternal { + // Label associated with the message's analytics data. + analytics_label: String, +} + +#[derive(Debug)] +pub struct AndroidFcmOptions { + pub analytics_label: String, +} + +impl AndroidFcmOptions { + pub fn finalize(self) -> AndroidFcmOptionsInternal { + AndroidFcmOptionsInternal { + analytics_label: self.analytics_label, + } + } +} diff --git a/src/android/android_message_priority.rs b/src/android/android_message_priority.rs new file mode 100644 index 000000000..046466294 --- /dev/null +++ b/src/android/android_message_priority.rs @@ -0,0 +1,10 @@ +use serde::Serialize; + +#[allow(dead_code)] +#[derive(Serialize, Debug)] +#[serde(rename_all = "UPPERCASE")] +// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidmessagepriority +pub enum AndroidMessagePriority { + Normal, + High, +} diff --git a/src/android/android_notification.rs b/src/android/android_notification.rs new file mode 100644 index 000000000..0656c13fe --- /dev/null +++ b/src/android/android_notification.rs @@ -0,0 +1,234 @@ +use serde::Serialize; + +use super::{ + light_settings::{LightSettings, LightSettingsInternal}, + notification_priority::NotificationPriority, + visibility::Visibility, +}; + +#[derive(Serialize, Debug)] +// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidnotification +pub struct AndroidNotificationInternal { + // The notification's title. + #[serde(skip_serializing_if = "Option::is_none")] + title: Option, + + // The notification's body text. + #[serde(skip_serializing_if = "Option::is_none")] + body: Option, + + // The notification's icon. + #[serde(skip_serializing_if = "Option::is_none")] + icon: Option, + + // The notification's icon color, expressed in #rrggbb format. + #[serde(skip_serializing_if = "Option::is_none")] + color: Option, + + // The sound to play when the device receives the notification. + #[serde(skip_serializing_if = "Option::is_none")] + sound: Option, + + // Identifier used to replace existing notifications in the notification drawer. + #[serde(skip_serializing_if = "Option::is_none")] + tag: Option, + + // The action associated with a user click on the notification. + #[serde(skip_serializing_if = "Option::is_none")] + click_action: Option, + + // The key to the body string in the app's string resources to use to localize the body text to the user's + // current localization. + #[serde(skip_serializing_if = "Option::is_none")] + body_loc_key: Option, + + // Variable string values to be used in place of the format specifiers in body_loc_key to use to localize the + // body text to the user's current localization. + #[serde(skip_serializing_if = "Option::is_none")] + body_loc_args: Option>, + + // The key to the title string in the app's string resources to use to localize the title text to the user's + // current localization. + #[serde(skip_serializing_if = "Option::is_none")] + title_loc_key: Option, + + // Variable string values to be used in place of the format specifiers in title_loc_key to use to localize the + // title text to the user's current localization. + #[serde(skip_serializing_if = "Option::is_none")] + title_loc_args: Option>, + + // The notification's channel id (new in Android O). + #[serde(skip_serializing_if = "Option::is_none")] + channel_id: Option, + + // Sets the "ticker" text, which is sent to accessibility services. + #[serde(skip_serializing_if = "Option::is_none")] + ticker: Option, + + // When set to false or unset, the notification is automatically dismissed when the user clicks it in the panel. + #[serde(skip_serializing_if = "Option::is_none")] + sticky: Option, + + // Set the time that the event in the notification occurred. Notifications in the panel are sorted by this time. + // Timestamp format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Timestamp + #[serde(skip_serializing_if = "Option::is_none")] + event_time: Option, + + // Set whether or not this notification is relevant only to the current device. + #[serde(skip_serializing_if = "Option::is_none")] + local_only: Option, + + // Set the relative priority for this notification. + #[serde(skip_serializing_if = "Option::is_none")] + notification_priority: Option, + + // If set to true, use the Android framework's default sound for the notification. + #[serde(skip_serializing_if = "Option::is_none")] + default_sound: Option, + + // If set to true, use the Android framework's default vibrate pattern for the notification. + #[serde(skip_serializing_if = "Option::is_none")] + default_vibrate_timings: Option, + + // If set to true, use the Android framework's default LED light settings for the notification. + #[serde(skip_serializing_if = "Option::is_none")] + default_light_settings: Option, + + // Set the vibration pattern to use + // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration + #[serde(skip_serializing_if = "Option::is_none")] + vibrate_timings: Option>, + + // Set the Notification.visibility of the notification. + #[serde(skip_serializing_if = "Option::is_none")] + visibility: Option, + + // Sets the number of items this notification represents. + #[serde(skip_serializing_if = "Option::is_none")] + notification_count: Option, + + // Settings to control the notification's LED blinking rate and color if LED is available on the device. + #[serde(skip_serializing_if = "Option::is_none")] + light_settings: Option, + + // Contains the URL of an image that is going to be displayed in a notification. + #[serde(skip_serializing_if = "Option::is_none")] + image: Option, +} + +#[derive(Debug)] +// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidnotification +pub struct AndroidNotification { + // The notification's title. + pub title: Option, + + // The notification's body text. + pub body: Option, + + // The notification's icon. + pub icon: Option, + + // The notification's icon color, expressed in #rrggbb format. + pub color: Option, + + // The sound to play when the device receives the notification. + pub sound: Option, + + // Identifier used to replace existing notifications in the notification drawer. + pub tag: Option, + + // The action associated with a user click on the notification. + pub click_action: Option, + + // The key to the body string in the app's string resources to use to localize the body text to the user's + // current localization. + pub body_loc_key: Option, + + // Variable string values to be used in place of the format specifiers in body_loc_key to use to localize the + // body text to the user's current localization. + pub body_loc_args: Option>, + + // The key to the title string in the app's string resources to use to localize the title text to the user's + // current localization. + pub title_loc_key: Option, + + // Variable string values to be used in place of the format specifiers in title_loc_key to use to localize the + // title text to the user's current localization. + pub title_loc_args: Option>, + + // The notification's channel id (new in Android O). + pub channel_id: Option, + + // Sets the "ticker" text, which is sent to accessibility services. + pub ticker: Option, + + // When set to false or unset, the notification is automatically dismissed when the user clicks it in the panel. + pub sticky: Option, + + // Set the time that the event in the notification occurred. Notifications in the panel are sorted by this time. + // Timestamp format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Timestamp + pub event_time: Option, + + // Set whether or not this notification is relevant only to the current device. + pub local_only: Option, + + // Set the relative priority for this notification. + pub notification_priority: Option, + + // If set to true, use the Android framework's default sound for the notification. + pub default_sound: Option, + + // If set to true, use the Android framework's default vibrate pattern for the notification. + pub default_vibrate_timings: Option, + + // If set to true, use the Android framework's default LED light settings for the notification. + pub default_light_settings: Option, + + // Set the vibration pattern to use + // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration + pub vibrate_timings: Option>, + + // Set the Notification.visibility of the notification. + pub visibility: Option, + + // Sets the number of items this notification represents. + pub notification_count: Option, + + // Settings to control the notification's LED blinking rate and color if LED is available on the device. + pub light_settings: Option, + + // Contains the URL of an image that is going to be displayed in a notification. + pub image: Option, +} + +impl AndroidNotification { + pub fn finalize(self) -> AndroidNotificationInternal { + AndroidNotificationInternal { + title: self.title, + body: self.body, + icon: self.icon, + color: self.color, + sound: self.sound, + tag: self.tag, + click_action: self.click_action, + body_loc_key: self.body_loc_key, + body_loc_args: self.body_loc_args, + title_loc_key: self.title_loc_key, + title_loc_args: self.title_loc_args, + channel_id: self.channel_id, + ticker: self.ticker, + sticky: self.sticky, + event_time: self.event_time, + local_only: self.local_only, + notification_priority: self.notification_priority, + default_sound: self.default_sound, + default_vibrate_timings: self.default_vibrate_timings, + default_light_settings: self.default_light_settings, + vibrate_timings: self.vibrate_timings, + visibility: self.visibility, + notification_count: self.notification_count, + light_settings: self.light_settings.map(|x| x.finalize()), + image: self.image, + } + } +} diff --git a/src/android/color.rs b/src/android/color.rs new file mode 100644 index 000000000..9520607f3 --- /dev/null +++ b/src/android/color.rs @@ -0,0 +1,43 @@ +use serde::Serialize; + +#[derive(Serialize, Debug)] +// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#Color +pub struct ColorInternal { + // The amount of red in the color as a value in the interval [0, 1]. + red: f32, + + // The amount of green in the color as a value in the interval [0, 1]. + green: f32, + + // The amount of blue in the color as a value in the interval [0, 1]. + blue: f32, + + // The fraction of this color that should be applied to the pixel. + alpha: f32, +} + +#[derive(Debug)] +pub struct Color { + // The amount of red in the color as a value in the interval [0, 1]. + pub red: f32, + + // The amount of green in the color as a value in the interval [0, 1]. + pub green: f32, + + // The amount of blue in the color as a value in the interval [0, 1]. + pub blue: f32, + + // The fraction of this color that should be applied to the pixel. + pub alpha: f32, +} + +impl Color { + pub fn finalize(self) -> ColorInternal { + ColorInternal { + red: self.red, + green: self.green, + blue: self.blue, + alpha: self.alpha, + } + } +} diff --git a/src/android/light_settings.rs b/src/android/light_settings.rs new file mode 100644 index 000000000..43cd7b01b --- /dev/null +++ b/src/android/light_settings.rs @@ -0,0 +1,43 @@ +use serde::Serialize; + +use super::color::{Color, ColorInternal}; + +#[derive(Serialize, Debug)] +// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#LightSettings +pub struct LightSettingsInternal { + // Set color of the LED with google.type.Color. + color: ColorInternal, + + // Along with light_off_duration, define the blink rate of LED flashes + // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration + light_on_duration: String, + + // Along with light_on_duration, define the blink rate of LED flashes. + // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration + light_off_duration: String, +} + +#[derive(Debug)] +// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#LightSettings +pub struct LightSettings { + // Set color of the LED with google.type.Color. + pub color: Color, + + // Along with light_off_duration, define the blink rate of LED flashes + // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration + pub light_on_duration: String, + + // Along with light_on_duration, define the blink rate of LED flashes. + // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration + pub light_off_duration: String, +} + +impl LightSettings { + pub fn finalize(self) -> LightSettingsInternal { + LightSettingsInternal { + color: self.color.finalize(), + light_on_duration: self.light_on_duration, + light_off_duration: self.light_off_duration, + } + } +} diff --git a/src/android/mod.rs b/src/android/mod.rs index a0f1778a0..b4ba2e81b 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -1,221 +1,8 @@ -use serde::Serialize; -use serde_json::Value; - -#[derive(Serialize, Debug)] -//https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidconfig -pub struct AndroidConfig { - // An identifier of a group of messages that can be collapsed, so that only the last message gets - // sent when delivery can be resumed. - #[serde(skip_serializing_if = "Option::is_none")] - collapse_key: Option, - - // Message priority. - #[serde(skip_serializing_if = "Option::is_none")] - priority: Option, - - // How long (in seconds) the message should be kept in FCM storage if the device is offline. - // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration - #[serde(skip_serializing_if = "Option::is_none")] - ttl: Option, - - // Package name of the application where the registration token must match in order to receive the message. - #[serde(skip_serializing_if = "Option::is_none")] - restricted_package_name: Option, - - // Arbitrary key/value payload. - #[serde(skip_serializing_if = "Option::is_none")] - data: Option, - - // Notification to send to android devices. - #[serde(skip_serializing_if = "Option::is_none")] - notification: Option, - - // Options for features provided by the FCM SDK for Android. - #[serde(skip_serializing_if = "Option::is_none")] - fcm_options: Option, - - // If set to true, messages will be allowed to be delivered to the app while the device is in direct boot mode. - #[serde(skip_serializing_if = "Option::is_none")] - direct_boot_ok: Option, -} - -#[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#Color -pub struct Color { - // The amount of red in the color as a value in the interval [0, 1]. - red: f32, - - // The amount of green in the color as a value in the interval [0, 1]. - green: f32, - - // The amount of blue in the color as a value in the interval [0, 1]. - blue: f32, - - // The fraction of this color that should be applied to the pixel. - alpha: f32, -} - -#[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#LightSettings -pub struct LightSettings { - // Set color of the LED with google.type.Color. - color: Color, - - // Along with light_off_duration, define the blink rate of LED flashes - // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration - light_on_duration: String, - - // Along with light_on_duration, define the blink rate of LED flashes. - // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration - light_off_duration: String, -} - -#[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidnotification -pub struct AndroidNotification { - // The notification's title. - #[serde(skip_serializing_if = "Option::is_none")] - title: Option, - - // The notification's body text. - #[serde(skip_serializing_if = "Option::is_none")] - body: Option, - - // The notification's icon. - #[serde(skip_serializing_if = "Option::is_none")] - icon: Option, - - // The notification's icon color, expressed in #rrggbb format. - #[serde(skip_serializing_if = "Option::is_none")] - color: Option, - - // The sound to play when the device receives the notification. - #[serde(skip_serializing_if = "Option::is_none")] - sound: Option, - - // Identifier used to replace existing notifications in the notification drawer. - #[serde(skip_serializing_if = "Option::is_none")] - tag: Option, - - // The action associated with a user click on the notification. - #[serde(skip_serializing_if = "Option::is_none")] - click_action: Option, - - // The key to the body string in the app's string resources to use to localize the body text to the user's - // current localization. - #[serde(skip_serializing_if = "Option::is_none")] - body_loc_key: Option, - - // Variable string values to be used in place of the format specifiers in body_loc_key to use to localize the - // body text to the user's current localization. - #[serde(skip_serializing_if = "Option::is_none")] - body_loc_args: Option>, - - // The key to the title string in the app's string resources to use to localize the title text to the user's - // current localization. - #[serde(skip_serializing_if = "Option::is_none")] - title_loc_key: Option, - - // Variable string values to be used in place of the format specifiers in title_loc_key to use to localize the - // title text to the user's current localization. - #[serde(skip_serializing_if = "Option::is_none")] - title_loc_args: Option>, - - // The notification's channel id (new in Android O). - #[serde(skip_serializing_if = "Option::is_none")] - channel_id: Option, - - // Sets the "ticker" text, which is sent to accessibility services. - #[serde(skip_serializing_if = "Option::is_none")] - ticker: Option, - - // When set to false or unset, the notification is automatically dismissed when the user clicks it in the panel. - #[serde(skip_serializing_if = "Option::is_none")] - sticky: Option, - - // Set the time that the event in the notification occurred. Notifications in the panel are sorted by this time. - // Timestamp format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Timestamp - #[serde(skip_serializing_if = "Option::is_none")] - event_time: Option, - - // Set whether or not this notification is relevant only to the current device. - #[serde(skip_serializing_if = "Option::is_none")] - local_only: Option, - - // Set the relative priority for this notification. - #[serde(skip_serializing_if = "Option::is_none")] - notification_priority: Option, - - // If set to true, use the Android framework's default sound for the notification. - #[serde(skip_serializing_if = "Option::is_none")] - default_sound: Option, - - // If set to true, use the Android framework's default vibrate pattern for the notification. - #[serde(skip_serializing_if = "Option::is_none")] - default_vibrate_timings: Option, - - // If set to true, use the Android framework's default LED light settings for the notification. - #[serde(skip_serializing_if = "Option::is_none")] - default_light_settings: Option, - - // Set the vibration pattern to use - // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration - #[serde(skip_serializing_if = "Option::is_none")] - vibrate_timings: Option>, - - // Set the Notification.visibility of the notification. - #[serde(skip_serializing_if = "Option::is_none")] - visibility: Option, - - // Sets the number of items this notification represents. - #[serde(skip_serializing_if = "Option::is_none")] - notification_count: Option, - - // Settings to control the notification's LED blinking rate and color if LED is available on the device. - #[serde(skip_serializing_if = "Option::is_none")] - light_settings: Option, - - // Contains the URL of an image that is going to be displayed in a notification. - #[serde(skip_serializing_if = "Option::is_none")] - image: Option, -} - -#[derive(Serialize, Debug)] -//https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidconfig -pub struct AndroidFcmOptions { - // Label associated with the message's analytics data. - analytics_label: String, -} - -#[allow(dead_code)] -#[derive(Serialize, Debug)] -#[serde(rename_all = "UPPERCASE")] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidmessagepriority -pub enum AndroidMessagePriority { - Normal, - High, -} - -#[allow(dead_code)] -#[derive(Serialize, Debug)] -#[serde(rename_all = "UPPERCASE")] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#notificationpriority -pub enum NotificationPriority { - PriorityUnspecified, - PriorityMin, - PriorityLow, - PriorityDefault, - PriorityHigh, - PriorityMax, -} - -#[allow(dead_code)] -#[derive(Serialize, Debug)] -#[serde(rename_all = "UPPERCASE")] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#visibility -pub enum Visibility { - VisibilityUnspecified, - Private, - Public, - Secret, -} +pub mod android_config; +pub mod android_fcm_options; +pub mod android_message_priority; +pub mod android_notification; +pub mod color; +pub mod light_settings; +pub mod notification_priority; +pub mod visibility; diff --git a/src/android/notification_priority.rs b/src/android/notification_priority.rs new file mode 100644 index 000000000..bb1bdbf17 --- /dev/null +++ b/src/android/notification_priority.rs @@ -0,0 +1,14 @@ +use serde::Serialize; + +#[allow(dead_code)] +#[derive(Serialize, Debug)] +#[serde(rename_all = "UPPERCASE")] +// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#notificationpriority +pub enum NotificationPriority { + PriorityUnspecified, + PriorityMin, + PriorityLow, + PriorityDefault, + PriorityHigh, + PriorityMax, +} diff --git a/src/android/visibility.rs b/src/android/visibility.rs new file mode 100644 index 000000000..cd8b56dd4 --- /dev/null +++ b/src/android/visibility.rs @@ -0,0 +1,12 @@ +use serde::Serialize; + +#[allow(dead_code)] +#[derive(Serialize, Debug)] +#[serde(rename_all = "UPPERCASE")] +// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#visibility +pub enum Visibility { + VisibilityUnspecified, + Private, + Public, + Secret, +} diff --git a/src/apns/apns_config.rs b/src/apns/apns_config.rs new file mode 100644 index 000000000..6d2b6b8e8 --- /dev/null +++ b/src/apns/apns_config.rs @@ -0,0 +1,37 @@ +use serde::Serialize; +use serde_json::Value; + +use super::apns_fcm_options::{ApnsFcmOptions, ApnsFcmOptionsInternal}; + +#[derive(Serialize, Debug)] +// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#apnsconfig +pub struct ApnsConfigInternal { + // HTTP request headers defined in Apple Push Notification Service. + #[serde(skip_serializing_if = "Option::is_none")] + headers: Option, + + // APNs payload as a JSON object, including both aps dictionary and custom payload. + #[serde(skip_serializing_if = "Option::is_none")] + payload: Option, + + // Options for features provided by the FCM SDK for iOS. + #[serde(skip_serializing_if = "Option::is_none")] + fcm_options: Option, +} + +#[derive(Debug)] +pub struct ApnsConfig { + pub headers: Option, + pub payload: Option, + pub fcm_options: Option, +} + +impl ApnsConfig { + pub fn finalize(self) -> ApnsConfigInternal { + ApnsConfigInternal { + headers: self.headers, + payload: self.payload, + fcm_options: self.fcm_options.map(|fcm_options| fcm_options.finalize()), + } + } +} diff --git a/src/apns/apns_fcm_options.rs b/src/apns/apns_fcm_options.rs new file mode 100644 index 000000000..841fa6412 --- /dev/null +++ b/src/apns/apns_fcm_options.rs @@ -0,0 +1,29 @@ +use serde::Serialize; + +#[derive(Serialize, Debug)] +// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#apnsfcmoptions +pub struct ApnsFcmOptionsInternal { + // Label associated with the message's analytics data. + analytics_label: Option, + + // Contains the URL of an image that is going to be displayed in a notification. + image: Option, +} + +#[derive(Debug)] +pub struct ApnsFcmOptions { + // Label associated with the message's analytics data. + pub analytics_label: Option, + + // Contains the URL of an image that is going to be displayed in a notification. + pub image: Option, +} + +impl ApnsFcmOptions { + pub fn finalize(self) -> ApnsFcmOptionsInternal { + ApnsFcmOptionsInternal { + analytics_label: self.analytics_label, + image: self.image, + } + } +} diff --git a/src/apns/mod.rs b/src/apns/mod.rs index bfd5a2f71..8cd464e28 100644 --- a/src/apns/mod.rs +++ b/src/apns/mod.rs @@ -1,28 +1,2 @@ -use serde::Serialize; -use serde_json::Value; - -#[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#apnsconfig -pub struct ApnsConfig { - // HTTP request headers defined in Apple Push Notification Service. - #[serde(skip_serializing_if = "Option::is_none")] - headers: Option, - - // APNs payload as a JSON object, including both aps dictionary and custom payload. - #[serde(skip_serializing_if = "Option::is_none")] - payload: Option, - - // Options for features provided by the FCM SDK for iOS. - #[serde(skip_serializing_if = "Option::is_none")] - fcm_options: Option, -} - -#[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#apnsfcmoptions -pub struct ApnsFcmOptions { - // Label associated with the message's analytics data. - analytics_label: String, - - // Contains the URL of an image that is going to be displayed in a notification. - image: String, -} +pub mod apns_config; +pub mod apns_fcm_options; diff --git a/src/client/mod.rs b/src/client/mod.rs index 788fda83b..b82ca8821 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,7 +1,7 @@ pub(crate) mod response; use crate::client::response::{ErrorReason, FcmError, FcmResponse, RetryAfter}; -use crate::Message; +use crate::{Message, MessageInternal}; use gauth::serv_account::ServiceAccount; use reqwest::header::RETRY_AFTER; use reqwest::{Body, StatusCode}; @@ -18,9 +18,17 @@ impl Default for Client { } } -#[derive(Serialize, Debug)] -pub struct MessageWrapper { - pub message: Message, +// will be used to wrap the message in a "message" field +#[derive(Serialize)] +struct MessageWrapper<'a> { + #[serde(rename = "message")] + message: &'a MessageInternal, +} + +impl MessageWrapper<'_> { + fn new(message: &MessageInternal) -> MessageWrapper { + MessageWrapper { message } + } } impl Client { @@ -107,7 +115,8 @@ impl Client { } pub async fn send(&self, message: Message) -> Result { - let wrapper = MessageWrapper { message }; + let fin = message.finalize(); + let wrapper = MessageWrapper::new(&fin); let payload = serde_json::to_vec(&wrapper).unwrap(); let project_id = match self.get_project_id() { @@ -131,6 +140,11 @@ impl Client { .body(Body::from(payload)) .build()?; + // print the body + let body = request.body().unwrap(); + let body = std::str::from_utf8(body.as_bytes().unwrap()).unwrap(); + println!("body: {}", body); + let response = self.http_client.execute(request).await?; let response_status = response.status(); diff --git a/src/lib.rs b/src/lib.rs index c44d0a4ba..58461c143 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,7 @@ mod message; pub use crate::message::*; + mod notification; pub use crate::notification::*; mod android; diff --git a/src/message/fcm_options.rs b/src/message/fcm_options.rs new file mode 100644 index 000000000..8aa0cc7dc --- /dev/null +++ b/src/message/fcm_options.rs @@ -0,0 +1,21 @@ +use serde::Serialize; + +#[derive(Serialize, Debug)] +// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#fcmoptions +pub struct FcmOptionsInternal { + // Label associated with the message's analytics data. + analytics_label: String, +} + +#[derive(Debug)] +pub struct FcmOptions { + pub analytics_label: String, +} + +impl FcmOptions { + pub fn finalize(self) -> FcmOptionsInternal { + FcmOptionsInternal { + analytics_label: self.analytics_label, + } + } +} diff --git a/src/message/mod.rs b/src/message/mod.rs index 988073154..9abb1bcaf 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -1,21 +1,25 @@ -use crate::android::AndroidConfig; -use crate::apns::ApnsConfig; -use crate::web::WebpushConfig; -use crate::Notification; +pub mod fcm_options; +pub mod target; +#[cfg(test)] +mod tests; + use serde::ser::SerializeMap; -use serde::{Serialize, Serializer}; +use serde::Serialize; +use serde::Serializer; use serde_json::Value; -#[cfg(test)] -mod tests; +use crate::android::android_config::AndroidConfig; +use crate::android::android_config::AndroidConfigInternal; +use crate::apns::apns_config::ApnsConfig; +use crate::apns::apns_config::ApnsConfigInternal; +use crate::fcm_options::FcmOptions; +use crate::fcm_options::FcmOptionsInternal; +use crate::notification::Notification; +use crate::notification::NotificationInternal; +use crate::web::webpush_config::WebpushConfig; +use crate::web::webpush_config::WebpushConfigInternal; -#[derive(Clone, Serialize, Debug, PartialEq)] -#[serde(rename_all = "lowercase")] -pub enum Target { - Token(String), - Topic(String), - Condition(String), -} +use self::target::Target; fn output_target(target: &Target, s: S) -> Result where @@ -32,43 +36,36 @@ where #[derive(Serialize, Debug)] // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#resource:-message -pub struct Message { +pub struct MessageInternal { // Arbitrary key/value payload, which must be UTF-8 encoded. #[serde(skip_serializing_if = "Option::is_none")] data: Option, // Basic notification template to use across all platforms. #[serde(skip_serializing_if = "Option::is_none")] - notification: Option, + notification: Option, // Android specific options for messages sent through FCM connection server. #[serde(skip_serializing_if = "Option::is_none")] - android: Option, + android: Option, // Webpush protocol options. #[serde(skip_serializing_if = "Option::is_none")] - webpush: Option, + webpush: Option, // Apple Push Notification Service specific options. #[serde(skip_serializing_if = "Option::is_none")] - apns: Option, + apns: Option, // Template for FCM SDK feature options to use across all platforms. #[serde(skip_serializing_if = "Option::is_none")] - fcm_options: Option, + fcm_options: Option, // Target to send a message to. #[serde(flatten, serialize_with = "output_target")] target: Target, } -#[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#fcmoptions -pub struct FcmOptions { - // Label associated with the message's analytics data. - analytics_label: String, -} - /// /// A builder to get a `Message` instance. /// @@ -82,71 +79,26 @@ pub struct FcmOptions { /// let message = builder.finalize(); /// ``` #[derive(Debug)] -pub struct MessageBuilder { - data: Option, - notification: Option, - target: Target, +pub struct Message { + pub data: Option, + pub notification: Option, + pub target: Target, + pub android: Option, + pub webpush: Option, + pub apns: Option, + pub fcm_options: Option, } -impl MessageBuilder { - /// Get a new instance of Message. You need to supply to. - pub fn new(target: Target) -> Self { - MessageBuilder { - data: None, - notification: None, - target, - } - } - - /// Use this to add custom key-value pairs to the message. This data - /// must be handled appropriately on the client end. The data can be - /// anything that Serde can serialize to JSON. - /// - /// # Examples: - /// ```rust - /// use fcm::{MessageBuilder, Target}; - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// map.insert("message", "Howdy!"); - /// - /// let mut builder = MessageBuilder::new(Target::Token("token".to_string())); - /// builder.data(&map).expect("Should have been able to add data"); - /// let message = builder.finalize(); - /// ``` - pub fn data(&mut self, data: &dyn erased_serde::Serialize) -> Result<&mut Self, serde_json::Error> { - self.data = Some(serde_json::to_value(data)?); - Ok(self) - } - - /// Use this to set a `Notification` for the message. - /// # Examples: - /// ```rust - /// use fcm::{MessageBuilder, NotificationBuilder, Target}; - /// - /// let mut builder = NotificationBuilder::new(); - /// builder.title("Hey!".to_string()); - /// builder.body("Do you want to catch up later?".to_string()); - /// let notification = builder.finalize(); - /// - /// let mut builder = MessageBuilder::new(Target::Token("token".to_string())); - /// builder.notification(notification); - /// let message = builder.finalize(); - /// ``` - pub fn notification(&mut self, notification: Notification) -> &mut Self { - self.notification = Some(notification); - self - } - +impl Message { /// Complete the build and get a `Message` instance - pub fn finalize(self) -> Message { - Message { + pub fn finalize(self) -> MessageInternal { + MessageInternal { data: self.data, - notification: self.notification, - android: None, - webpush: None, - apns: None, - fcm_options: None, + notification: self.notification.map(|n| n.finalize()), + android: self.android.map(|a| a.finalize()), + webpush: self.webpush.map(|w| w.finalize()), + apns: self.apns.map(|a| a.finalize()), + fcm_options: self.fcm_options.map(|f| f.finalize()), target: self.target, } } diff --git a/src/message/target.rs b/src/message/target.rs new file mode 100644 index 000000000..b90db8584 --- /dev/null +++ b/src/message/target.rs @@ -0,0 +1,9 @@ +use serde::Serialize; + +#[derive(Clone, Serialize, Debug, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum Target { + Token(String), + Topic(String), + Condition(String), +} diff --git a/src/message/tests.rs b/src/message/tests.rs index c23e94e45..0773b3433 100644 --- a/src/message/tests.rs +++ b/src/message/tests.rs @@ -1,18 +1,19 @@ -use crate::notification::NotificationBuilder; -use crate::{MessageBuilder, Target}; -use serde::Serialize; +use crate::{notification::Notification, target::Target, Message}; use serde_json::json; -#[derive(Serialize)] -struct CustomData { - foo: &'static str, - bar: bool, -} - #[test] fn should_create_new_message() { let target = Target::Token("token".to_string()); - let msg = MessageBuilder::new(target.clone()).finalize(); + let msg = Message { + target: target.clone(), + data: None, + notification: None, + android: None, + webpush: None, + apns: None, + fcm_options: None, + } + .finalize(); assert_eq!(msg.target, target); } @@ -20,7 +21,16 @@ fn should_create_new_message() { #[test] fn should_leave_nones_out_of_the_json() { let target = Target::Token("token".to_string()); - let msg = MessageBuilder::new(target).finalize(); + let msg = Message { + target: target.clone(), + data: None, + notification: None, + android: None, + webpush: None, + apns: None, + fcm_options: None, + } + .finalize(); let payload = serde_json::to_string(&msg).unwrap(); let expected_payload = json!({ @@ -34,11 +44,17 @@ fn should_leave_nones_out_of_the_json() { #[test] fn should_add_custom_data_to_the_payload() { let target = Target::Token("token".to_string()); - let mut builder = MessageBuilder::new(target); - - let data = CustomData { foo: "bar", bar: false }; - - builder.data(&data).unwrap(); + let data = json!({ "foo": "bar", "bar": false }); + + let builder = Message { + target: target, + data: Some(data), + notification: None, + android: None, + webpush: None, + apns: None, + fcm_options: None, + }; let msg = builder.finalize(); let payload = serde_json::to_string(&msg).unwrap(); @@ -58,9 +74,20 @@ fn should_add_custom_data_to_the_payload() { #[test] fn should_be_able_to_render_a_full_token_message_to_json() { let target = Target::Token("token".to_string()); - let mut builder = MessageBuilder::new(target); - - builder.notification(NotificationBuilder::new().finalize()); + let notification = Notification { + title: None, + body: None, + image: None, + }; + let builder = Message { + target: target.clone(), + data: None, + notification: Some(notification), + android: None, + webpush: None, + apns: None, + fcm_options: None, + }; let payload = serde_json::to_string(&builder.finalize()).unwrap(); @@ -76,9 +103,20 @@ fn should_be_able_to_render_a_full_token_message_to_json() { #[test] fn should_be_able_to_render_a_full_topic_message_to_json() { let target = Target::Topic("my_topic".to_string()); - let mut builder = MessageBuilder::new(target); - - builder.notification(NotificationBuilder::new().finalize()); + let notification = Notification { + title: None, + body: None, + image: None, + }; + let builder = Message { + target: target.clone(), + data: None, + notification: Some(notification), + android: None, + webpush: None, + apns: None, + fcm_options: None, + }; let payload = serde_json::to_string(&builder.finalize()).unwrap(); @@ -94,9 +132,20 @@ fn should_be_able_to_render_a_full_topic_message_to_json() { #[test] fn should_be_able_to_render_a_full_condition_message_to_json() { let target = Target::Condition("my_condition".to_string()); - let mut builder = MessageBuilder::new(target); - - builder.notification(NotificationBuilder::new().finalize()); + let notification = Notification { + title: None, + body: None, + image: None, + }; + let builder = Message { + target: target.clone(), + data: None, + notification: Some(notification), + android: None, + webpush: None, + apns: None, + fcm_options: None, + }; let payload = serde_json::to_string(&builder.finalize()).unwrap(); @@ -112,15 +161,34 @@ fn should_be_able_to_render_a_full_condition_message_to_json() { #[test] fn should_set_notifications() { let target = Target::Token("token".to_string()); - let msg = MessageBuilder::new(target.clone()).finalize(); - - assert_eq!(msg.notification, None); - - let nm = NotificationBuilder::new().finalize(); - - let mut builder = MessageBuilder::new(target); - builder.notification(nm); + let msg = Message { + target: target.clone(), + data: None, + notification: None, + android: None, + webpush: None, + apns: None, + fcm_options: None, + }; + + assert_eq!(msg.notification.is_none(), true); + + let nm = Notification { + title: None, + body: None, + image: None, + }; + + let builder = Message { + target: target.clone(), + data: None, + notification: Some(nm), + android: None, + webpush: None, + apns: None, + fcm_options: None, + }; let msg = builder.finalize(); - assert_ne!(msg.notification, None); + assert_eq!(msg.notification.is_none(), false); } diff --git a/src/notification/mod.rs b/src/notification/mod.rs index 0ca5606a0..7be580c22 100644 --- a/src/notification/mod.rs +++ b/src/notification/mod.rs @@ -1,14 +1,14 @@ -use serde::Serialize; - #[cfg(test)] mod tests; +use serde::Serialize; + /// This struct represents a FCM notification. Use the /// corresponding `NotificationBuilder` to get an instance. You can then use /// this notification instance when sending a FCM message. #[derive(Serialize, Debug, PartialEq)] // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#notification -pub struct Notification { +pub struct NotificationInternal { // The notification's title. #[serde(skip_serializing_if = "Option::is_none")] title: Option, @@ -34,40 +34,17 @@ pub struct Notification { /// builder.body("3 runs to win in 1 ball".to_string()); /// let notification = builder.finalize(); /// ``` -#[derive(Default)] -pub struct NotificationBuilder { - title: Option, - body: Option, - image: Option, +#[derive(Debug)] +pub struct Notification { + pub title: Option, + pub body: Option, + pub image: Option, } -impl NotificationBuilder { - /// Get a new `NotificationBuilder` instance, with a title. - pub fn new() -> NotificationBuilder { - Self::default() - } - - // Set the title of the notification - pub fn title(&mut self, title: String) -> &mut Self { - self.title = Some(title); - self - } - - /// Set the body of the notification - pub fn body(&mut self, body: String) -> &mut Self { - self.body = Some(body); - self - } - - /// Set the image - pub fn image(&mut self, image: String) -> &mut Self { - self.image = Some(image); - self - } - +impl Notification { /// Complete the build and get a `Notification` instance - pub fn finalize(self) -> Notification { - Notification { + pub fn finalize(self) -> NotificationInternal { + NotificationInternal { title: self.title, body: self.body, image: self.image, diff --git a/src/notification/tests.rs b/src/notification/tests.rs index 36315ec1a..3bf593d87 100644 --- a/src/notification/tests.rs +++ b/src/notification/tests.rs @@ -1,16 +1,15 @@ -use crate::NotificationBuilder; +use crate::Notification; use serde_json::json; #[test] fn should_be_able_to_render_a_full_notification_to_json() { - let mut builder = NotificationBuilder::new(); + let not = Notification { + title: Some("foo".to_string()), + body: Some("bar".to_string()), + image: Some("https://my.image.com/test.jpg".to_string()), + }; - builder - .title("foo".to_string()) - .body("bar".to_string()) - .image("https://my.image.com/test.jpg".to_string()); - - let payload = serde_json::to_string(&builder.finalize()).unwrap(); + let payload = serde_json::to_string(¬.finalize()).unwrap(); let expected_payload = json!({ "title": "foo", @@ -21,38 +20,3 @@ fn should_be_able_to_render_a_full_notification_to_json() { assert_eq!(expected_payload, payload); } - -#[test] -fn should_set_notification_title() { - let nm = NotificationBuilder::new().finalize(); - - assert_eq!(nm.title, None); - - let mut builder = NotificationBuilder::new(); - builder.title("title".to_string()); - let nm = builder.finalize(); - - assert_eq!(nm.title, Some("title".to_string())); -} - -#[test] -fn should_set_notification_body() { - let nm = NotificationBuilder::new().finalize(); - - assert_eq!(nm.body, None); - - let mut builder = NotificationBuilder::new(); - builder.body("body".to_string()); - let nm = builder.finalize(); - - assert_eq!(nm.body, Some("body".to_string())); -} - -#[test] -fn should_set_notification_image() { - let mut builder = NotificationBuilder::new(); - builder.image("https://my.image.com/test.jpg".to_string()); - let nm = builder.finalize(); - - assert_eq!(nm.image, Some("https://my.image.com/test.jpg".to_string())); -} diff --git a/src/web/mod.rs b/src/web/mod.rs index 60d42990d..52e1762c8 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -1,33 +1,2 @@ -use serde::Serialize; -use serde_json::Value; - -#[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#webpushconfig -pub struct WebpushConfig { - // HTTP headers defined in webpush protocol. - #[serde(skip_serializing_if = "Option::is_none")] - headers: Option, - - // Arbitrary key/value payload. - #[serde(skip_serializing_if = "Option::is_none")] - data: Option, - - // Web Notification options as a JSON object. - // Struct format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Struct - #[serde(skip_serializing_if = "Option::is_none")] - notification: Option, - - // Options for features provided by the FCM SDK for Web. - #[serde(skip_serializing_if = "Option::is_none")] - fcm_options: Option, -} - -#[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#webpushfcmoptions -pub struct WebpushFcmOptions { - // The link to open when the user clicks on the notification. - link: String, - - // Label associated with the message's analytics data. - analytics_label: String, -} +pub mod webpush_config; +pub mod webpush_fcm_options; diff --git a/src/web/webpush_config.rs b/src/web/webpush_config.rs new file mode 100644 index 000000000..e78bfd345 --- /dev/null +++ b/src/web/webpush_config.rs @@ -0,0 +1,44 @@ +use serde::Serialize; +use serde_json::Value; + +use super::webpush_fcm_options::{WebpushFcmOptions, WebpushFcmOptionsInternal}; + +#[derive(Serialize, Debug)] +// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#webpushconfig +pub struct WebpushConfigInternal { + // HTTP headers defined in webpush protocol. + #[serde(skip_serializing_if = "Option::is_none")] + headers: Option, + + // Arbitrary key/value payload. + #[serde(skip_serializing_if = "Option::is_none")] + data: Option, + + // Web Notification options as a JSON object. + // Struct format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Struct + #[serde(skip_serializing_if = "Option::is_none")] + notification: Option, + + // Options for features provided by the FCM SDK for Web. + #[serde(skip_serializing_if = "Option::is_none")] + fcm_options: Option, +} + +#[derive(Debug)] +pub struct WebpushConfig { + pub headers: Option, + pub data: Option, + pub notification: Option, + pub fcm_options: Option, +} + +impl WebpushConfig { + pub fn finalize(self) -> WebpushConfigInternal { + WebpushConfigInternal { + headers: self.headers, + data: self.data, + notification: self.notification, + fcm_options: self.fcm_options.map(|fcm_options| fcm_options.finalize()), + } + } +} diff --git a/src/web/webpush_fcm_options.rs b/src/web/webpush_fcm_options.rs new file mode 100644 index 000000000..45b78c673 --- /dev/null +++ b/src/web/webpush_fcm_options.rs @@ -0,0 +1,30 @@ +use serde::Serialize; + +#[derive(Serialize, Debug)] +// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#webpushfcmoptions +pub struct WebpushFcmOptionsInternal { + // The link to open when the user clicks on the notification. + link: String, + + // Label associated with the message's analytics data. + analytics_label: String, +} + +#[derive(Debug)] +// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#webpushfcmoptions +pub struct WebpushFcmOptions { + // The link to open when the user clicks on the notification. + pub link: String, + + // Label associated with the message's analytics data. + pub analytics_label: String, +} + +impl WebpushFcmOptions { + pub fn finalize(self) -> WebpushFcmOptionsInternal { + WebpushFcmOptionsInternal { + link: self.link, + analytics_label: self.analytics_label, + } + } +} From a4dc1154a693104714637e5b9c40e084fc31b3e2 Mon Sep 17 00:00:00 2001 From: adnanjpg Date: Mon, 22 Jan 2024 17:45:26 +0300 Subject: [PATCH 2/7] feat: reorganize exports and expose necessary stuff from lib.rs --- examples/simple_sender.rs | 2 +- src/android/android_config.rs | 4 ++-- src/android/android_fcm_options.rs | 4 ++-- src/android/android_notification.rs | 4 ++-- src/android/color.rs | 4 ++-- src/android/light_settings.rs | 4 ++-- src/apns/apns_config.rs | 4 ++-- src/apns/apns_fcm_options.rs | 4 ++-- src/lib.rs | 22 +++++++++++++++++++--- src/message/fcm_options.rs | 4 ++-- src/message/mod.rs | 9 +++++---- src/message/tests.rs | 2 +- src/notification/mod.rs | 4 ++-- src/web/webpush_config.rs | 4 ++-- src/web/webpush_fcm_options.rs | 4 ++-- 15 files changed, 48 insertions(+), 31 deletions(-) diff --git a/examples/simple_sender.rs b/examples/simple_sender.rs index fb7cedc28..934b24e99 100644 --- a/examples/simple_sender.rs +++ b/examples/simple_sender.rs @@ -1,7 +1,7 @@ // cargo run --example simple_sender -- -t use argparse::{ArgumentParser, Store}; -use fcm::{fcm_options::FcmOptions, target::Target, Client, Message, Notification}; +use fcm::{Client, FcmOptions, Message, Notification, Target}; use serde_json::json; #[tokio::main] diff --git a/src/android/android_config.rs b/src/android/android_config.rs index 800a840e5..de9c213ef 100644 --- a/src/android/android_config.rs +++ b/src/android/android_config.rs @@ -9,7 +9,7 @@ use super::{ #[derive(Serialize, Debug)] //https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidconfig -pub struct AndroidConfigInternal { +pub(crate) struct AndroidConfigInternal { // An identifier of a group of messages that can be collapsed, so that only the last message gets // sent when delivery can be resumed. #[serde(skip_serializing_if = "Option::is_none")] @@ -58,7 +58,7 @@ pub struct AndroidConfig { } impl AndroidConfig { - pub fn finalize(self) -> AndroidConfigInternal { + pub(crate) fn finalize(self) -> AndroidConfigInternal { AndroidConfigInternal { collapse_key: self.collapse_key, priority: self.priority, diff --git a/src/android/android_fcm_options.rs b/src/android/android_fcm_options.rs index 81a32de7a..830e12b56 100644 --- a/src/android/android_fcm_options.rs +++ b/src/android/android_fcm_options.rs @@ -2,7 +2,7 @@ use serde::Serialize; #[derive(Serialize, Debug)] //https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidconfig -pub struct AndroidFcmOptionsInternal { +pub(crate) struct AndroidFcmOptionsInternal { // Label associated with the message's analytics data. analytics_label: String, } @@ -13,7 +13,7 @@ pub struct AndroidFcmOptions { } impl AndroidFcmOptions { - pub fn finalize(self) -> AndroidFcmOptionsInternal { + pub(crate) fn finalize(self) -> AndroidFcmOptionsInternal { AndroidFcmOptionsInternal { analytics_label: self.analytics_label, } diff --git a/src/android/android_notification.rs b/src/android/android_notification.rs index 0656c13fe..7bf4546be 100644 --- a/src/android/android_notification.rs +++ b/src/android/android_notification.rs @@ -8,7 +8,7 @@ use super::{ #[derive(Serialize, Debug)] // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidnotification -pub struct AndroidNotificationInternal { +pub(crate) struct AndroidNotificationInternal { // The notification's title. #[serde(skip_serializing_if = "Option::is_none")] title: Option, @@ -202,7 +202,7 @@ pub struct AndroidNotification { } impl AndroidNotification { - pub fn finalize(self) -> AndroidNotificationInternal { + pub(crate) fn finalize(self) -> AndroidNotificationInternal { AndroidNotificationInternal { title: self.title, body: self.body, diff --git a/src/android/color.rs b/src/android/color.rs index 9520607f3..d9096cab3 100644 --- a/src/android/color.rs +++ b/src/android/color.rs @@ -2,7 +2,7 @@ use serde::Serialize; #[derive(Serialize, Debug)] // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#Color -pub struct ColorInternal { +pub(crate) struct ColorInternal { // The amount of red in the color as a value in the interval [0, 1]. red: f32, @@ -32,7 +32,7 @@ pub struct Color { } impl Color { - pub fn finalize(self) -> ColorInternal { + pub(crate) fn finalize(self) -> ColorInternal { ColorInternal { red: self.red, green: self.green, diff --git a/src/android/light_settings.rs b/src/android/light_settings.rs index 43cd7b01b..0b991b312 100644 --- a/src/android/light_settings.rs +++ b/src/android/light_settings.rs @@ -4,7 +4,7 @@ use super::color::{Color, ColorInternal}; #[derive(Serialize, Debug)] // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#LightSettings -pub struct LightSettingsInternal { +pub(crate) struct LightSettingsInternal { // Set color of the LED with google.type.Color. color: ColorInternal, @@ -33,7 +33,7 @@ pub struct LightSettings { } impl LightSettings { - pub fn finalize(self) -> LightSettingsInternal { + pub(crate) fn finalize(self) -> LightSettingsInternal { LightSettingsInternal { color: self.color.finalize(), light_on_duration: self.light_on_duration, diff --git a/src/apns/apns_config.rs b/src/apns/apns_config.rs index 6d2b6b8e8..294ed670b 100644 --- a/src/apns/apns_config.rs +++ b/src/apns/apns_config.rs @@ -5,7 +5,7 @@ use super::apns_fcm_options::{ApnsFcmOptions, ApnsFcmOptionsInternal}; #[derive(Serialize, Debug)] // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#apnsconfig -pub struct ApnsConfigInternal { +pub(crate) struct ApnsConfigInternal { // HTTP request headers defined in Apple Push Notification Service. #[serde(skip_serializing_if = "Option::is_none")] headers: Option, @@ -27,7 +27,7 @@ pub struct ApnsConfig { } impl ApnsConfig { - pub fn finalize(self) -> ApnsConfigInternal { + pub(crate) fn finalize(self) -> ApnsConfigInternal { ApnsConfigInternal { headers: self.headers, payload: self.payload, diff --git a/src/apns/apns_fcm_options.rs b/src/apns/apns_fcm_options.rs index 841fa6412..c680eaceb 100644 --- a/src/apns/apns_fcm_options.rs +++ b/src/apns/apns_fcm_options.rs @@ -2,7 +2,7 @@ use serde::Serialize; #[derive(Serialize, Debug)] // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#apnsfcmoptions -pub struct ApnsFcmOptionsInternal { +pub(crate) struct ApnsFcmOptionsInternal { // Label associated with the message's analytics data. analytics_label: Option, @@ -20,7 +20,7 @@ pub struct ApnsFcmOptions { } impl ApnsFcmOptions { - pub fn finalize(self) -> ApnsFcmOptionsInternal { + pub(crate) fn finalize(self) -> ApnsFcmOptionsInternal { ApnsFcmOptionsInternal { analytics_label: self.analytics_label, image: self.image, diff --git a/src/lib.rs b/src/lib.rs index 58461c143..5a6247968 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,15 +61,31 @@ //! ``` mod message; +pub use crate::message::fcm_options::*; +pub use crate::message::target::*; pub use crate::message::*; mod notification; pub use crate::notification::*; + mod android; +pub use crate::android::android_config::*; +pub use crate::android::android_fcm_options::*; +pub use crate::android::android_message_priority::*; +pub use crate::android::android_notification::*; +pub use crate::android::color::*; +pub use crate::android::light_settings::*; +pub use crate::android::notification_priority::*; +pub use crate::android::visibility::*; + mod apns; -mod client; -mod web; +pub use crate::apns::apns_config::*; +pub use crate::apns::apns_fcm_options::*; -pub use crate::client::*; +mod web; +pub use crate::web::webpush_config::*; +pub use crate::web::webpush_fcm_options::*; +mod client; pub use crate::client::response::FcmError as Error; +pub use crate::client::*; diff --git a/src/message/fcm_options.rs b/src/message/fcm_options.rs index 8aa0cc7dc..0b26aeb47 100644 --- a/src/message/fcm_options.rs +++ b/src/message/fcm_options.rs @@ -2,7 +2,7 @@ use serde::Serialize; #[derive(Serialize, Debug)] // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#fcmoptions -pub struct FcmOptionsInternal { +pub(crate) struct FcmOptionsInternal { // Label associated with the message's analytics data. analytics_label: String, } @@ -13,7 +13,7 @@ pub struct FcmOptions { } impl FcmOptions { - pub fn finalize(self) -> FcmOptionsInternal { + pub(crate) fn finalize(self) -> FcmOptionsInternal { FcmOptionsInternal { analytics_label: self.analytics_label, } diff --git a/src/message/mod.rs b/src/message/mod.rs index 9abb1bcaf..a3a2801fd 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -1,5 +1,6 @@ pub mod fcm_options; pub mod target; + #[cfg(test)] mod tests; @@ -12,13 +13,13 @@ use crate::android::android_config::AndroidConfig; use crate::android::android_config::AndroidConfigInternal; use crate::apns::apns_config::ApnsConfig; use crate::apns::apns_config::ApnsConfigInternal; -use crate::fcm_options::FcmOptions; -use crate::fcm_options::FcmOptionsInternal; use crate::notification::Notification; use crate::notification::NotificationInternal; use crate::web::webpush_config::WebpushConfig; use crate::web::webpush_config::WebpushConfigInternal; +use self::fcm_options::FcmOptions; +use self::fcm_options::FcmOptionsInternal; use self::target::Target; fn output_target(target: &Target, s: S) -> Result @@ -36,7 +37,7 @@ where #[derive(Serialize, Debug)] // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#resource:-message -pub struct MessageInternal { +pub(crate) struct MessageInternal { // Arbitrary key/value payload, which must be UTF-8 encoded. #[serde(skip_serializing_if = "Option::is_none")] data: Option, @@ -91,7 +92,7 @@ pub struct Message { impl Message { /// Complete the build and get a `Message` instance - pub fn finalize(self) -> MessageInternal { + pub(crate) fn finalize(self) -> MessageInternal { MessageInternal { data: self.data, notification: self.notification.map(|n| n.finalize()), diff --git a/src/message/tests.rs b/src/message/tests.rs index 0773b3433..143ec9edc 100644 --- a/src/message/tests.rs +++ b/src/message/tests.rs @@ -1,4 +1,4 @@ -use crate::{notification::Notification, target::Target, Message}; +use crate::{message::Target, notification::Notification, Message}; use serde_json::json; #[test] diff --git a/src/notification/mod.rs b/src/notification/mod.rs index 7be580c22..e61441d6e 100644 --- a/src/notification/mod.rs +++ b/src/notification/mod.rs @@ -8,7 +8,7 @@ use serde::Serialize; /// this notification instance when sending a FCM message. #[derive(Serialize, Debug, PartialEq)] // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#notification -pub struct NotificationInternal { +pub(crate) struct NotificationInternal { // The notification's title. #[serde(skip_serializing_if = "Option::is_none")] title: Option, @@ -43,7 +43,7 @@ pub struct Notification { impl Notification { /// Complete the build and get a `Notification` instance - pub fn finalize(self) -> NotificationInternal { + pub(crate) fn finalize(self) -> NotificationInternal { NotificationInternal { title: self.title, body: self.body, diff --git a/src/web/webpush_config.rs b/src/web/webpush_config.rs index e78bfd345..1486518d8 100644 --- a/src/web/webpush_config.rs +++ b/src/web/webpush_config.rs @@ -5,7 +5,7 @@ use super::webpush_fcm_options::{WebpushFcmOptions, WebpushFcmOptionsInternal}; #[derive(Serialize, Debug)] // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#webpushconfig -pub struct WebpushConfigInternal { +pub(crate) struct WebpushConfigInternal { // HTTP headers defined in webpush protocol. #[serde(skip_serializing_if = "Option::is_none")] headers: Option, @@ -33,7 +33,7 @@ pub struct WebpushConfig { } impl WebpushConfig { - pub fn finalize(self) -> WebpushConfigInternal { + pub(crate) fn finalize(self) -> WebpushConfigInternal { WebpushConfigInternal { headers: self.headers, data: self.data, diff --git a/src/web/webpush_fcm_options.rs b/src/web/webpush_fcm_options.rs index 45b78c673..b9b239549 100644 --- a/src/web/webpush_fcm_options.rs +++ b/src/web/webpush_fcm_options.rs @@ -2,7 +2,7 @@ use serde::Serialize; #[derive(Serialize, Debug)] // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#webpushfcmoptions -pub struct WebpushFcmOptionsInternal { +pub(crate) struct WebpushFcmOptionsInternal { // The link to open when the user clicks on the notification. link: String, @@ -21,7 +21,7 @@ pub struct WebpushFcmOptions { } impl WebpushFcmOptions { - pub fn finalize(self) -> WebpushFcmOptionsInternal { + pub(crate) fn finalize(self) -> WebpushFcmOptionsInternal { WebpushFcmOptionsInternal { link: self.link, analytics_label: self.analytics_label, From 59c8a7a94021311030df745c54224c3dc5d408b6 Mon Sep 17 00:00:00 2001 From: adnanjpg Date: Mon, 22 Jan 2024 19:12:52 +0300 Subject: [PATCH 3/7] fix docs tests and add default impl to all --- examples/simple_sender.rs | 22 ++++++--- src/android/android_config.rs | 2 +- src/android/android_fcm_options.rs | 2 +- src/android/android_notification.rs | 2 +- src/android/color.rs | 2 +- src/android/light_settings.rs | 2 +- src/apns/apns_config.rs | 2 +- src/apns/apns_fcm_options.rs | 2 +- src/lib.rs | 71 +++++++++++------------------ src/message/fcm_options.rs | 2 +- src/message/mod.rs | 13 +----- src/notification/mod.rs | 13 +----- src/web/webpush_config.rs | 2 +- src/web/webpush_fcm_options.rs | 2 +- 14 files changed, 55 insertions(+), 84 deletions(-) diff --git a/examples/simple_sender.rs b/examples/simple_sender.rs index 934b24e99..e5fc85300 100644 --- a/examples/simple_sender.rs +++ b/examples/simple_sender.rs @@ -1,7 +1,9 @@ // cargo run --example simple_sender -- -t use argparse::{ArgumentParser, Store}; -use fcm::{Client, FcmOptions, Message, Notification, Target}; +use fcm::{ + AndroidConfig, AndroidNotification, ApnsConfig, Client, FcmOptions, Message, Notification, Target, WebpushConfig, +}; use serde_json::json; #[tokio::main] @@ -27,17 +29,25 @@ async fn main() -> Result<(), Box> { let builder = Message { data: Some(data), notification: Some(Notification { - title: Some("Hello".to_string()), + title: Some("I'm high".to_string()), body: Some(format!("it's {}", chrono::Utc::now())), - image: None, + ..Default::default() }), target: Target::Token(device_token), - android: None, - webpush: None, - apns: None, fcm_options: Some(FcmOptions { analytics_label: "analytics_label".to_string(), }), + android: Some(AndroidConfig { + priority: Some(fcm::AndroidMessagePriority::High), + notification: Some(AndroidNotification { + title: Some("I'm Android high".to_string()), + body: Some(format!("Hi Android, it's {}", chrono::Utc::now())), + ..Default::default() + }), + ..Default::default() + }), + apns: Some(ApnsConfig { ..Default::default() }), + webpush: Some(WebpushConfig { ..Default::default() }), }; let response = client.send(builder).await?; diff --git a/src/android/android_config.rs b/src/android/android_config.rs index de9c213ef..938cf9508 100644 --- a/src/android/android_config.rs +++ b/src/android/android_config.rs @@ -45,7 +45,7 @@ pub(crate) struct AndroidConfigInternal { direct_boot_ok: Option, } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct AndroidConfig { pub collapse_key: Option, pub priority: Option, diff --git a/src/android/android_fcm_options.rs b/src/android/android_fcm_options.rs index 830e12b56..4864af376 100644 --- a/src/android/android_fcm_options.rs +++ b/src/android/android_fcm_options.rs @@ -7,7 +7,7 @@ pub(crate) struct AndroidFcmOptionsInternal { analytics_label: String, } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct AndroidFcmOptions { pub analytics_label: String, } diff --git a/src/android/android_notification.rs b/src/android/android_notification.rs index 7bf4546be..6d4105349 100644 --- a/src/android/android_notification.rs +++ b/src/android/android_notification.rs @@ -116,7 +116,7 @@ pub(crate) struct AndroidNotificationInternal { image: Option, } -#[derive(Debug)] +#[derive(Debug, Default)] // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidnotification pub struct AndroidNotification { // The notification's title. diff --git a/src/android/color.rs b/src/android/color.rs index d9096cab3..61aeda3a6 100644 --- a/src/android/color.rs +++ b/src/android/color.rs @@ -16,7 +16,7 @@ pub(crate) struct ColorInternal { alpha: f32, } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Color { // The amount of red in the color as a value in the interval [0, 1]. pub red: f32, diff --git a/src/android/light_settings.rs b/src/android/light_settings.rs index 0b991b312..1ec4e1a1b 100644 --- a/src/android/light_settings.rs +++ b/src/android/light_settings.rs @@ -17,7 +17,7 @@ pub(crate) struct LightSettingsInternal { light_off_duration: String, } -#[derive(Debug)] +#[derive(Debug, Default)] // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#LightSettings pub struct LightSettings { // Set color of the LED with google.type.Color. diff --git a/src/apns/apns_config.rs b/src/apns/apns_config.rs index 294ed670b..b5ccd1b74 100644 --- a/src/apns/apns_config.rs +++ b/src/apns/apns_config.rs @@ -19,7 +19,7 @@ pub(crate) struct ApnsConfigInternal { fcm_options: Option, } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct ApnsConfig { pub headers: Option, pub payload: Option, diff --git a/src/apns/apns_fcm_options.rs b/src/apns/apns_fcm_options.rs index c680eaceb..1ac76e74b 100644 --- a/src/apns/apns_fcm_options.rs +++ b/src/apns/apns_fcm_options.rs @@ -10,7 +10,7 @@ pub(crate) struct ApnsFcmOptionsInternal { image: Option, } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct ApnsFcmOptions { // Label associated with the message's analytics data. pub analytics_label: Option, diff --git a/src/lib.rs b/src/lib.rs index 5a6247968..259f34f97 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,55 +9,38 @@ //! To send out a FCM Message with some custom data: //! //! ```no_run -//! # use std::collections::HashMap; -//! # #[tokio::main] -//! # async fn main() -> Result<(), Box> { -//! use fcm::Target; -//! let client = fcm::Client::new(); //! -//! let mut map = HashMap::new(); -//! map.insert("message", "Howdy!"); +//! #[tokio::main] +//! async fn main() -> Result<(), Box> { +//! use serde_json::json; +//! use fcm::{Target, FcmOptions, Notification, Message}; +//! let client = fcm::Client::new(); //! -//! let mut builder = fcm::MessageBuilder::new(Target::Token("token".to_string())); -//! builder.data(&map); +//! let data = json!({ +//! "message": "Howdy!" +//! }); //! -//! let response = client.send(builder.finalize()).await?; -//! println!("Sent: {:?}", response); -//! # Ok(()) -//! # } -//! ``` -//! -//! To send a message using FCM Notifications, we first build the notification: -//! -//! ```rust -//! # fn main() { -//! let mut builder = fcm::NotificationBuilder::new(); -//! builder.title("Hey!".to_string()); -//! builder.body("Do you want to catch up later?".to_string()); -//! let notification = builder.finalize(); -//! # } -//! ``` -//! -//! And then set it in the message, before sending it: -//! -//! ```no_run -//! # #[tokio::main] -//! # async fn main() -> Result<(), Box> { -//! use fcm::Target; -//! let client = fcm::Client::new(); -//! -//! let mut notification_builder = fcm::NotificationBuilder::new(); -//! notification_builder.title("Hey!".to_string()); -//! notification_builder.body("Do you want to catch up later?".to_string()); +//! let builder = Message { +//! data: Some(data), +//! notification: Some(Notification { +//! title: Some("Hello".to_string()), +//! body: Some(format!("it's {}", chrono::Utc::now())), +//! image: None, +//! }), +//! target: Target::Token("token".to_string()), +//! android: None, +//! webpush: None, +//! apns: None, +//! fcm_options: Some(FcmOptions { +//! analytics_label: "analytics_label".to_string(), +//! }), +//! }; //! -//! let notification = notification_builder.finalize(); -//! let mut message_builder = fcm::MessageBuilder::new(Target::Token("token".to_string())); -//! message_builder.notification(notification); +//! let response = client.send(builder).await?; +//! println!("Sent: {:?}", response); //! -//! let response = client.send(message_builder.finalize()).await?; -//! println!("Sent: {:?}", response); -//! # Ok(()) -//! # } +//! Ok(()) +//! } //! ``` mod message; diff --git a/src/message/fcm_options.rs b/src/message/fcm_options.rs index 0b26aeb47..3c2a50855 100644 --- a/src/message/fcm_options.rs +++ b/src/message/fcm_options.rs @@ -7,7 +7,7 @@ pub(crate) struct FcmOptionsInternal { analytics_label: String, } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct FcmOptions { pub analytics_label: String, } diff --git a/src/message/mod.rs b/src/message/mod.rs index a3a2801fd..a84d7834b 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -67,18 +67,7 @@ pub(crate) struct MessageInternal { target: Target, } -/// -/// A builder to get a `Message` instance. -/// -/// # Examples -/// -/// ```rust -/// use fcm::{MessageBuilder, NotificationBuilder, Target}; -/// -/// let mut builder = MessageBuilder::new(Target::Token("token".to_string())); -/// builder.notification(NotificationBuilder::new().finalize()); -/// let message = builder.finalize(); -/// ``` +/// A `Message` instance is the main object to send to the FCM API. #[derive(Debug)] pub struct Message { pub data: Option, diff --git a/src/notification/mod.rs b/src/notification/mod.rs index e61441d6e..2105cdfda 100644 --- a/src/notification/mod.rs +++ b/src/notification/mod.rs @@ -23,18 +23,7 @@ pub(crate) struct NotificationInternal { } /// A builder to get a `Notification` instance. -/// -/// # Examples -/// -/// ```rust -/// use fcm::NotificationBuilder; -/// -/// let mut builder = NotificationBuilder::new(); -/// builder.title("Australia vs New Zealand".to_string()); -/// builder.body("3 runs to win in 1 ball".to_string()); -/// let notification = builder.finalize(); -/// ``` -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Notification { pub title: Option, pub body: Option, diff --git a/src/web/webpush_config.rs b/src/web/webpush_config.rs index 1486518d8..d467a1e55 100644 --- a/src/web/webpush_config.rs +++ b/src/web/webpush_config.rs @@ -24,7 +24,7 @@ pub(crate) struct WebpushConfigInternal { fcm_options: Option, } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct WebpushConfig { pub headers: Option, pub data: Option, diff --git a/src/web/webpush_fcm_options.rs b/src/web/webpush_fcm_options.rs index b9b239549..b1caaaa95 100644 --- a/src/web/webpush_fcm_options.rs +++ b/src/web/webpush_fcm_options.rs @@ -10,7 +10,7 @@ pub(crate) struct WebpushFcmOptionsInternal { analytics_label: String, } -#[derive(Debug)] +#[derive(Debug, Default)] // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#webpushfcmoptions pub struct WebpushFcmOptions { // The link to open when the user clicks on the notification. From d9673b29e77bd940856aa09acef6c2f56a0b7f5f Mon Sep 17 00:00:00 2001 From: adnanjpg Date: Wed, 31 Jan 2024 19:46:24 +0300 Subject: [PATCH 4/7] chore: remove new from a func that dont need it --- src/client/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index b82ca8821..ff58cad37 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -99,7 +99,7 @@ impl Client { Ok(tkn) } - pub async fn access_token(&self) -> Result { + async fn access_token(&self) -> Result { let scopes = vec!["https://www.googleapis.com/auth/firebase.messaging"]; let key_path = self.get_service_key_file_name()?; From 0dbb30bbad9a567c4eb3f890555ad8dbbf854b3a Mon Sep 17 00:00:00 2001 From: adnanjpg Date: Wed, 31 Jan 2024 20:10:55 +0300 Subject: [PATCH 5/7] docs: add docs to all props --- src/android/android_config.rs | 29 ++++-- src/android/android_fcm_options.rs | 4 +- src/android/android_message_priority.rs | 2 +- src/android/android_notification.rs | 128 ++++++++++++------------ src/android/color.rs | 21 ++-- src/android/light_settings.rs | 24 ++--- src/android/notification_priority.rs | 2 +- src/android/visibility.rs | 2 +- src/apns/apns_config.rs | 12 ++- src/apns/apns_fcm_options.rs | 11 +- src/message/fcm_options.rs | 6 +- src/message/mod.rs | 26 +++-- src/message/target.rs | 9 ++ src/notification/mod.rs | 16 +-- src/web/webpush_config.rs | 21 ++-- src/web/webpush_fcm_options.rs | 12 +-- 16 files changed, 185 insertions(+), 140 deletions(-) diff --git a/src/android/android_config.rs b/src/android/android_config.rs index 938cf9508..c6ef78756 100644 --- a/src/android/android_config.rs +++ b/src/android/android_config.rs @@ -8,52 +8,59 @@ use super::{ }; #[derive(Serialize, Debug)] -//https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidconfig pub(crate) struct AndroidConfigInternal { - // An identifier of a group of messages that can be collapsed, so that only the last message gets - // sent when delivery can be resumed. #[serde(skip_serializing_if = "Option::is_none")] collapse_key: Option, - // Message priority. #[serde(skip_serializing_if = "Option::is_none")] priority: Option, - // How long (in seconds) the message should be kept in FCM storage if the device is offline. - // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration #[serde(skip_serializing_if = "Option::is_none")] ttl: Option, - // Package name of the application where the registration token must match in order to receive the message. #[serde(skip_serializing_if = "Option::is_none")] restricted_package_name: Option, - // Arbitrary key/value payload. #[serde(skip_serializing_if = "Option::is_none")] data: Option, - // Notification to send to android devices. #[serde(skip_serializing_if = "Option::is_none")] notification: Option, - // Options for features provided by the FCM SDK for Android. #[serde(skip_serializing_if = "Option::is_none")] fcm_options: Option, - // If set to true, messages will be allowed to be delivered to the app while the device is in direct boot mode. #[serde(skip_serializing_if = "Option::is_none")] direct_boot_ok: Option, } #[derive(Debug, Default)] +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidconfig pub struct AndroidConfig { + /// An identifier of a group of messages that can be collapsed, so that only the last message gets + /// sent when delivery can be resumed. pub collapse_key: Option, + + /// Message priority. pub priority: Option, + + /// How long (in seconds) the message should be kept in FCM storage if the device is offline. + /// Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration pub ttl: Option, + + /// Package name of the application where the registration token must match in order to receive the message. pub restricted_package_name: Option, + + /// Arbitrary key/value payload. pub data: Option, + + /// Notification to send to android devices. pub notification: Option, + + /// Options for features provided by the FCM SDK for Android. pub fcm_options: Option, + + /// If set to true, messages will be allowed to be delivered to the app while the device is in direct boot mode. pub direct_boot_ok: Option, } diff --git a/src/android/android_fcm_options.rs b/src/android/android_fcm_options.rs index 4864af376..212d6a0cc 100644 --- a/src/android/android_fcm_options.rs +++ b/src/android/android_fcm_options.rs @@ -1,14 +1,14 @@ use serde::Serialize; #[derive(Serialize, Debug)] -//https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidconfig pub(crate) struct AndroidFcmOptionsInternal { - // Label associated with the message's analytics data. analytics_label: String, } #[derive(Debug, Default)] +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidconfig pub struct AndroidFcmOptions { + /// Label associated with the message's analytics data. pub analytics_label: String, } diff --git a/src/android/android_message_priority.rs b/src/android/android_message_priority.rs index 046466294..aa26c9c7c 100644 --- a/src/android/android_message_priority.rs +++ b/src/android/android_message_priority.rs @@ -3,7 +3,7 @@ use serde::Serialize; #[allow(dead_code)] #[derive(Serialize, Debug)] #[serde(rename_all = "UPPERCASE")] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidmessagepriority +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidmessagepriority pub enum AndroidMessagePriority { Normal, High, diff --git a/src/android/android_notification.rs b/src/android/android_notification.rs index 6d4105349..eba41511a 100644 --- a/src/android/android_notification.rs +++ b/src/android/android_notification.rs @@ -7,197 +7,197 @@ use super::{ }; #[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidnotification +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidnotification pub(crate) struct AndroidNotificationInternal { - // The notification's title. + /// The notification's title. #[serde(skip_serializing_if = "Option::is_none")] title: Option, - // The notification's body text. + /// The notification's body text. #[serde(skip_serializing_if = "Option::is_none")] body: Option, - // The notification's icon. + /// The notification's icon. #[serde(skip_serializing_if = "Option::is_none")] icon: Option, - // The notification's icon color, expressed in #rrggbb format. + /// The notification's icon color, expressed in #rrggbb format. #[serde(skip_serializing_if = "Option::is_none")] color: Option, - // The sound to play when the device receives the notification. + /// The sound to play when the device receives the notification. #[serde(skip_serializing_if = "Option::is_none")] sound: Option, - // Identifier used to replace existing notifications in the notification drawer. + /// Identifier used to replace existing notifications in the notification drawer. #[serde(skip_serializing_if = "Option::is_none")] tag: Option, - // The action associated with a user click on the notification. + /// The action associated with a user click on the notification. #[serde(skip_serializing_if = "Option::is_none")] click_action: Option, - // The key to the body string in the app's string resources to use to localize the body text to the user's - // current localization. + /// The key to the body string in the app's string resources to use to localize the body text to the user's + /// current localization. #[serde(skip_serializing_if = "Option::is_none")] body_loc_key: Option, - // Variable string values to be used in place of the format specifiers in body_loc_key to use to localize the - // body text to the user's current localization. + /// Variable string values to be used in place of the format specifiers in body_loc_key to use to localize the + /// body text to the user's current localization. #[serde(skip_serializing_if = "Option::is_none")] body_loc_args: Option>, - // The key to the title string in the app's string resources to use to localize the title text to the user's - // current localization. + /// The key to the title string in the app's string resources to use to localize the title text to the user's + /// current localization. #[serde(skip_serializing_if = "Option::is_none")] title_loc_key: Option, - // Variable string values to be used in place of the format specifiers in title_loc_key to use to localize the - // title text to the user's current localization. + /// Variable string values to be used in place of the format specifiers in title_loc_key to use to localize the + /// title text to the user's current localization. #[serde(skip_serializing_if = "Option::is_none")] title_loc_args: Option>, - // The notification's channel id (new in Android O). + /// The notification's channel id (new in Android O). #[serde(skip_serializing_if = "Option::is_none")] channel_id: Option, - // Sets the "ticker" text, which is sent to accessibility services. + /// Sets the "ticker" text, which is sent to accessibility services. #[serde(skip_serializing_if = "Option::is_none")] ticker: Option, - // When set to false or unset, the notification is automatically dismissed when the user clicks it in the panel. + /// When set to false or unset, the notification is automatically dismissed when the user clicks it in the panel. #[serde(skip_serializing_if = "Option::is_none")] sticky: Option, - // Set the time that the event in the notification occurred. Notifications in the panel are sorted by this time. - // Timestamp format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Timestamp + /// Set the time that the event in the notification occurred. Notifications in the panel are sorted by this time. + /// Timestamp format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Timestamp #[serde(skip_serializing_if = "Option::is_none")] event_time: Option, - // Set whether or not this notification is relevant only to the current device. + /// Set whether or not this notification is relevant only to the current device. #[serde(skip_serializing_if = "Option::is_none")] local_only: Option, - // Set the relative priority for this notification. + /// Set the relative priority for this notification. #[serde(skip_serializing_if = "Option::is_none")] notification_priority: Option, - // If set to true, use the Android framework's default sound for the notification. + /// If set to true, use the Android framework's default sound for the notification. #[serde(skip_serializing_if = "Option::is_none")] default_sound: Option, - // If set to true, use the Android framework's default vibrate pattern for the notification. + /// If set to true, use the Android framework's default vibrate pattern for the notification. #[serde(skip_serializing_if = "Option::is_none")] default_vibrate_timings: Option, - // If set to true, use the Android framework's default LED light settings for the notification. + /// If set to true, use the Android framework's default LED light settings for the notification. #[serde(skip_serializing_if = "Option::is_none")] default_light_settings: Option, - // Set the vibration pattern to use - // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration + /// Set the vibration pattern to use + /// Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration #[serde(skip_serializing_if = "Option::is_none")] vibrate_timings: Option>, - // Set the Notification.visibility of the notification. + /// Set the Notification.visibility of the notification. #[serde(skip_serializing_if = "Option::is_none")] visibility: Option, - // Sets the number of items this notification represents. + /// Sets the number of items this notification represents. #[serde(skip_serializing_if = "Option::is_none")] notification_count: Option, - // Settings to control the notification's LED blinking rate and color if LED is available on the device. + /// Settings to control the notification's LED blinking rate and color if LED is available on the device. #[serde(skip_serializing_if = "Option::is_none")] light_settings: Option, - // Contains the URL of an image that is going to be displayed in a notification. + /// Contains the URL of an image that is going to be displayed in a notification. #[serde(skip_serializing_if = "Option::is_none")] image: Option, } #[derive(Debug, Default)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidnotification +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#androidnotification pub struct AndroidNotification { - // The notification's title. + /// The notification's title. pub title: Option, - // The notification's body text. + /// The notification's body text. pub body: Option, - // The notification's icon. + /// The notification's icon. pub icon: Option, - // The notification's icon color, expressed in #rrggbb format. + /// The notification's icon color, expressed in #rrggbb format. pub color: Option, - // The sound to play when the device receives the notification. + /// The sound to play when the device receives the notification. pub sound: Option, - // Identifier used to replace existing notifications in the notification drawer. + /// Identifier used to replace existing notifications in the notification drawer. pub tag: Option, - // The action associated with a user click on the notification. + /// The action associated with a user click on the notification. pub click_action: Option, - // The key to the body string in the app's string resources to use to localize the body text to the user's - // current localization. + /// The key to the body string in the app's string resources to use to localize the body text to the user's + /// current localization. pub body_loc_key: Option, - // Variable string values to be used in place of the format specifiers in body_loc_key to use to localize the - // body text to the user's current localization. + /// Variable string values to be used in place of the format specifiers in body_loc_key to use to localize the + /// body text to the user's current localization. pub body_loc_args: Option>, - // The key to the title string in the app's string resources to use to localize the title text to the user's - // current localization. + /// The key to the title string in the app's string resources to use to localize the title text to the user's + /// current localization. pub title_loc_key: Option, - // Variable string values to be used in place of the format specifiers in title_loc_key to use to localize the - // title text to the user's current localization. + /// Variable string values to be used in place of the format specifiers in title_loc_key to use to localize the + /// title text to the user's current localization. pub title_loc_args: Option>, - // The notification's channel id (new in Android O). + /// The notification's channel id (new in Android O). pub channel_id: Option, - // Sets the "ticker" text, which is sent to accessibility services. + /// Sets the "ticker" text, which is sent to accessibility services. pub ticker: Option, - // When set to false or unset, the notification is automatically dismissed when the user clicks it in the panel. + /// When set to false or unset, the notification is automatically dismissed when the user clicks it in the panel. pub sticky: Option, - // Set the time that the event in the notification occurred. Notifications in the panel are sorted by this time. - // Timestamp format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Timestamp + /// Set the time that the event in the notification occurred. Notifications in the panel are sorted by this time. + /// Timestamp format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Timestamp pub event_time: Option, - // Set whether or not this notification is relevant only to the current device. + /// Set whether or not this notification is relevant only to the current device. pub local_only: Option, - // Set the relative priority for this notification. + /// Set the relative priority for this notification. pub notification_priority: Option, - // If set to true, use the Android framework's default sound for the notification. + /// If set to true, use the Android framework's default sound for the notification. pub default_sound: Option, - // If set to true, use the Android framework's default vibrate pattern for the notification. + /// If set to true, use the Android framework's default vibrate pattern for the notification. pub default_vibrate_timings: Option, - // If set to true, use the Android framework's default LED light settings for the notification. + /// If set to true, use the Android framework's default LED light settings for the notification. pub default_light_settings: Option, - // Set the vibration pattern to use - // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration + /// Set the vibration pattern to use + /// Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration pub vibrate_timings: Option>, - // Set the Notification.visibility of the notification. + /// Set the Notification.visibility of the notification. pub visibility: Option, - // Sets the number of items this notification represents. + /// Sets the number of items this notification represents. pub notification_count: Option, - // Settings to control the notification's LED blinking rate and color if LED is available on the device. + /// Settings to control the notification's LED blinking rate and color if LED is available on the device. pub light_settings: Option, - // Contains the URL of an image that is going to be displayed in a notification. + /// Contains the URL of an image that is going to be displayed in a notification. pub image: Option, } diff --git a/src/android/color.rs b/src/android/color.rs index 61aeda3a6..a22c0077c 100644 --- a/src/android/color.rs +++ b/src/android/color.rs @@ -1,33 +1,34 @@ use serde::Serialize; #[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#Color +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#Color pub(crate) struct ColorInternal { - // The amount of red in the color as a value in the interval [0, 1]. + /// The amount of red in the color as a value in the interval [0, 1]. red: f32, - // The amount of green in the color as a value in the interval [0, 1]. + /// The amount of green in the color as a value in the interval [0, 1]. green: f32, - // The amount of blue in the color as a value in the interval [0, 1]. + /// The amount of blue in the color as a value in the interval [0, 1]. blue: f32, - // The fraction of this color that should be applied to the pixel. + /// The fraction of this color that should be applied to the pixel. alpha: f32, } -#[derive(Debug, Default)] +#[derive(Debug, Default, Serialize)] +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#Color pub struct Color { - // The amount of red in the color as a value in the interval [0, 1]. + /// The amount of red in the color as a value in the interval [0, 1]. pub red: f32, - // The amount of green in the color as a value in the interval [0, 1]. + /// The amount of green in the color as a value in the interval [0, 1]. pub green: f32, - // The amount of blue in the color as a value in the interval [0, 1]. + /// The amount of blue in the color as a value in the interval [0, 1]. pub blue: f32, - // The fraction of this color that should be applied to the pixel. + /// The fraction of this color that should be applied to the pixel. pub alpha: f32, } diff --git a/src/android/light_settings.rs b/src/android/light_settings.rs index 1ec4e1a1b..1a8850932 100644 --- a/src/android/light_settings.rs +++ b/src/android/light_settings.rs @@ -3,32 +3,32 @@ use serde::Serialize; use super::color::{Color, ColorInternal}; #[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#LightSettings +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#LightSettings pub(crate) struct LightSettingsInternal { - // Set color of the LED with google.type.Color. + /// Set color of the LED with google.type.Color. color: ColorInternal, - // Along with light_off_duration, define the blink rate of LED flashes - // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration + /// Along with light_off_duration, define the blink rate of LED flashes + /// Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration light_on_duration: String, - // Along with light_on_duration, define the blink rate of LED flashes. - // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration + /// Along with light_on_duration, define the blink rate of LED flashes. + /// Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration light_off_duration: String, } #[derive(Debug, Default)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#LightSettings +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#LightSettings pub struct LightSettings { - // Set color of the LED with google.type.Color. + /// Set color of the LED with google.type.Color. pub color: Color, - // Along with light_off_duration, define the blink rate of LED flashes - // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration + /// Along with light_off_duration, define the blink rate of LED flashes + /// Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration pub light_on_duration: String, - // Along with light_on_duration, define the blink rate of LED flashes. - // Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration + /// Along with light_on_duration, define the blink rate of LED flashes. + /// Duration format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Duration pub light_off_duration: String, } diff --git a/src/android/notification_priority.rs b/src/android/notification_priority.rs index bb1bdbf17..1d21c6e03 100644 --- a/src/android/notification_priority.rs +++ b/src/android/notification_priority.rs @@ -3,7 +3,7 @@ use serde::Serialize; #[allow(dead_code)] #[derive(Serialize, Debug)] #[serde(rename_all = "UPPERCASE")] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#notificationpriority +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#notificationpriority pub enum NotificationPriority { PriorityUnspecified, PriorityMin, diff --git a/src/android/visibility.rs b/src/android/visibility.rs index cd8b56dd4..d07d24dda 100644 --- a/src/android/visibility.rs +++ b/src/android/visibility.rs @@ -3,7 +3,7 @@ use serde::Serialize; #[allow(dead_code)] #[derive(Serialize, Debug)] #[serde(rename_all = "UPPERCASE")] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#visibility +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#visibility pub enum Visibility { VisibilityUnspecified, Private, diff --git a/src/apns/apns_config.rs b/src/apns/apns_config.rs index b5ccd1b74..db26718fa 100644 --- a/src/apns/apns_config.rs +++ b/src/apns/apns_config.rs @@ -4,25 +4,29 @@ use serde_json::Value; use super::apns_fcm_options::{ApnsFcmOptions, ApnsFcmOptionsInternal}; #[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#apnsconfig +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#apnsconfig pub(crate) struct ApnsConfigInternal { - // HTTP request headers defined in Apple Push Notification Service. + /// HTTP request headers defined in Apple Push Notification Service. #[serde(skip_serializing_if = "Option::is_none")] headers: Option, - // APNs payload as a JSON object, including both aps dictionary and custom payload. + /// APNs payload as a JSON object, including both aps dictionary and custom payload. #[serde(skip_serializing_if = "Option::is_none")] payload: Option, - // Options for features provided by the FCM SDK for iOS. + /// Options for features provided by the FCM SDK for iOS. #[serde(skip_serializing_if = "Option::is_none")] fcm_options: Option, } #[derive(Debug, Default)] +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#apnsconfig pub struct ApnsConfig { + /// HTTP request headers defined in Apple Push Notification Service. pub headers: Option, + /// APNs payload as a JSON object, including both aps dictionary and custom payload. pub payload: Option, + /// Options for features provided by the FCM SDK for iOS. pub fcm_options: Option, } diff --git a/src/apns/apns_fcm_options.rs b/src/apns/apns_fcm_options.rs index 1ac76e74b..68e41c105 100644 --- a/src/apns/apns_fcm_options.rs +++ b/src/apns/apns_fcm_options.rs @@ -1,21 +1,22 @@ use serde::Serialize; #[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#apnsfcmoptions +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#apnsfcmoptions pub(crate) struct ApnsFcmOptionsInternal { - // Label associated with the message's analytics data. + /// Label associated with the message's analytics data. analytics_label: Option, - // Contains the URL of an image that is going to be displayed in a notification. + /// Contains the URL of an image that is going to be displayed in a notification. image: Option, } #[derive(Debug, Default)] +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#apnsfcmoptions pub struct ApnsFcmOptions { - // Label associated with the message's analytics data. + /// Label associated with the message's analytics data. pub analytics_label: Option, - // Contains the URL of an image that is going to be displayed in a notification. + /// Contains the URL of an image that is going to be displayed in a notification. pub image: Option, } diff --git a/src/message/fcm_options.rs b/src/message/fcm_options.rs index 3c2a50855..2a7172f94 100644 --- a/src/message/fcm_options.rs +++ b/src/message/fcm_options.rs @@ -1,14 +1,16 @@ use serde::Serialize; #[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#fcmoptions +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#fcmoptions pub(crate) struct FcmOptionsInternal { - // Label associated with the message's analytics data. + /// Label associated with the message's analytics data. analytics_label: String, } #[derive(Debug, Default)] +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#fcmoptions pub struct FcmOptions { + /// Label associated with the message's analytics data. pub analytics_label: String, } diff --git a/src/message/mod.rs b/src/message/mod.rs index a84d7834b..6ca73dc91 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -36,51 +36,59 @@ where } #[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#resource:-message +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#resource:-message pub(crate) struct MessageInternal { - // Arbitrary key/value payload, which must be UTF-8 encoded. + /// Arbitrary key/value payload, which must be UTF-8 encoded. #[serde(skip_serializing_if = "Option::is_none")] data: Option, - // Basic notification template to use across all platforms. + /// Basic notification template to use across all platforms. #[serde(skip_serializing_if = "Option::is_none")] notification: Option, - // Android specific options for messages sent through FCM connection server. + /// Android specific options for messages sent through FCM connection server. #[serde(skip_serializing_if = "Option::is_none")] android: Option, - // Webpush protocol options. + /// Webpush protocol options. #[serde(skip_serializing_if = "Option::is_none")] webpush: Option, - // Apple Push Notification Service specific options. + /// Apple Push Notification Service specific options. #[serde(skip_serializing_if = "Option::is_none")] apns: Option, - // Template for FCM SDK feature options to use across all platforms. + /// Template for FCM SDK feature options to use across all platforms. #[serde(skip_serializing_if = "Option::is_none")] fcm_options: Option, - // Target to send a message to. + /// Target to send a message to. #[serde(flatten, serialize_with = "output_target")] target: Target, } /// A `Message` instance is the main object to send to the FCM API. +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#resource:-message #[derive(Debug)] pub struct Message { + /// Arbitrary key/value payload, which must be UTF-8 encoded. pub data: Option, + /// Basic notification template to use across all platforms. pub notification: Option, + /// Android specific options for messages sent through FCM connection server. pub target: Target, + /// Webpush protocol options. pub android: Option, + /// Apple Push Notification Service specific options. pub webpush: Option, + /// Template for FCM SDK feature options to use across all platforms. pub apns: Option, + /// Target to send a message to. pub fcm_options: Option, } impl Message { - /// Complete the build and get a `Message` instance + /// Complete the build and get a `MessageInternal` instance pub(crate) fn finalize(self) -> MessageInternal { MessageInternal { data: self.data, diff --git a/src/message/target.rs b/src/message/target.rs index b90db8584..b1af04f3f 100644 --- a/src/message/target.rs +++ b/src/message/target.rs @@ -1,5 +1,14 @@ use serde::Serialize; +/// Target to send a message to. +/// +/// ```rust +/// use fcm::{Target}; +/// +/// Target::Token("myfcmtoken".to_string()); +/// Target::Topic("my-topic-name".to_string()); +/// Target::Condition("my-condition".to_string()); +/// ``` #[derive(Clone, Serialize, Debug, PartialEq)] #[serde(rename_all = "lowercase")] pub enum Target { diff --git a/src/notification/mod.rs b/src/notification/mod.rs index 2105cdfda..d76d7ed28 100644 --- a/src/notification/mod.rs +++ b/src/notification/mod.rs @@ -4,29 +4,33 @@ mod tests; use serde::Serialize; /// This struct represents a FCM notification. Use the -/// corresponding `NotificationBuilder` to get an instance. You can then use +/// corresponding `Notification` to get an instance. You can then use /// this notification instance when sending a FCM message. #[derive(Serialize, Debug, PartialEq)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#notification +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#notification pub(crate) struct NotificationInternal { - // The notification's title. + /// The notification's title. #[serde(skip_serializing_if = "Option::is_none")] title: Option, - // The notification's body text. + /// The notification's body text. #[serde(skip_serializing_if = "Option::is_none")] body: Option, - // Contains the URL of an image that is going to be downloaded on the device and displayed in a notification. + /// Contains the URL of an image that is going to be downloaded on the device and displayed in a notification. #[serde(skip_serializing_if = "Option::is_none")] image: Option, } -/// A builder to get a `Notification` instance. #[derive(Debug, Default)] pub struct Notification { + /// The notification's title. pub title: Option, + + /// The notification's body text. pub body: Option, + + /// Contains the URL of an image that is going to be downloaded on the device and displayed in a notification. pub image: Option, } diff --git a/src/web/webpush_config.rs b/src/web/webpush_config.rs index d467a1e55..85f1757ff 100644 --- a/src/web/webpush_config.rs +++ b/src/web/webpush_config.rs @@ -4,31 +4,40 @@ use serde_json::Value; use super::webpush_fcm_options::{WebpushFcmOptions, WebpushFcmOptionsInternal}; #[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#webpushconfig +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#webpushconfig pub(crate) struct WebpushConfigInternal { - // HTTP headers defined in webpush protocol. + /// HTTP headers defined in webpush protocol. #[serde(skip_serializing_if = "Option::is_none")] headers: Option, - // Arbitrary key/value payload. + /// Arbitrary key/value payload. #[serde(skip_serializing_if = "Option::is_none")] data: Option, - // Web Notification options as a JSON object. - // Struct format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Struct + /// Web Notification options as a JSON object. + /// Struct format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Struct #[serde(skip_serializing_if = "Option::is_none")] notification: Option, - // Options for features provided by the FCM SDK for Web. + /// Options for features provided by the FCM SDK for Web. #[serde(skip_serializing_if = "Option::is_none")] fcm_options: Option, } #[derive(Debug, Default)] +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#webpushconfig pub struct WebpushConfig { + /// HTTP headers defined in webpush protocol. pub headers: Option, + + /// Arbitrary key/value payload. pub data: Option, + + /// Web Notification options as a JSON object. + /// Struct format: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf?authuser=0#google.protobuf.Struct pub notification: Option, + + /// Options for features provided by the FCM SDK for Web. pub fcm_options: Option, } diff --git a/src/web/webpush_fcm_options.rs b/src/web/webpush_fcm_options.rs index b1caaaa95..56ceb05ab 100644 --- a/src/web/webpush_fcm_options.rs +++ b/src/web/webpush_fcm_options.rs @@ -1,22 +1,22 @@ use serde::Serialize; #[derive(Serialize, Debug)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#webpushfcmoptions +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#webpushfcmoptions pub(crate) struct WebpushFcmOptionsInternal { - // The link to open when the user clicks on the notification. + /// The link to open when the user clicks on the notification. link: String, - // Label associated with the message's analytics data. + /// Label associated with the message's analytics data. analytics_label: String, } #[derive(Debug, Default)] -// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#webpushfcmoptions +/// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?authuser=0#webpushfcmoptions pub struct WebpushFcmOptions { - // The link to open when the user clicks on the notification. + /// The link to open when the user clicks on the notification. pub link: String, - // Label associated with the message's analytics data. + /// Label associated with the message's analytics data. pub analytics_label: String, } From e9d5d9d3823c86ece50f71a630a1ff2e7b899efc Mon Sep 17 00:00:00 2001 From: adnanjpg Date: Wed, 31 Jan 2024 20:26:03 +0300 Subject: [PATCH 6/7] docs: update readme --- README.md | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b74bfbd09..7c361866b 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,70 @@ This fork is a rewrite to use Google's HTTP v1 API. + +# Getting started + +## Installation + +Add the following to your `Cargo.toml` file: + +```toml +[dependencies] +fcm = { git = "https://github.com/rj76/fcm-rust.git" } +``` + +Then, you need to add the credentials described in the [Credentials](#credentials) to a `.env` file at the root of your project. + +## Usage + +For a complete usage example, you may check the [Examples](#examples) section. + +### Import + +```rust +use fcm; +``` + +### Create a client instance + +```rust +let client = fcm::Client::new(); +``` + +### Construct a message + +```rust +let message = fcm::Message { + data: None, + notification: Some(Notification { + title: Some("I'm high".to_string()), + body: Some(format!("it's {}", chrono::Utc::now())), + ..Default::default() + }), + target: Target::Token(device_token), + fcm_options: Some(FcmOptions { + analytics_label: "analytics_label".to_string(), + }), + android: Some(AndroidConfig { + priority: Some(fcm::AndroidMessagePriority::High), + notification: Some(AndroidNotification { + title: Some("I'm Android high".to_string()), + body: Some(format!("Hi Android, it's {}", chrono::Utc::now())), + ..Default::default() + }), + ..Default::default() + }), + apns: Some(ApnsConfig { ..Default::default() }), + webpush: Some(WebpushConfig { ..Default::default() }), +} +``` + +### Send the message + +```rust +let response = client.send(message).await?; +``` + # Credentials This library expects the Google credentials JSON location to be @@ -19,4 +83,13 @@ Please follow the instructions in the [Firebase Documentation](https://firebase. ## Examples -Check out the examples directory for a simple sender. +For a complete usage example, you may check out the [`simple_sender`](examples/simple_sender.rs) example. + +To run the example, first of all clone the [`.env.example`](.env.example) file to `.env` and fill in the required values. + +You can find info about the required credentials in the [Credentials](#credentials) section. + +Then run the example with `cargo run --example simple_sender -- -t ` + + + From 2128a958a4482c12382ca461486a68691b3d813a Mon Sep 17 00:00:00 2001 From: adnanjpg Date: Sun, 3 Mar 2024 11:21:46 +0300 Subject: [PATCH 7/7] refactor: #4 pr review --- a.sh | 1 - src/client/mod.rs | 7 +------ src/message/tests.rs | 11 ----------- 3 files changed, 1 insertion(+), 18 deletions(-) delete mode 100644 a.sh diff --git a/a.sh b/a.sh deleted file mode 100644 index 5d89ab970..000000000 --- a/a.sh +++ /dev/null @@ -1 +0,0 @@ -cargo run --example simple_sender -- -t fP0EXs_HQ4Gj1sE5xBP6LP:APA91bGxDOk4GzvIvvGFPa6kDWOOHm2zYA3tUN1jYwErSBq_s27NioYjYWfUpgUW3_LIsreSw1pbtTjEpCK_cKR6AHxo1PGZ1Yd3S8lyX148I_LgKaTNYorG2QtkcwfB9Nm5yKGJwLaG \ No newline at end of file diff --git a/src/client/mod.rs b/src/client/mod.rs index ff58cad37..f2418fa87 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -109,7 +109,7 @@ impl Client { Err(err) => return Err(err.to_string()), }; - let token_no_bearer = access_token.split(" ").collect::>()[1]; + let token_no_bearer = access_token.split(char::is_whitespace).collect::>()[1]; Ok(token_no_bearer.to_string()) } @@ -140,11 +140,6 @@ impl Client { .body(Body::from(payload)) .build()?; - // print the body - let body = request.body().unwrap(); - let body = std::str::from_utf8(body.as_bytes().unwrap()).unwrap(); - println!("body: {}", body); - let response = self.http_client.execute(request).await?; let response_status = response.status(); diff --git a/src/message/tests.rs b/src/message/tests.rs index 143ec9edc..8cb9fb713 100644 --- a/src/message/tests.rs +++ b/src/message/tests.rs @@ -161,17 +161,6 @@ fn should_be_able_to_render_a_full_condition_message_to_json() { #[test] fn should_set_notifications() { let target = Target::Token("token".to_string()); - let msg = Message { - target: target.clone(), - data: None, - notification: None, - android: None, - webpush: None, - apns: None, - fcm_options: None, - }; - - assert_eq!(msg.notification.is_none(), true); let nm = Notification { title: None,