Page MenuHomePhorge

D8939.1768193041.diff
No OneTemporary

Size
6 KB
Referenced Files
None
Subscribers
None

D8939.1768193041.diff

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
@@ -115,6 +115,38 @@
}
}
+/// Helper trait for extracting attributes from a collection
+pub trait AttributeExtractor {
+ /// Gets an attribute from the map and tries to convert it to the given type
+ /// This method does not consume the raw attribute - it gets cloned
+ /// See [`AttributeExtractor::take_attr`] for a non-cloning method
+ fn get_attr<T: TryFromAttribute>(
+ &self,
+ attribute_name: &str,
+ ) -> Result<T, DBItemError>;
+ /// Takes an attribute from the map and tries to convert it to the given type
+ /// This method consumes the raw attribute - it gets removed from the map
+ /// See [`AttributeExtractor::get_attr`] for a non-mutating method
+ fn take_attr<T: TryFromAttribute>(
+ &mut self,
+ attribute_name: &str,
+ ) -> Result<T, DBItemError>;
+}
+impl AttributeExtractor for AttributeMap {
+ fn get_attr<T: TryFromAttribute>(
+ &self,
+ attribute_name: &str,
+ ) -> Result<T, DBItemError> {
+ T::try_from_attr(attribute_name, self.get(attribute_name).cloned())
+ }
+ fn take_attr<T: TryFromAttribute>(
+ &mut self,
+ attribute_name: &str,
+ ) -> Result<T, DBItemError> {
+ T::try_from_attr(attribute_name, self.remove(attribute_name))
+ }
+}
+
impl TryFromAttribute for String {
fn try_from_attr(
attribute_name: impl Into<String>,
diff --git a/services/reports/src/database/item.rs b/services/reports/src/database/item.rs
--- a/services/reports/src/database/item.rs
+++ b/services/reports/src/database/item.rs
@@ -1,5 +1,11 @@
-use aws_sdk_dynamodb::types::AttributeValue;
-use comm_services_lib::database::{self, DBItemError, TryFromAttribute};
+use aws_sdk_dynamodb::{primitives::Blob, types::AttributeValue};
+use chrono::{DateTime, Utc};
+use comm_services_lib::{
+ blob::types::BlobInfo,
+ database::{
+ self, AttributeExtractor, AttributeMap, DBItemError, TryFromAttribute,
+ },
+};
use num_traits::FromPrimitive;
use tracing::debug;
@@ -7,6 +13,142 @@
use crate::report_types::*;
+/// Represents a report item row in DynamoDB
+/// This is serializable to display a list of reports
+#[derive(Clone, Debug, serde::Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ReportItem {
+ pub id: ReportID,
+ #[serde(rename = "userID")]
+ pub user_id: String,
+ pub report_type: ReportType,
+ pub platform: ReportPlatform,
+ pub creation_time: DateTime<Utc>,
+ #[serde(skip_serializing)]
+ pub content: ReportContent,
+ #[serde(skip_serializing)]
+ pub encryption_key: Option<String>,
+}
+
+impl ReportItem {
+ pub fn into_attrs(self) -> AttributeMap {
+ let creation_time = self
+ .creation_time
+ .to_rfc3339_opts(chrono::SecondsFormat::Millis, true);
+
+ let mut attrs = AttributeMap::from([
+ (ATTR_REPORT_ID.to_string(), self.id.into()),
+ (ATTR_USER_ID.to_string(), AttributeValue::S(self.user_id)),
+ (ATTR_REPORT_TYPE.to_string(), self.report_type.into()),
+ (ATTR_PLATFORM.to_string(), self.platform.into()),
+ (
+ ATTR_CREATION_TIME.to_string(),
+ AttributeValue::S(creation_time),
+ ),
+ ]);
+
+ let (content_attr_name, content_attr) = self.content.into_attr_pair();
+ attrs.insert(content_attr_name, content_attr);
+
+ if let Some(key) = self.encryption_key {
+ attrs.insert(ATTR_ENCRYPTION_KEY.to_string(), AttributeValue::S(key));
+ }
+ attrs
+ }
+
+ /// Creates a report item from a report input payload
+ pub fn from_input(
+ payload: ReportInput,
+ user_id: Option<String>,
+ ) -> Result<Self, serde_json::Error> {
+ let ReportInput {
+ platform_details,
+ report_type,
+ time,
+ mut report_content,
+ } = payload;
+
+ let platform = platform_details.platform.clone();
+
+ // Add "platformDetails" back to report content
+ let platform_details_value = serde_json::to_value(platform_details)?;
+ report_content
+ .insert("platformDetails".to_string(), platform_details_value);
+
+ // serialize report JSON to bytes
+ let content_bytes = serde_json::to_vec(&report_content)?;
+ let content = ReportContent::Database(content_bytes);
+
+ Ok(ReportItem {
+ id: ReportID::default(),
+ user_id: user_id.unwrap_or("[null]".to_string()),
+ platform,
+ report_type,
+ creation_time: time.unwrap_or_else(Utc::now),
+ encryption_key: None,
+ content,
+ })
+ }
+}
+
+impl TryFrom<AttributeMap> for ReportItem {
+ type Error = DBItemError;
+
+ fn try_from(mut row: AttributeMap) -> Result<Self, Self::Error> {
+ let id = row.remove(ATTR_REPORT_ID).try_into()?;
+ let user_id = row.take_attr(ATTR_USER_ID)?;
+ let report_type = row.take_attr(ATTR_REPORT_TYPE)?;
+ let platform = row.take_attr(ATTR_PLATFORM)?;
+ let creation_time = row.take_attr(ATTR_CREATION_TIME)?;
+
+ let content = ReportContent::parse_from_attrs(&mut row)?;
+ let encryption_key = row
+ .remove(ATTR_ENCRYPTION_KEY)
+ .map(|attr| String::try_from_attr(ATTR_ENCRYPTION_KEY, Some(attr)))
+ .transpose()?;
+
+ Ok(ReportItem {
+ id,
+ user_id,
+ report_type,
+ platform,
+ content,
+ encryption_key,
+ creation_time,
+ })
+ }
+}
+
+/// Represents the content of a report item stored in DynamoDB
+#[derive(Clone, Debug)]
+pub enum ReportContent {
+ Blob(BlobInfo),
+ Database(Vec<u8>),
+}
+
+impl ReportContent {
+ /// Returns a tuple of attribute name and value for this content
+ fn into_attr_pair(self) -> (String, AttributeValue) {
+ match self {
+ Self::Blob(blob_info) => (ATTR_BLOB_INFO.to_string(), blob_info.into()),
+ Self::Database(data) => (
+ ATTR_REPORT_CONTENT.to_string(),
+ AttributeValue::B(Blob::new(data)),
+ ),
+ }
+ }
+ fn parse_from_attrs(attrs: &mut AttributeMap) -> Result<Self, DBItemError> {
+ if let Some(blob_info_attr) = attrs.remove(ATTR_BLOB_INFO) {
+ let blob_info =
+ BlobInfo::try_from_attr(ATTR_BLOB_INFO, Some(blob_info_attr))?;
+ return Ok(ReportContent::Blob(blob_info));
+ }
+
+ let content_data = attrs.take_attr(ATTR_REPORT_CONTENT)?;
+ Ok(ReportContent::Database(content_data))
+ }
+}
+
// DB conversions for report types
// ReportID

File Metadata

Mime Type
text/plain
Expires
Mon, Jan 12, 4:44 AM (3 h, 10 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5921712
Default Alt Text
D8939.1768193041.diff (6 KB)

Event Timeline