fix(sync): ignore unknown sync items to provide forward compatibility

This commit is contained in:
link2xt
2023-11-15 17:24:55 +00:00
parent bf8e74198d
commit b0ef082b2a

View File

@@ -52,10 +52,18 @@ pub(crate) enum SyncData {
}, },
} }
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum SyncDataOrUnknown {
SyncData(SyncData),
Unknown(serde_json::Value),
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub(crate) struct SyncItem { pub(crate) struct SyncItem {
timestamp: i64, timestamp: i64,
data: SyncData,
data: SyncDataOrUnknown,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@@ -63,6 +71,12 @@ pub(crate) struct SyncItems {
items: Vec<SyncItem>, items: Vec<SyncItem>,
} }
impl From<SyncData> for SyncDataOrUnknown {
fn from(sync_data: SyncData) -> Self {
Self::SyncData(sync_data)
}
}
impl Context { impl Context {
/// Adds an item to the list of items that should be synchronized to other devices. /// Adds an item to the list of items that should be synchronized to other devices.
/// ///
@@ -79,7 +93,10 @@ impl Context {
return Ok(()); return Ok(());
} }
let item = SyncItem { timestamp, data }; let item = SyncItem {
timestamp,
data: data.into(),
};
let item = serde_json::to_string(&item)?; let item = serde_json::to_string(&item)?;
self.sql self.sql
.execute("INSERT INTO multi_device_sync (item) VALUES(?);", (item,)) .execute("INSERT INTO multi_device_sync (item) VALUES(?);", (item,))
@@ -242,9 +259,15 @@ impl Context {
info!(self, "executing {} sync item(s)", items.items.len()); info!(self, "executing {} sync item(s)", items.items.len());
for item in &items.items { for item in &items.items {
match &item.data { match &item.data {
AddQrToken(token) => self.add_qr_token(token).await, SyncDataOrUnknown::SyncData(data) => match data {
DeleteQrToken(token) => self.delete_qr_token(token).await, AddQrToken(token) => self.add_qr_token(token).await,
AlterChat { id, action } => self.sync_alter_chat(id, action).await, DeleteQrToken(token) => self.delete_qr_token(token).await,
AlterChat { id, action } => self.sync_alter_chat(id, action).await,
},
SyncDataOrUnknown::Unknown(data) => {
warn!(self, "Ignored unknown sync item: {data}.");
Ok(())
}
} }
.log_err(self) .log_err(self)
.ok(); .ok();
@@ -383,48 +406,32 @@ mod tests {
assert!(t.parse_sync_items(r#"{"badname":[]}"#.to_string()).is_err()); assert!(t.parse_sync_items(r#"{"badname":[]}"#.to_string()).is_err());
assert!(t.parse_sync_items( for bad_item_example in [
r#"{"items":[{"timestamp":1631781316,"data":{"BadItem":{"invitenumber":"in","auth":"a","grpid":null}}}]}"# r#"{"items":[{"timestamp":1631781316,"data":{"BadItem":{"invitenumber":"in","auth":"a","grpid":null}}}]}"#,
.to_string(), r#"{"items":[{"timestamp":1631781316,"data":{"AddQrToken":{"invitenumber":"in","auth":123}}}]}"#, // `123` is invalid for `String`
) r#"{"items":[{"timestamp":1631781316,"data":{"AddQrToken":{"invitenumber":"in","auth":true}}}]}"#, // `true` is invalid for `String`
.is_err()); r#"{"items":[{"timestamp":1631781316,"data":{"AddQrToken":{"invitenumber":"in","auth":[]}}}]}"#, // `[]` is invalid for `String`
r#"{"items":[{"timestamp":1631781316,"data":{"AddQrToken":{"invitenumber":"in","auth":{}}}}]}"#, // `{}` is invalid for `String`
assert!(t.parse_sync_items( r#"{"items":[{"timestamp":1631781316,"data":{"AddQrToken":{"invitenumber":"in","grpid":null}}}]}"#, // missing field
r#"{"items":[{"timestamp":1631781316,"data":{"AddQrToken":{"invitenumber":"in","auth":123}}}]}"#.to_string(), r#"{"items":[{"timestamp":1631781316,"data":{"AlterChat":{"id":{"ContactAddr":"bob@example.net"},"action":"Burn"}}}]}"#, // Unknown enum value
) ] {
.is_err()); // `123` is invalid for `String` let sync_items = t.parse_sync_items(bad_item_example.to_string()).unwrap();
assert_eq!(sync_items.items.len(), 1);
assert!(t.parse_sync_items( assert!(matches!(sync_items.items[0].timestamp, 1631781316));
r#"{"items":[{"timestamp":1631781316,"data":{"AddQrToken":{"invitenumber":"in","auth":true}}}]}"#.to_string(), assert!(matches!(
) sync_items.items[0].data,
.is_err()); // `true` is invalid for `String` SyncDataOrUnknown::Unknown(_)
));
assert!(t.parse_sync_items( }
r#"{"items":[{"timestamp":1631781316,"data":{"AddQrToken":{"invitenumber":"in","auth":[]}}}]}"#.to_string(),
)
.is_err()); // `[]` is invalid for `String`
assert!(t.parse_sync_items(
r#"{"items":[{"timestamp":1631781316,"data":{"AddQrToken":{"invitenumber":"in","auth":{}}}}]}"#.to_string(),
)
.is_err()); // `{}` is invalid for `String`
assert!(t.parse_sync_items(
r#"{"items":[{"timestamp":1631781316,"data":{"AddQrToken":{"invitenumber":"in","grpid":null}}}]}"#.to_string(),
)
.is_err()); // missing field
assert!(t.parse_sync_items(
r#"{"items":[{"timestamp":1631781318,"data":{"AlterChat":{"id":{"ContactAddr":"bob@example.net"},"action":"Burn"}}}]}"#.to_string(),
)
.is_err()); // Unknown enum value
// Test enums inside items and SystemTime // Test enums inside items and SystemTime
let sync_items = t.parse_sync_items( let sync_items = t.parse_sync_items(
r#"{"items":[{"timestamp":1631781318,"data":{"AlterChat":{"id":{"ContactAddr":"bob@example.net"},"action":{"SetMuted":{"Until":{"secs_since_epoch":42,"nanos_since_epoch":999000000}}}}}}]}"#.to_string(), r#"{"items":[{"timestamp":1631781318,"data":{"AlterChat":{"id":{"ContactAddr":"bob@example.net"},"action":{"SetMuted":{"Until":{"secs_since_epoch":42,"nanos_since_epoch":999000000}}}}}}]}"#.to_string(),
)?; )?;
assert_eq!(sync_items.items.len(), 1); assert_eq!(sync_items.items.len(), 1);
let AlterChat { id, action } = &sync_items.items.get(0).unwrap().data else { let SyncDataOrUnknown::SyncData(AlterChat { id, action }) =
&sync_items.items.get(0).unwrap().data
else {
bail!("bad item"); bail!("bad item");
}; };
assert_eq!( assert_eq!(
@@ -466,7 +473,9 @@ mod tests {
)?; )?;
assert_eq!(sync_items.items.len(), 1); assert_eq!(sync_items.items.len(), 1);
if let AddQrToken(token) = &sync_items.items.get(0).unwrap().data { if let SyncDataOrUnknown::SyncData(AddQrToken(token)) =
&sync_items.items.get(0).unwrap().data
{
assert_eq!(token.invitenumber, "in"); assert_eq!(token.invitenumber, "in");
assert_eq!(token.auth, "yip"); assert_eq!(token.auth, "yip");
assert_eq!(token.grpid, None); assert_eq!(token.grpid, None);