Skip to content

Commit bf6c2cc

Browse files
committed
Feat: Add urgency support
Allow clients to define the minimum urgency of messages they wish to receive. For example, a client with low battery can request a high urgency message only until their battery is good again. Note: this is a resubmit of #815.
1 parent 4d252b4 commit bf6c2cc

File tree

14 files changed

+143
-6
lines changed

14 files changed

+143
-6
lines changed

autoconnect/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ docopt = "1.1"
5656
default = ["bigtable", "reliable_report"]
5757
bigtable = ["autopush_common/bigtable", "autoconnect_settings/bigtable"]
5858
emulator = ["bigtable"]
59+
urgency = ["autoconnect_ws/urgency"]
5960
log_vapid = []
6061
reliable_report = [
6162
"autoconnect_settings/reliable_report",

autoconnect/autoconnect-common/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ autopush_common.workspace = true
2727

2828
[features]
2929
test-support = []
30+
urgency = []

autoconnect/autoconnect-common/src/protocol.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ use uuid::Uuid;
1414

1515
use autopush_common::notification::Notification;
1616

17+
#[cfg(feature = "urgency")]
18+
use autopush_common::db::Urgency;
19+
1720
#[derive(Debug, Eq, PartialEq, Serialize)]
1821
#[serde(untagged)]
1922
pub enum BroadcastValue {
@@ -66,6 +69,11 @@ pub enum ClientMessage {
6669
version: String,
6770
},
6871

72+
#[cfg(feature = "urgency")]
73+
Urgency {
74+
min: Urgency,
75+
},
76+
6977
Ping,
7078
}
7179

@@ -123,6 +131,11 @@ pub enum ServerMessage {
123131

124132
Notification(Notification),
125133

134+
#[cfg(feature = "urgency")]
135+
Urgency {
136+
status: u32,
137+
},
138+
126139
Ping,
127140
}
128141

autoconnect/autoconnect-ws/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ reliable_report = [
3939
"autopush_common/reliable_report",
4040
"autoconnect_ws_sm/reliable_report",
4141
]
42+
urgency = ["autoconnect_ws_sm/urgency"]

autoconnect/autoconnect-ws/autoconnect-ws-sm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ autoconnect_common = { workspace = true, features = ["test-support"] }
3333

3434
[features]
3535
reliable_report = []
36+
urgency = ["autoconnect_common/urgency"]

autoconnect/autoconnect-ws/autoconnect-ws-sm/src/identified/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use autoconnect_common::{
1212

1313
use autoconnect_settings::{AppState, Settings};
1414
use autopush_common::{
15-
db::User,
15+
db::{Urgency, User},
1616
notification::Notification,
1717
util::{ms_since_epoch, user_agent::UserAgentInfo},
1818
};
@@ -297,6 +297,8 @@ pub struct ClientFlags {
297297
pub old_record_version: bool,
298298
/// First time a user has connected "today"
299299
pub emit_channel_metrics: bool,
300+
/// Minimum urgency
301+
pub min_urgency: Urgency,
300302
}
301303

302304
impl Default for ClientFlags {
@@ -307,6 +309,7 @@ impl Default for ClientFlags {
307309
check_storage: false,
308310
old_record_version: false,
309311
emit_channel_metrics: false,
312+
min_urgency: Urgency::VeryLow,
310313
}
311314
}
312315
}

autoconnect/autoconnect-ws/autoconnect-ws-sm/src/identified/on_client_msg.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ use autoconnect_common::{
99
};
1010
use autopush_common::{endpoint::make_endpoint, util::sec_since_epoch};
1111

12+
#[cfg(feature = "urgency")]
13+
use autopush_common::{db::Urgency, util::ms_since_epoch};
14+
1215
use super::WebPushClient;
1316
use crate::error::{SMError, SMErrorKind};
1417

@@ -38,6 +41,8 @@ impl WebPushClient {
3841
self.nack(code);
3942
Ok(vec![])
4043
}
44+
#[cfg(feature = "urgency")]
45+
ClientMessage::Urgency { min } => Ok(self.change_min_urgency(min).await?),
4146
ClientMessage::Ping => Ok(vec![self.ping()?]),
4247
}
4348
}
@@ -330,4 +335,40 @@ impl WebPushClient {
330335
Ok(vec![])
331336
}
332337
}
338+
339+
/// Update minimum urgency for the user and the flag
340+
///
341+
/// If the new urgency is lower than the previous one,
342+
/// We check pending messages, to send messages that were
343+
/// retained because of their urgency
344+
#[cfg(feature = "urgency")]
345+
async fn change_min_urgency(
346+
&mut self,
347+
new_min: Urgency,
348+
) -> Result<Vec<ServerMessage>, SMError> {
349+
// Change the min urgency
350+
self.flags.min_urgency = new_min;
351+
352+
if let Some(mut user) = self.app_state.db.get_user(&self.uaid).await? {
353+
// If the user hasn't set a minimum urgency yet, they receive all messages,
354+
// which is equivalent to setting very-low as a minimum
355+
let current_urgency = user.urgency.unwrap_or(Urgency::VeryLow);
356+
357+
// We update the user
358+
user.urgency = Some(new_min);
359+
user.connected_at = ms_since_epoch();
360+
self.app_state.db.update_user(&mut user).await?;
361+
362+
let mut res = vec![ServerMessage::Urgency { status: 200 }];
363+
// if new urgency < previous: fetch pending messages
364+
if new_min < current_urgency {
365+
self.ack_state.unacked_stored_highest = None;
366+
self.current_timestamp = None;
367+
res.append(&mut self.check_storage().await?);
368+
}
369+
Ok(res)
370+
} else {
371+
Ok(vec![ServerMessage::Urgency { status: 404 }])
372+
}
373+
}
333374
}

autoconnect/autoconnect-ws/autoconnect-ws-sm/src/identified/on_server_notif.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ use autopush_common::{
66
};
77

88
use super::WebPushClient;
9-
use crate::error::{SMError, SMErrorKind};
9+
use crate::{
10+
error::{SMError, SMErrorKind},
11+
identified::Urgency,
12+
};
1013

1114
impl WebPushClient {
1215
/// Handle a `ServerNotification` for this user
@@ -119,7 +122,11 @@ impl WebPushClient {
119122
let mut expired_topic_sort_keys = vec![];
120123
messages.retain(|msg| {
121124
if !msg.expired(now_sec) {
122-
return true;
125+
if let Some(headers) = msg.headers.as_ref() {
126+
return Urgency::from(headers.get("urgency")) >= self.flags.min_urgency;
127+
} else {
128+
return true;
129+
}
123130
}
124131
if msg.sortkey_timestamp.is_none() {
125132
expired_topic_sort_keys.push(msg.chidmessageid());

autoconnect/autoconnect-ws/autoconnect-ws-sm/src/unidentified.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use autoconnect_common::{
99
};
1010
use autoconnect_settings::{AppState, Settings};
1111
use autopush_common::{
12-
db::{User, USER_RECORD_VERSION},
12+
db::{Urgency, User, USER_RECORD_VERSION},
1313
util::{ms_since_epoch, ms_utc_midnight},
1414
};
1515

@@ -145,6 +145,7 @@ impl UnidentifiedClient {
145145
.record_version
146146
.map_or(true, |rec_ver| rec_ver < USER_RECORD_VERSION),
147147
emit_channel_metrics: user.connected_at < ms_utc_midnight(),
148+
min_urgency: user.urgency.unwrap_or(Urgency::VeryLow),
148149
..Default::default()
149150
};
150151
user.node_id = Some(self.app_state.router_url.to_owned());

autoendpoint/src/extractors/notification_headers.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use lazy_static::lazy_static;
77
use regex::Regex;
88
use std::cmp::min;
99
use std::collections::HashMap;
10-
use validator::Validate;
10+
use validator::{Validate, ValidationError};
1111
use validator_derive::Validate;
1212

1313
lazy_static! {
@@ -37,6 +37,9 @@ pub struct NotificationHeaders {
3737
)]
3838
pub topic: Option<String>,
3939

40+
#[validate(custom(function = "validate_urgency"))]
41+
pub urgency: Option<String>,
42+
4043
// These fields are validated separately, because the validation is complex
4144
// and based upon the content encoding
4245
pub encoding: Option<String>,
@@ -45,10 +48,21 @@ pub struct NotificationHeaders {
4548
pub crypto_key: Option<String>,
4649
}
4750

51+
fn validate_urgency(value: &str) -> Result<(), ValidationError> {
52+
if ["very-low", "low", "normal", "high"].contains(&value) {
53+
Ok(())
54+
} else {
55+
Err(ValidationError::new(
56+
"Value not equal to \"very-low\", \"low\", \"normal\" or \"high\"",
57+
))
58+
}
59+
}
60+
4861
impl From<NotificationHeaders> for HashMap<String, String> {
4962
fn from(headers: NotificationHeaders) -> Self {
5063
let mut map = HashMap::new();
5164

65+
map.insert_opt("urgency", headers.urgency);
5266
map.insert_opt("encoding", headers.encoding);
5367
map.insert_opt("encryption", headers.encryption);
5468
map.insert_opt("encryption_key", headers.encryption_key);
@@ -73,11 +87,13 @@ impl NotificationHeaders {
7387
.map(|ttl| min(ttl, MAX_NOTIFICATION_TTL as i64))
7488
.ok_or(ApiErrorKind::NoTTL)?;
7589
let topic = get_owned_header(req, "topic");
90+
let urgency = get_owned_header(req, "urgency");
7691

7792
let headers = if has_data {
7893
NotificationHeaders {
7994
ttl,
8095
topic,
96+
urgency,
8197
encoding: get_owned_header(req, "content-encoding"),
8298
encryption: get_owned_header(req, "encryption").map(Self::strip_header),
8399
encryption_key: get_owned_header(req, "encryption-key"),
@@ -88,6 +104,7 @@ impl NotificationHeaders {
88104
NotificationHeaders {
89105
ttl,
90106
topic,
107+
urgency,
91108
encoding: None,
92109
encryption: None,
93110
encryption_key: None,
@@ -359,6 +376,7 @@ mod tests {
359376
NotificationHeaders {
360377
ttl: 10,
361378
topic: None,
379+
urgency: None,
362380
encoding: Some("aesgcm".to_string()),
363381
encryption: Some("salt=foo".to_string()),
364382
encryption_key: None,
@@ -384,6 +402,7 @@ mod tests {
384402
NotificationHeaders {
385403
ttl: 10,
386404
topic: None,
405+
urgency: None,
387406
encoding: Some("aes128gcm".to_string()),
388407
encryption: Some("notsalt=foo".to_string()),
389408
encryption_key: None,
@@ -410,6 +429,7 @@ mod tests {
410429
NotificationHeaders {
411430
ttl: 10,
412431
topic: None,
432+
urgency: None,
413433
encoding: Some("aesgcm".to_string()),
414434
encryption: Some("salt=foo".to_string()),
415435
encryption_key: None,

0 commit comments

Comments
 (0)