diff --git a/services/blob/src/constants.rs b/services/blob/src/constants.rs --- a/services/blob/src/constants.rs +++ b/services/blob/src/constants.rs @@ -23,6 +23,7 @@ /// attribute names pub const ATTR_BLOB_HASH: &str = "blob_hash"; pub const ATTR_HOLDER: &str = "holder"; + pub const ATTR_INDEXED_TAG: &str = "indexed_tag"; pub const ATTR_CREATED_AT: &str = "created_at"; pub const ATTR_LAST_MODIFIED: &str = "last_modified"; pub const ATTR_S3_PATH: &str = "s3_path"; diff --git a/services/blob/src/database/client.rs b/services/blob/src/database/client.rs --- a/services/blob/src/database/client.rs +++ b/services/blob/src/database/client.rs @@ -99,13 +99,19 @@ let blob_hash: String = blob_hash.into(); let holder: String = holder.into(); + let indexed_tag = get_indexable_tag(&holder, &[]); + validate_holder(&holder)?; - let item = HashMap::from([ + let mut item = HashMap::from([ (ATTR_BLOB_HASH.to_string(), AttributeValue::S(blob_hash)), (ATTR_HOLDER.to_string(), AttributeValue::S(holder)), (ATTR_UNCHECKED.to_string(), UncheckedKind::Holder.into()), ]); + if let Some(tag) = indexed_tag { + item.insert(ATTR_INDEXED_TAG.to_string(), AttributeValue::S(tag)); + } + self.insert_item(item).await?; Ok(()) } @@ -434,3 +440,59 @@ } Ok(()) } + +/// In the future we'll want to add a `tags` meta attribute for internal +/// use, e.g. in case of data loss on other services. The attribute +/// is going to be a list of string values - _tags_. +/// First tag from the list is going to be indexed in DDB. +/// +/// While tags are not implemented, we accept _holder prefixes_ +/// as an intermediate solution - holder prefix (separated by the `:` character) +/// is treated as the first holder _tag_, unless [`tags`] is not empty. +fn get_indexable_tag(holder: &str, tags: &[String]) -> Option { + const HOLDER_PREFIX_SEPARATOR: char = ':'; + + if let Some(first_tag) = tags.first() { + return Some(first_tag.to_string()); + } + + if !holder.contains(HOLDER_PREFIX_SEPARATOR) { + return None; + } + + holder + .split(HOLDER_PREFIX_SEPARATOR) + .next() + .map(str::to_string) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn get_indexable_tag_no_tags_no_prefix() { + let tag = get_indexable_tag("foo", &[]); + assert!(tag.is_none()); + } + + #[test] + fn get_indexable_tag_tags_no_prefix() { + let tags = vec!["tag1".to_string(), "tag2".to_string()]; + let tag = get_indexable_tag("foo", &tags); + assert_eq!(tag, Some("tag1".into())); + } + + #[test] + fn get_indexable_tag_tags_and_prefix() { + let tags = vec!["tag1".to_string(), "tag2".to_string()]; + let tag = get_indexable_tag("device1:foo", &tags); + assert_eq!(tag, Some("tag1".into())); + } + + #[test] + fn get_indexable_tag_prefix_no_tags() { + let tag = get_indexable_tag("device1:foo", &[]); + assert_eq!(tag, Some("device1".into())); + } +}