Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F32903517
D8939.1768193041.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
D8939.1768193041.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D8939: [report-service] Implement DB row item type
Attached
Detach File
Event Timeline
Log In to Comment