Skip to content

Commit

Permalink
Add the unregister channel route (#194)
Browse files Browse the repository at this point in the history
Closes #180
  • Loading branch information
AzureMarker committed Jul 28, 2020
1 parent ce51957 commit 5b23dbb
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 2 deletions.
38 changes: 37 additions & 1 deletion autoendpoint/src/db/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ pub trait DbClient: Send + Sync {
/// Get the set of channel IDs for a user
async fn get_channels(&self, uaid: Uuid) -> DbResult<HashSet<Uuid>>;

/// Remove a channel from a user. Returns if the removed channel did exist.
async fn remove_channel(&self, uaid: Uuid, channel_id: Uuid) -> DbResult<bool>;

/// Remove the node ID from a user in the router table.
/// The node ID will only be cleared if `connected_at` matches up with the
/// item's `connected_at`.
Expand Down Expand Up @@ -244,7 +247,7 @@ impl DbClient for DbClientImpl {
},
update_expression: Some("ADD chids :channel_id SET expiry = :expiry".to_string()),
expression_attribute_values: Some(hashmap! {
":channel_id".to_string() => val!(SS => Some(channel_id.to_hyphenated())),
":channel_id".to_string() => val!(SS => Some(channel_id)),
":expiry".to_string() => val!(N => sec_since_epoch() + MAX_CHANNEL_TTL)
}),
..Default::default()
Expand Down Expand Up @@ -298,6 +301,39 @@ impl DbClient for DbClientImpl {
Ok(channels)
}

async fn remove_channel(&self, uaid: Uuid, channel_id: Uuid) -> DbResult<bool> {
let input = UpdateItemInput {
table_name: self.message_table.clone(),
key: ddb_item! {
uaid: s => uaid.to_simple().to_string(),
chidmessageid: s => " ".to_string()
},
update_expression: Some("DELETE chids :channel_id SET expiry = :expiry".to_string()),
expression_attribute_values: Some(hashmap! {
":channel_id".to_string() => val!(SS => Some(channel_id)),
":expiry".to_string() => val!(N => sec_since_epoch() + MAX_CHANNEL_TTL)
}),
return_values: Some("UPDATED_OLD".to_string()),
..Default::default()
};

let output = retry_policy()
.retry_if(
|| self.ddb.update_item(input.clone()),
retryable_updateitem_error(self.metrics.clone()),
)
.await?;

// Check if the old channel IDs contain the removed channel
Ok(output
.attributes
.as_ref()
.and_then(|map| map.get("chids"))
.and_then(|item| item.ss.as_ref())
.map(|channel_ids| channel_ids.contains(&channel_id.to_string()))
.unwrap_or(false))
}

async fn remove_node_id(&self, uaid: Uuid, node_id: String, connected_at: u64) -> DbResult<()> {
let input = UpdateItemInput {
key: ddb_item! { uaid: s => uaid.to_simple().to_string() },
Expand Down
6 changes: 6 additions & 0 deletions autoendpoint/src/db/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ mockall::mock! {

fn get_channels(&self, uaid: Uuid) -> DbResult<HashSet<Uuid>>;

fn remove_channel(&self, uaid: Uuid, channel_id: Uuid) -> DbResult<bool>;

fn remove_node_id(&self, uaid: Uuid, node_id: String, connected_at: u64) -> DbResult<()>;

fn save_message(&self, uaid: Uuid, message: Notification) -> DbResult<()>;
Expand Down Expand Up @@ -69,6 +71,10 @@ impl DbClient for Arc<MockDbClient> {
Arc::as_ref(self).get_channels(uaid)
}

async fn remove_channel(&self, uaid: Uuid, channel_id: Uuid) -> DbResult<bool> {
Arc::as_ref(self).remove_channel(uaid, channel_id)
}

async fn remove_node_id(&self, uaid: Uuid, node_id: String, connected_at: u64) -> DbResult<()> {
Arc::as_ref(self).remove_node_id(uaid, node_id, connected_at)
}
Expand Down
30 changes: 30 additions & 0 deletions autoendpoint/src/routes/registration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,36 @@ pub async fn get_channels_route(
})))
}

/// Handle the `DELETE /v1/{router_type}/{app_id}/registration/{uaid}/subscription/{chid}` route
pub async fn unregister_channel_route(
_auth: AuthorizationCheck,
path_args: RegistrationPathArgsWithUaid,
state: Data<ServerState>,
request: HttpRequest,
) -> ApiResult<HttpResponse> {
let channel_id = request
.match_info()
.get("chid")
.expect("{chid} must be part of the path")
.parse::<Uuid>()
.map_err(|_| ApiErrorKind::NoSubscription)?;

debug!(
"Unregistering CHID {} for UAID {}",
channel_id, path_args.uaid
);

incr_metric("ua.command.unregister", &state.metrics, &request);
let channel_did_exist = state.ddb.remove_channel(path_args.uaid, channel_id).await?;

if channel_did_exist {
Ok(HttpResponse::Ok().finish())
} else {
debug!("Channel did not exist");
Err(ApiErrorKind::NoSubscription.into())
}
}

/// Increment a metric with data from the request
fn incr_metric(name: &str, metrics: &StatsdClient, request: &HttpRequest) {
metrics
Expand Down
9 changes: 8 additions & 1 deletion autoendpoint/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use crate::routers::apns::router::ApnsRouter;
use crate::routers::fcm::router::FcmRouter;
use crate::routes::health::{health_route, lb_heartbeat_route, status_route, version_route};
use crate::routes::registration::{
get_channels_route, new_channel_route, register_uaid_route, update_token_route,
get_channels_route, new_channel_route, register_uaid_route, unregister_channel_route,
update_token_route,
};
use crate::routes::webpush::{delete_notification_route, webpush_route};
use crate::settings::Settings;
Expand Down Expand Up @@ -109,6 +110,12 @@ impl Server {
web::resource("/v1/{router_type}/{app_id}/registration/{uaid}/subscription")
.route(web::post().to(new_channel_route)),
)
.service(
web::resource(
"/v1/{router_type}/{app_id}/registration/{uaid}/subscription/{chid}",
)
.route(web::delete().to(unregister_channel_route)),
)
// Health checks
.service(web::resource("/status").route(web::get().to(status_route)))
.service(web::resource("/health").route(web::get().to(health_route)))
Expand Down

0 comments on commit 5b23dbb

Please sign in to comment.