diff --git a/src/mastodon_api/accounts/helpers.rs b/src/mastodon_api/accounts/helpers.rs new file mode 100644 index 0000000..bba79fb --- /dev/null +++ b/src/mastodon_api/accounts/helpers.rs @@ -0,0 +1,125 @@ +use tokio_postgres::GenericClient; +use uuid::Uuid; + +use crate::errors::DatabaseError; +use crate::models::relationships::queries::get_relationships; +use crate::models::relationships::types::RelationshipType; +use super::types::RelationshipMap; + +pub async fn get_relationship( + db_client: &impl GenericClient, + source_id: &Uuid, + target_id: &Uuid, +) -> Result { + // NOTE: this method returns relationship map even if target does not exist + let relationships = get_relationships(db_client, source_id, target_id).await?; + let mut relationship_map = RelationshipMap { id: *target_id, ..Default::default() }; + for relationship in relationships { + match relationship.relationship_type { + RelationshipType::Follow => { + if relationship.is_direct(source_id, target_id)? { + relationship_map.following = true; + } else { + relationship_map.followed_by = true; + }; + }, + RelationshipType::FollowRequest => { + if relationship.is_direct(source_id, target_id)? { + relationship_map.requested = true; + }; + }, + RelationshipType::Subscription => { + if relationship.is_direct(source_id, target_id)? { + relationship_map.subscription_to = true; + } else { + relationship_map.subscription_from = true; + }; + }, + }; + }; + Ok(relationship_map) +} + +#[cfg(test)] +mod tests { + use serial_test::serial; + use crate::database::test_utils::create_test_database; + use crate::models::relationships::queries::{ + create_follow_request, + follow, + follow_request_accepted, + subscribe, + unfollow, + unsubscribe, + }; + use crate::models::users::queries::create_user; + use crate::models::users::types::{User, UserCreateData}; + use super::*; + + async fn create_users(db_client: &mut impl GenericClient) + -> Result<(User, User), DatabaseError> + { + let user_data_1 = UserCreateData { + username: "user".to_string(), + ..Default::default() + }; + let user_1 = create_user(db_client, user_data_1).await.unwrap(); + let user_data_2 = UserCreateData { + username: "another-user".to_string(), + ..Default::default() + }; + let user_2 = create_user(db_client, user_data_2).await.unwrap(); + Ok((user_1, user_2)) + } + + #[tokio::test] + #[serial] + async fn test_follow_unfollow() { + let db_client = &mut create_test_database().await; + let (user_1, user_2) = create_users(db_client).await.unwrap(); + // Initial state + let relationship = get_relationship(db_client, &user_1.id, &user_2.id).await.unwrap(); + assert_eq!(relationship.id, user_2.id); + assert_eq!(relationship.following, false); + assert_eq!(relationship.followed_by, false); + assert_eq!(relationship.requested, false); + assert_eq!(relationship.subscription_to, false); + assert_eq!(relationship.subscription_from, false); + // Follow request + let follow_request = create_follow_request(db_client, &user_1.id, &user_2.id).await.unwrap(); + let relationship = get_relationship(db_client, &user_1.id, &user_2.id).await.unwrap(); + assert_eq!(relationship.following, false); + assert_eq!(relationship.followed_by, false); + assert_eq!(relationship.requested, true); + // Mutual follow + follow_request_accepted(db_client, &follow_request.id).await.unwrap(); + follow(db_client, &user_2.id, &user_1.id).await.unwrap(); + let relationship = get_relationship(db_client, &user_1.id, &user_2.id).await.unwrap(); + assert_eq!(relationship.following, true); + assert_eq!(relationship.followed_by, true); + assert_eq!(relationship.requested, false); + // Unfollow + unfollow(db_client, &user_1.id, &user_2.id).await.unwrap(); + let relationship = get_relationship(db_client, &user_1.id, &user_2.id).await.unwrap(); + assert_eq!(relationship.following, false); + assert_eq!(relationship.followed_by, true); + assert_eq!(relationship.requested, false); + } + + #[tokio::test] + #[serial] + async fn test_subscribe_unsubscribe() { + let db_client = &mut create_test_database().await; + let (user_1, user_2) = create_users(db_client).await.unwrap(); + + subscribe(db_client, &user_1.id, &user_2.id).await.unwrap(); + let relationship = get_relationship(db_client, &user_1.id, &user_2.id).await.unwrap(); + assert_eq!(relationship.subscription_to, true); + assert_eq!(relationship.subscription_from, false); + + unsubscribe(db_client, &user_1.id, &user_2.id).await.unwrap(); + let relationship = get_relationship(db_client, &user_1.id, &user_2.id).await.unwrap(); + assert_eq!(relationship.subscription_to, false); + assert_eq!(relationship.subscription_from, false); + } +} diff --git a/src/mastodon_api/accounts/mod.rs b/src/mastodon_api/accounts/mod.rs index a964b01..328cdc2 100644 --- a/src/mastodon_api/accounts/mod.rs +++ b/src/mastodon_api/accounts/mod.rs @@ -1,2 +1,3 @@ +mod helpers; pub mod types; pub mod views; diff --git a/src/mastodon_api/accounts/types.rs b/src/mastodon_api/accounts/types.rs index c0a7664..f33133f 100644 --- a/src/mastodon_api/accounts/types.rs +++ b/src/mastodon_api/accounts/types.rs @@ -193,6 +193,16 @@ pub struct RelationshipQueryParams { pub id: Uuid, } +#[derive(Default, Serialize)] +pub struct RelationshipMap { + pub id: Uuid, // target ID + pub following: bool, + pub followed_by: bool, + pub requested: bool, + pub subscription_to: bool, + pub subscription_from: bool, +} + fn default_page_size() -> i64 { 20 } #[derive(Deserialize)] diff --git a/src/mastodon_api/accounts/views.rs b/src/mastodon_api/accounts/views.rs index 9771696..4f5ae51 100644 --- a/src/mastodon_api/accounts/views.rs +++ b/src/mastodon_api/accounts/views.rs @@ -33,7 +33,6 @@ use crate::models::relationships::queries::{ get_follow_request_by_path, get_followers, get_following, - get_relationship, unfollow, }; use crate::models::users::queries::{ @@ -47,6 +46,7 @@ use crate::utils::crypto::{ serialize_private_key, }; use crate::utils::files::FileError; +use super::helpers::get_relationship; use super::types::{ Account, AccountCreateData, diff --git a/src/models/relationships/queries.rs b/src/models/relationships/queries.rs index 64b17bf..d6e2b89 100644 --- a/src/models/relationships/queries.rs +++ b/src/models/relationships/queries.rs @@ -16,11 +16,10 @@ use super::types::{ DbFollowRequest, DbRelationship, FollowRequestStatus, - RelationshipMap, RelationshipType, }; -async fn get_relationships( +pub async fn get_relationships( db_client: &impl GenericClient, source_id: &Uuid, target_id: &Uuid, @@ -53,40 +52,6 @@ async fn get_relationships( Ok(relationships) } -pub async fn get_relationship( - db_client: &impl GenericClient, - source_id: &Uuid, - target_id: &Uuid, -) -> Result { - // NOTE: this method returns relationship map even if target does not exist - let relationships = get_relationships(db_client, source_id, target_id).await?; - let mut relationship_map = RelationshipMap { id: *target_id, ..Default::default() }; - for relationship in relationships { - match relationship.relationship_type { - RelationshipType::Follow => { - if relationship.is_direct(source_id, target_id)? { - relationship_map.following = true; - } else { - relationship_map.followed_by = true; - }; - }, - RelationshipType::FollowRequest => { - if relationship.is_direct(source_id, target_id)? { - relationship_map.requested = true; - }; - }, - RelationshipType::Subscription => { - if relationship.is_direct(source_id, target_id)? { - relationship_map.subscription_to = true; - } else { - relationship_map.subscription_from = true; - }; - }, - }; - }; - Ok(relationship_map) -} - pub async fn has_relationship( db_client: &impl GenericClient, source_id: &Uuid, @@ -412,80 +377,3 @@ pub async fn get_subscribers( .collect::>()?; Ok(profiles) } - -#[cfg(test)] -mod tests { - use serial_test::serial; - use crate::database::test_utils::create_test_database; - use crate::models::relationships::queries::follow; - use crate::models::users::queries::create_user; - use crate::models::users::types::{User, UserCreateData}; - use super::*; - - async fn create_users(db_client: &mut impl GenericClient) - -> Result<(User, User), DatabaseError> - { - let user_data_1 = UserCreateData { - username: "user".to_string(), - ..Default::default() - }; - let user_1 = create_user(db_client, user_data_1).await.unwrap(); - let user_data_2 = UserCreateData { - username: "another-user".to_string(), - ..Default::default() - }; - let user_2 = create_user(db_client, user_data_2).await.unwrap(); - Ok((user_1, user_2)) - } - - #[tokio::test] - #[serial] - async fn test_follow_unfollow() { - let db_client = &mut create_test_database().await; - let (user_1, user_2) = create_users(db_client).await.unwrap(); - // Initial state - let relationship = get_relationship(db_client, &user_1.id, &user_2.id).await.unwrap(); - assert_eq!(relationship.id, user_2.id); - assert_eq!(relationship.following, false); - assert_eq!(relationship.followed_by, false); - assert_eq!(relationship.requested, false); - assert_eq!(relationship.subscription_to, false); - assert_eq!(relationship.subscription_from, false); - // Follow request - let follow_request = create_follow_request(db_client, &user_1.id, &user_2.id).await.unwrap(); - let relationship = get_relationship(db_client, &user_1.id, &user_2.id).await.unwrap(); - assert_eq!(relationship.following, false); - assert_eq!(relationship.followed_by, false); - assert_eq!(relationship.requested, true); - // Mutual follow - follow_request_accepted(db_client, &follow_request.id).await.unwrap(); - follow(db_client, &user_2.id, &user_1.id).await.unwrap(); - let relationship = get_relationship(db_client, &user_1.id, &user_2.id).await.unwrap(); - assert_eq!(relationship.following, true); - assert_eq!(relationship.followed_by, true); - assert_eq!(relationship.requested, false); - // Unfollow - unfollow(db_client, &user_1.id, &user_2.id).await.unwrap(); - let relationship = get_relationship(db_client, &user_1.id, &user_2.id).await.unwrap(); - assert_eq!(relationship.following, false); - assert_eq!(relationship.followed_by, true); - assert_eq!(relationship.requested, false); - } - - #[tokio::test] - #[serial] - async fn test_subscribe_unsubscribe() { - let db_client = &mut create_test_database().await; - let (user_1, user_2) = create_users(db_client).await.unwrap(); - - subscribe(db_client, &user_1.id, &user_2.id).await.unwrap(); - let relationship = get_relationship(db_client, &user_1.id, &user_2.id).await.unwrap(); - assert_eq!(relationship.subscription_to, true); - assert_eq!(relationship.subscription_from, false); - - unsubscribe(db_client, &user_1.id, &user_2.id).await.unwrap(); - let relationship = get_relationship(db_client, &user_1.id, &user_2.id).await.unwrap(); - assert_eq!(relationship.subscription_to, false); - assert_eq!(relationship.subscription_from, false); - } -} diff --git a/src/models/relationships/types.rs b/src/models/relationships/types.rs index bf1bc2d..7caeb67 100644 --- a/src/models/relationships/types.rs +++ b/src/models/relationships/types.rs @@ -1,7 +1,6 @@ use std::convert::TryFrom; use postgres_types::FromSql; -use serde::Serialize; use tokio_postgres::Row; use uuid::Uuid; @@ -78,16 +77,6 @@ impl TryFrom<&Row> for DbRelationship { } } -#[derive(Default, Serialize)] -pub struct RelationshipMap { - pub id: Uuid, // target ID - pub following: bool, - pub followed_by: bool, - pub requested: bool, - pub subscription_to: bool, - pub subscription_from: bool, -} - #[derive(Debug)] pub enum FollowRequestStatus { Pending,