diff --git a/services/identity/src/constants.rs b/services/identity/src/constants.rs --- a/services/identity/src/constants.rs +++ b/services/identity/src/constants.rs @@ -56,6 +56,7 @@ pub const USERS_TABLE_FARCASTER_ID_ATTRIBUTE_NAME: &str = "farcasterID"; pub const USERS_TABLE_USERNAME_INDEX: &str = "username-index"; pub const USERS_TABLE_WALLET_ADDRESS_INDEX: &str = "walletAddress-index"; +pub const USERS_TABLE_FARCASTER_ID_INDEX: &str = "farcasterID-index"; pub const ACCESS_TOKEN_TABLE: &str = "identity-tokens"; pub const ACCESS_TOKEN_TABLE_PARTITION_KEY: &str = "userID"; diff --git a/services/identity/src/database.rs b/services/identity/src/database.rs --- a/services/identity/src/database.rs +++ b/services/identity/src/database.rs @@ -58,6 +58,7 @@ pub use grpc_clients::identity::DeviceType; mod device_list; +mod farcaster; mod workflows; pub use device_list::{DeviceListRow, DeviceListUpdate, DeviceRow}; diff --git a/services/identity/src/database/farcaster.rs b/services/identity/src/database/farcaster.rs new file mode 100644 --- /dev/null +++ b/services/identity/src/database/farcaster.rs @@ -0,0 +1,92 @@ +use comm_lib::aws::ddb::types::AttributeValue; +use comm_lib::database::AttributeExtractor; +use comm_lib::database::AttributeMap; +use comm_lib::database::DBItemAttributeError; +use comm_lib::database::DBItemError; +use comm_lib::database::TryFromAttribute; +use comm_lib::database::Value; +use grpc_clients::identity::protos::unauth::FarcasterUser; +use tracing::error; + +use crate::constants::USERS_TABLE; +use crate::constants::USERS_TABLE_FARCASTER_ID_ATTRIBUTE_NAME; +use crate::constants::USERS_TABLE_FARCASTER_ID_INDEX; +use crate::constants::USERS_TABLE_PARTITION_KEY; +use crate::constants::USERS_TABLE_USERNAME_ATTRIBUTE; +use crate::constants::USERS_TABLE_WALLET_ADDRESS_ATTRIBUTE; + +use super::DatabaseClient; +use super::Error; + +struct FarcasterUserData(FarcasterUser); + +impl DatabaseClient { + async fn get_farcaster_users( + &self, + farcaster_ids: Vec, + ) -> Result, Error> { + let mut users: Vec = Vec::new(); + + for id in farcaster_ids { + let query_response = self + .client + .query() + .table_name(USERS_TABLE) + .index_name(USERS_TABLE_FARCASTER_ID_INDEX) + .key_condition_expression(format!( + "{} = :val", + USERS_TABLE_FARCASTER_ID_ATTRIBUTE_NAME + )) + .expression_attribute_values(":val", AttributeValue::S(id)) + .send() + .await + .map_err(|e| { + error!("Failed to query users by farcasterID: {:?}", e); + Error::AwsSdk(e.into()) + })? + .items + .and_then(|mut items| items.pop()) + .map(FarcasterUserData::try_from) + .transpose() + .map_err(Error::from)?; + if let Some(data) = query_response { + users.push(data); + } + } + + Ok(users) + } +} + +impl TryFrom for FarcasterUserData { + type Error = DBItemError; + + fn try_from(mut attrs: AttributeMap) -> Result { + let user_id = attrs.take_attr(USERS_TABLE_PARTITION_KEY)?; + let mut username_opt = Option::try_from_attr( + USERS_TABLE_USERNAME_ATTRIBUTE, + attrs.remove(USERS_TABLE_USERNAME_ATTRIBUTE), + )?; + if username_opt.is_none() { + username_opt = + Some(attrs.take_attr(USERS_TABLE_WALLET_ADDRESS_ATTRIBUTE)?); + } + let username = if let Some(u) = username_opt { + u + } else { + return Err(DBItemError { + attribute_name: USERS_TABLE_WALLET_ADDRESS_ATTRIBUTE.to_string(), + attribute_value: Value::AttributeValue(None), + attribute_error: DBItemAttributeError::Missing, + }); + }; + let farcaster_id = + attrs.take_attr(USERS_TABLE_FARCASTER_ID_ATTRIBUTE_NAME)?; + + Ok(Self(FarcasterUser { + user_id, + username, + farcaster_id, + })) + } +}