diff --git a/services/identity/src/database/device_list.rs b/services/identity/src/database/device_list.rs --- a/services/identity/src/database/device_list.rs +++ b/services/identity/src/database/device_list.rs @@ -128,14 +128,14 @@ fn new( user_id: impl Into, device_ids: Vec, - timestamp: Option>, + update_info: &UpdateOperationInfo, ) -> Self { Self { user_id: user_id.into(), device_ids, - timestamp: timestamp.unwrap_or_else(Utc::now), - current_primary_signature: None, - last_primary_signature: None, + timestamp: update_info.timestamp.unwrap_or_else(Utc::now), + current_primary_signature: update_info.current_signature.clone(), + last_primary_signature: update_info.last_signature.clone(), } } } @@ -812,7 +812,9 @@ let put_device_operation = TransactWriteItem::builder().put(put_device).build(); - Ok((Some(put_device_operation), None)) + let update_info = UpdateOperationInfo::identity_generated() + .with_ddb_operation(put_device_operation); + Ok(update_info) }) .await?; @@ -863,7 +865,9 @@ let operation = TransactWriteItem::builder().delete(delete_device).build(); - Ok((Some(operation), None)) + let update_info = UpdateOperationInfo::identity_generated() + .with_ddb_operation(operation); + Ok(update_info) }) .await?; @@ -879,11 +883,7 @@ // returns boolean determining if the new device list is valid. validator_fn: impl Fn(&[&str], &[&str]) -> bool, ) -> Result { - let DeviceListUpdate { - devices: new_list, - timestamp, - .. - } = update; + let new_list = update.devices.clone(); self .transact_update_devicelist(user_id, |current_list, _| { let previous_device_ids: Vec<&str> = @@ -900,7 +900,7 @@ debug!("Applying device list update"); *current_list = new_list; - Ok((None, Some(timestamp))) + Ok(UpdateOperationInfo::primary_device_issued(update)) }) .await } @@ -917,17 +917,11 @@ // It receives two arguments: // 1. A mutable reference to the current device list (ordered device IDs). // 2. Details (full data) of the current devices (unordered). - // The closure should return a two-element tuple: - // - (optional) transactional DDB operation to be performed - // when updating the device list. - // - (optional) new device list timestamp. Defaults to `Utc::now()`. + // The closure should return a [`UpdateOperationInfo`] object. action: impl FnOnce( &mut Vec, Vec, - ) -> Result< - (Option, Option>), - Error, - >, + ) -> Result, ) -> Result { let previous_timestamp = get_current_devicelist_timestamp(self, user_id).await?; @@ -939,13 +933,13 @@ .unwrap_or_default(); // Perform the update action, then generate new device list - let (operation, timestamp) = action(&mut device_ids, current_devices_data)?; + let update_info = action(&mut device_ids, current_devices_data)?; crate::device_list::verify_device_list_timestamp( previous_timestamp.as_ref(), - timestamp.as_ref(), + update_info.timestamp.as_ref(), )?; - let new_device_list = DeviceListRow::new(user_id, device_ids, timestamp); + let new_device_list = DeviceListRow::new(user_id, device_ids, &update_info); // Update timestamp in users table let timestamp_update_operation = device_list_timestamp_update_operation( @@ -967,7 +961,7 @@ let put_device_list_operation = TransactWriteItem::builder().put(put_device_list).build(); - let operations = if let Some(operation) = operation { + let operations = if let Some(operation) = update_info.ddb_operation { vec![ operation, put_device_list_operation, @@ -1158,6 +1152,43 @@ .consistent_read(true) } +/// [`transact_update_devicelist()`] closure result +struct UpdateOperationInfo { + /// (optional) transactional DDB operation to be performed + /// when updating the device list. + ddb_operation: Option, + /// new device list timestamp. Defaults to `Utc::now()` + /// for Identity-generated device lists. + timestamp: Option>, + current_signature: Option, + last_signature: Option, +} + +impl UpdateOperationInfo { + fn identity_generated() -> Self { + Self { + ddb_operation: None, + timestamp: None, + current_signature: None, + last_signature: None, + } + } + + fn primary_device_issued(source: DeviceListUpdate) -> Self { + Self { + ddb_operation: None, + timestamp: Some(source.timestamp), + current_signature: source.current_primary_signature, + last_signature: source.last_primary_signature, + } + } + + fn with_ddb_operation(mut self, operation: TransactWriteItem) -> Self { + self.ddb_operation = Some(operation); + self + } +} + // Helper module for "migration" code into new device list schema. // We can get rid of this when primary device takes over the responsibility // of managing the device list.