diff --git a/services/backup/src/constants.rs b/services/backup/src/constants.rs --- a/services/backup/src/constants.rs +++ b/services/backup/src/constants.rs @@ -18,8 +18,8 @@ pub const BACKUP_TABLE_FIELD_USER_ID: &str = "userID"; pub const BACKUP_TABLE_FIELD_BACKUP_ID: &str = "backupID"; pub const BACKUP_TABLE_FIELD_CREATED: &str = "created"; -pub const BACKUP_TABLE_FIELD_RECOVERY_DATA: &str = "recoveryData"; -pub const BACKUP_TABLE_FIELD_COMPACTION_HOLDER: &str = "compactionHolder"; +pub const BACKUP_TABLE_FIELD_USER_DATA: &str = "userData"; +pub const BACKUP_TABLE_FIELD_USER_KEYS: &str = "userKeys"; pub const BACKUP_TABLE_FIELD_ATTACHMENT_HOLDERS: &str = "attachmentHolders"; pub const BACKUP_TABLE_INDEX_USERID_CREATED: &str = "userID-created-index"; diff --git a/services/backup/src/database/backup_item.rs b/services/backup/src/database/backup_item.rs --- a/services/backup/src/database/backup_item.rs +++ b/services/backup/src/database/backup_item.rs @@ -1,12 +1,15 @@ use aws_sdk_dynamodb::types::AttributeValue; use chrono::{DateTime, Utc}; -use comm_services_lib::database::{DBItemError, TryFromAttribute}; -use std::collections::HashMap; +use comm_services_lib::{ + blob::types::BlobInfo, + database::{DBItemError, TryFromAttribute}, +}; +use std::collections::{HashMap, HashSet}; use crate::constants::{ BACKUP_TABLE_FIELD_ATTACHMENT_HOLDERS, BACKUP_TABLE_FIELD_BACKUP_ID, - BACKUP_TABLE_FIELD_COMPACTION_HOLDER, BACKUP_TABLE_FIELD_CREATED, - BACKUP_TABLE_FIELD_RECOVERY_DATA, BACKUP_TABLE_FIELD_USER_ID, + BACKUP_TABLE_FIELD_CREATED, BACKUP_TABLE_FIELD_USER_DATA, + BACKUP_TABLE_FIELD_USER_ID, BACKUP_TABLE_FIELD_USER_KEYS, }; #[derive(Clone, Debug)] @@ -14,65 +17,111 @@ pub user_id: String, pub backup_id: String, pub created: DateTime, - pub recovery_data: String, - pub compaction_holder: String, - pub attachment_holders: String, + pub user_keys: BlobInfo, + pub user_data: BlobInfo, + pub attachment_holders: HashSet, } impl BackupItem { pub fn new( user_id: String, backup_id: String, - compaction_holder: String, + user_keys: BlobInfo, + user_data: BlobInfo, + attachment_holders: HashSet, ) -> Self { BackupItem { user_id, backup_id, - compaction_holder, created: chrono::Utc::now(), - // TODO: Recovery data is mocked with random string - recovery_data: crate::utils::generate_random_string( - 20, - &mut rand::thread_rng(), + user_keys, + user_data, + attachment_holders, + } + } +} + +impl From for HashMap { + fn from(value: BackupItem) -> Self { + let mut attrs = HashMap::from([ + ( + BACKUP_TABLE_FIELD_USER_ID.to_string(), + AttributeValue::S(value.user_id), + ), + ( + BACKUP_TABLE_FIELD_BACKUP_ID.to_string(), + AttributeValue::S(value.backup_id), + ), + ( + BACKUP_TABLE_FIELD_CREATED.to_string(), + AttributeValue::S(value.created.to_rfc3339()), + ), + ( + BACKUP_TABLE_FIELD_USER_KEYS.to_string(), + value.user_keys.into(), + ), + ( + BACKUP_TABLE_FIELD_USER_DATA.to_string(), + value.user_data.into(), ), - attachment_holders: String::new(), + ]); + + if !value.attachment_holders.is_empty() { + attrs.insert( + BACKUP_TABLE_FIELD_ATTACHMENT_HOLDERS.to_string(), + AttributeValue::Ss(value.attachment_holders.into_iter().collect()), + ); } + + attrs } } -pub fn parse_backup_item( - mut item: HashMap, -) -> Result { - let user_id = String::try_from_attr( - BACKUP_TABLE_FIELD_USER_ID, - item.remove(BACKUP_TABLE_FIELD_USER_ID), - )?; - let backup_id = String::try_from_attr( - BACKUP_TABLE_FIELD_BACKUP_ID, - item.remove(BACKUP_TABLE_FIELD_BACKUP_ID), - )?; - let created = DateTime::::try_from_attr( - BACKUP_TABLE_FIELD_CREATED, - item.remove(BACKUP_TABLE_FIELD_CREATED), - )?; - let recovery_data = String::try_from_attr( - BACKUP_TABLE_FIELD_RECOVERY_DATA, - item.remove(BACKUP_TABLE_FIELD_RECOVERY_DATA), - )?; - let compaction_holder = String::try_from_attr( - BACKUP_TABLE_FIELD_COMPACTION_HOLDER, - item.remove(BACKUP_TABLE_FIELD_COMPACTION_HOLDER), - )?; - let attachment_holders = String::try_from_attr( - BACKUP_TABLE_FIELD_ATTACHMENT_HOLDERS, - item.remove(BACKUP_TABLE_FIELD_ATTACHMENT_HOLDERS), - )?; - Ok(BackupItem { - user_id, - backup_id, - created, - recovery_data, - compaction_holder, - attachment_holders, - }) +impl TryFrom> for BackupItem { + type Error = DBItemError; + + fn try_from( + mut value: HashMap, + ) -> Result { + let user_id = String::try_from_attr( + BACKUP_TABLE_FIELD_USER_ID, + value.remove(BACKUP_TABLE_FIELD_USER_ID), + )?; + let backup_id = String::try_from_attr( + BACKUP_TABLE_FIELD_BACKUP_ID, + value.remove(BACKUP_TABLE_FIELD_BACKUP_ID), + )?; + let created = DateTime::::try_from_attr( + BACKUP_TABLE_FIELD_CREATED, + value.remove(BACKUP_TABLE_FIELD_CREATED), + )?; + + let user_keys = BlobInfo::try_from_attr( + BACKUP_TABLE_FIELD_USER_KEYS, + value.remove(BACKUP_TABLE_FIELD_USER_KEYS), + )?; + let user_data = BlobInfo::try_from_attr( + BACKUP_TABLE_FIELD_USER_DATA, + value.remove(BACKUP_TABLE_FIELD_USER_DATA), + )?; + + let attachments = value.remove(BACKUP_TABLE_FIELD_ATTACHMENT_HOLDERS); + let attachment_holders = if attachments.is_some() { + HashSet::::try_from_attr( + BACKUP_TABLE_FIELD_ATTACHMENT_HOLDERS, + attachments, + )? + } else { + HashSet::new() + }; + + Ok(BackupItem { + user_id, + backup_id, + created, + user_keys, + user_data, + attachment_holders, + }) + } } diff --git a/services/backup/src/database/mod.rs b/services/backup/src/database/mod.rs --- a/services/backup/src/database/mod.rs +++ b/services/backup/src/database/mod.rs @@ -10,9 +10,7 @@ use tracing::error; use crate::constants::{ - BACKUP_TABLE_FIELD_ATTACHMENT_HOLDERS, BACKUP_TABLE_FIELD_BACKUP_ID, - BACKUP_TABLE_FIELD_COMPACTION_HOLDER, BACKUP_TABLE_FIELD_CREATED, - BACKUP_TABLE_FIELD_RECOVERY_DATA, BACKUP_TABLE_FIELD_USER_ID, + BACKUP_TABLE_FIELD_BACKUP_ID, BACKUP_TABLE_FIELD_USER_ID, BACKUP_TABLE_INDEX_USERID_CREATED, BACKUP_TABLE_NAME, LOG_TABLE_FIELD_ATTACHMENT_HOLDERS, LOG_TABLE_FIELD_BACKUP_ID, LOG_TABLE_FIELD_DATA_HASH, LOG_TABLE_FIELD_LOG_ID, @@ -20,7 +18,7 @@ }; use self::{ - backup_item::{parse_backup_item, BackupItem}, + backup_item::BackupItem, log_item::{parse_log_item, LogItem}, }; @@ -41,32 +39,7 @@ &self, backup_item: BackupItem, ) -> Result<(), Error> { - let item = HashMap::from([ - ( - BACKUP_TABLE_FIELD_USER_ID.to_string(), - AttributeValue::S(backup_item.user_id), - ), - ( - BACKUP_TABLE_FIELD_CREATED.to_string(), - AttributeValue::S(backup_item.created.to_rfc3339()), - ), - ( - BACKUP_TABLE_FIELD_BACKUP_ID.to_string(), - AttributeValue::S(backup_item.backup_id), - ), - ( - BACKUP_TABLE_FIELD_RECOVERY_DATA.to_string(), - AttributeValue::S(backup_item.recovery_data), - ), - ( - BACKUP_TABLE_FIELD_COMPACTION_HOLDER.to_string(), - AttributeValue::S(backup_item.compaction_holder), - ), - ( - BACKUP_TABLE_FIELD_ATTACHMENT_HOLDERS.to_string(), - AttributeValue::S(backup_item.attachment_holders), - ), - ]); + let item = backup_item.into(); self .client @@ -113,7 +86,7 @@ GetItemOutput { item: Some(item), .. } => { - let backup_item = parse_backup_item(item)?; + let backup_item = item.try_into()?; Ok(Some(backup_item)) } _ => Ok(None), @@ -146,7 +119,7 @@ match response.items.unwrap_or_default().pop() { Some(item) => { - let backup_item = parse_backup_item(item)?; + let backup_item = item.try_into()?; Ok(Some(backup_item)) } None => Ok(None), diff --git a/services/comm-services-lib/src/database.rs b/services/comm-services-lib/src/database.rs --- a/services/comm-services-lib/src/database.rs +++ b/services/comm-services-lib/src/database.rs @@ -1,6 +1,7 @@ use aws_sdk_dynamodb::types::AttributeValue; use aws_sdk_dynamodb::Error as DynamoDBError; use chrono::{DateTime, Utc}; +use std::collections::HashSet; use std::fmt::{Display, Formatter}; use std::num::ParseIntError; use std::str::FromStr; @@ -226,6 +227,27 @@ } } +impl TryFromAttribute for HashSet { + fn try_from_attr( + attribute_name: impl Into, + attribute_value: Option, + ) -> Result { + match attribute_value { + Some(AttributeValue::Ss(set)) => Ok(set.into_iter().collect()), + Some(_) => Err(DBItemError::new( + attribute_name.into(), + Value::AttributeValue(attribute_value), + DBItemAttributeError::IncorrectType, + )), + None => Err(DBItemError::new( + attribute_name.into(), + Value::AttributeValue(attribute_value), + DBItemAttributeError::Missing, + )), + } + } +} + #[deprecated = "Use `String::try_from_attr()` instead"] pub fn parse_string_attribute( attribute_name: impl Into,