Skip to content

Commit

Permalink
feat(notifications): add device token endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
UncleSamtoshi committed Jan 22, 2024
1 parent 25f250c commit 9935d94
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 1 deletion.
21 changes: 21 additions & 0 deletions core/notifications/proto/notifications.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ service NotificationsService {
rpc DisableNotificationCategory (DisableNotificationCategoryRequest) returns (DisableNotificationCategoryResponse) {}
rpc GetNotificationSettings (GetNotificationSettingsRequest) returns (GetNotificationSettingsResponse) {}
rpc UpdateUserLocale (UpdateUserLocaleRequest) returns (UpdateUserLocaleResponse) {}
rpc AddPushDeviceToken (AddPushDeviceTokenRequest) returns (AddPushDeviceTokenResponse) {}
rpc RemovePushDeviceToken (RemovePushDeviceTokenRequest) returns (RemovePushDeviceTokenResponse) {}
}

enum NotificationChannel {
Expand Down Expand Up @@ -48,6 +50,7 @@ message EnableNotificationChannelResponse {
message NotificationSettings {
ChannelNotificationSettings push = 1;
optional string locale = 2;
repeated string push_device_tokens = 3;
}

message ChannelNotificationSettings {
Expand Down Expand Up @@ -100,3 +103,21 @@ message UpdateUserLocaleRequest {
message UpdateUserLocaleResponse {
NotificationSettings notification_settings = 1;
}

message AddPushDeviceTokenRequest {
string user_id = 1;
string device_token = 2;
}

message AddPushDeviceTokenResponse {
NotificationSettings notification_settings = 1;
}

message RemovePushDeviceTokenRequest {
string user_id = 1;
string device_token = 2;
}

message RemovePushDeviceTokenResponse {
NotificationSettings notification_settings = 1;
}
32 changes: 32 additions & 0 deletions core/notifications/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,36 @@ impl NotificationsApp {
self.settings.persist(&mut user_settings).await?;
Ok(user_settings)
}

#[instrument(name = "app.add_push_device_token", skip(self), err)]
pub async fn add_push_device_token(
&self,
user_id: GaloyUserId,
device_token: PushDeviceToken,
) -> Result<UserNotificationSettings, ApplicationError> {
let mut user_settings = self
.settings
.find_for_user_id(&user_id)
.await?
.unwrap_or_else(|| UserNotificationSettings::new(user_id));
user_settings.add_push_device_token(device_token);
self.settings.persist(&mut user_settings).await?;
Ok(user_settings)
}

#[instrument(name = "app.remove_push_device_token", skip(self), err)]
pub async fn remove_push_device_token(
&self,
user_id: GaloyUserId,
device_token: PushDeviceToken,
) -> Result<UserNotificationSettings, ApplicationError> {
let mut user_settings = self
.settings
.find_for_user_id(&user_id)
.await?
.unwrap_or_else(|| UserNotificationSettings::new(user_id));
user_settings.remove_push_device_token(device_token);
self.settings.persist(&mut user_settings).await?;
Ok(user_settings)
}
}
5 changes: 5 additions & 0 deletions core/notifications/src/grpc/server/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ impl From<user_notification_settings::UserNotificationSettings> for proto::Notif
.map(|category| proto::NotificationCategory::from(category).into())
.collect(),
}),
push_device_tokens: settings
.push_device_tokens()
.into_iter()
.map(|token| token.to_string())
.collect(),
}
}
}
Expand Down
48 changes: 47 additions & 1 deletion core/notifications/src/grpc/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use self::proto::{notifications_service_server::NotificationsService, *};
use super::config::*;
use crate::{
app::*,
primitives::{GaloyUserId, UserNotificationCategory, UserNotificationChannel},
primitives::{GaloyUserId, PushDeviceToken, UserNotificationCategory, UserNotificationChannel},
};

pub struct Notifications {
Expand Down Expand Up @@ -178,6 +178,52 @@ impl NotificationsService for Notifications {
notification_settings: Some(notification_settings.into()),
}))
}

#[instrument(name = "notifications.add_push_device_token", skip_all, err)]
async fn add_push_device_token(
&self,
request: Request<AddPushDeviceTokenRequest>,
) -> Result<Response<AddPushDeviceTokenResponse>, Status> {
grpc::extract_tracing(&request);
let request = request.into_inner();
let AddPushDeviceTokenRequest {
user_id,
device_token,
} = request;
let user_id = GaloyUserId::from(user_id);
let device_token = PushDeviceToken::from(device_token);
let notification_settings = self
.app
.add_push_device_token(user_id, device_token)
.await?;

Ok(Response::new(AddPushDeviceTokenResponse {
notification_settings: Some(notification_settings.into()),
}))
}

#[instrument(name = "notifications.remove_push_device_token", skip_all, err)]
async fn remove_push_device_token(
&self,
request: Request<RemovePushDeviceTokenRequest>,
) -> Result<Response<RemovePushDeviceTokenResponse>, Status> {
grpc::extract_tracing(&request);
let request = request.into_inner();
let RemovePushDeviceTokenRequest {
user_id,
device_token,
} = request;
let user_id = GaloyUserId::from(user_id);
let device_token = PushDeviceToken::from(device_token);
let notification_settings = self
.app
.remove_push_device_token(user_id, device_token)
.await?;

Ok(Response::new(RemovePushDeviceTokenResponse {
notification_settings: Some(notification_settings.into()),
}))
}
}

pub(crate) async fn start(
Expand Down
20 changes: 20 additions & 0 deletions core/notifications/src/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,26 @@ impl std::fmt::Display for GaloyLocale {
}
}

#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, Hash)]
pub struct PushDeviceToken(String);
impl From<String> for PushDeviceToken {
fn from(s: String) -> Self {
Self(s)
}
}

impl AsRef<str> for PushDeviceToken {
fn as_ref(&self) -> &str {
&self.0
}
}

impl std::fmt::Display for PushDeviceToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}

#[derive(async_graphql::Enum, Debug, Copy, Clone, Eq, PartialEq, Deserialize, Serialize)]
#[graphql(name = "UserNotificationChannel")]
pub enum UserNotificationChannel {
Expand Down
65 changes: 65 additions & 0 deletions core/notifications/src/user_notification_settings/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ pub enum UserNotificationSettingsEvent {
locale: GaloyLocale,
},
LocaleSetToDefault,
PushDeviceTokenAdded {
token: PushDeviceToken,
},
PushDeviceTokenRemoved {
token: PushDeviceToken,
},
}

impl EntityEvent for UserNotificationSettingsEvent {
Expand Down Expand Up @@ -172,6 +178,40 @@ impl UserNotificationSettings {
self.is_channel_enabled(channel)
&& !self.disabled_categories_for(channel).contains(&category)
}

pub fn push_device_tokens(&self) -> HashSet<PushDeviceToken> {
self.events.iter().fold(HashSet::new(), |mut acc, event| {
match event {
UserNotificationSettingsEvent::PushDeviceTokenAdded { token } => {
acc.insert(token.clone());
}
UserNotificationSettingsEvent::PushDeviceTokenRemoved { token } => {
acc.remove(token);
}
_ => (),
}
acc
})
}

pub fn add_push_device_token(&mut self, token: PushDeviceToken) {
if self.push_device_tokens().contains(&token) {
return;
}
self.events
.push(UserNotificationSettingsEvent::PushDeviceTokenAdded { token });
}

pub fn remove_push_device_token(&mut self, token: PushDeviceToken) {
if !self.push_device_tokens().contains(&token) {
return;
}

self.events
.push(UserNotificationSettingsEvent::PushDeviceTokenRemoved { token })
}

// pub fn remove_push_device_token()
}

impl TryFrom<EntityEvents<UserNotificationSettingsEvent>> for UserNotificationSettings {
Expand Down Expand Up @@ -322,4 +362,29 @@ mod tests {
settings.set_locale_to_default();
assert_eq!(settings.locale(), None);
}

#[test]
fn can_add_and_remove_push_device_tokens() {
let events = initial_events();
let mut settings = UserNotificationSettings::try_from(events).expect("Could not hydrate");
assert_eq!(settings.push_device_tokens(), HashSet::new());
settings.add_push_device_token(PushDeviceToken::from("token1".to_string()));
assert_eq!(
settings.push_device_tokens(),
HashSet::from([PushDeviceToken::from("token1".to_string())])
);
settings.add_push_device_token(PushDeviceToken::from("token2".to_string()));
assert_eq!(
settings.push_device_tokens(),
HashSet::from([
PushDeviceToken::from("token1".to_string()),
PushDeviceToken::from("token2".to_string())
])
);
settings.remove_push_device_token(PushDeviceToken::from("token1".to_string()));
assert_eq!(
settings.push_device_tokens(),
HashSet::from([PushDeviceToken::from("token2".to_string())])
);
}
}

0 comments on commit 9935d94

Please sign in to comment.