diff --git a/src/api/users/relationships.rs b/src/api/users/relationships.rs index ede39f0..65c044e 100644 --- a/src/api/users/relationships.rs +++ b/src/api/users/relationships.rs @@ -5,7 +5,7 @@ use crate::{ api::{deserialize_response, handle_request_as_option}, errors::ChorusLibError, instance::UserMeta, - types, + types::{self, CreateUserRelationshipSchema, RelationshipType}, }; impl UserMeta { @@ -16,11 +16,11 @@ impl UserMeta { /// * `user_id` - A string slice that holds the ID of the user to retrieve the mutual relationships with. /// /// # Returns - /// This function returns a [`Option>>`]. + /// This function returns a [`Result, ChorusLibError>`]. pub async fn get_mutual_relationships( &mut self, user_id: &str, - ) -> Result>, ChorusLibError> { + ) -> Result, ChorusLibError> { let belongs_to = self.belongs_to.borrow(); let url = format!( "{}/users/{}/relationships/", @@ -29,7 +29,24 @@ impl UserMeta { ); drop(belongs_to); let request = Client::new().get(url).bearer_auth(self.token()); - deserialize_response::>>( + deserialize_response::>( + request, + self, + crate::api::limits::LimitType::Global, + ) + .await + } + + /// Retrieves the authenticated user's relationships. + /// + /// # Returns + /// This function returns a [`Result, ChorusLibError>`]. + pub async fn get_relationships(&mut self) -> Result, ChorusLibError> { + let belongs_to = self.belongs_to.borrow(); + let url = format!("{}/users/@me/relationships/", belongs_to.urls.get_api(),); + drop(belongs_to); + let request = Client::new().get(url).bearer_auth(self.token()); + deserialize_response::>( request, self, crate::api::limits::LimitType::Global, @@ -56,4 +73,81 @@ impl UserMeta { let request = Client::new().post(url).bearer_auth(self.token()).body(body); handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await } + + /// Modifies the relationship between the authenticated user and the specified user. + /// + /// # Arguments + /// + /// * `user_id` - A string slice that holds the ID of the user to modify the relationship with. + /// * `relationship_type` - A [`RelationshipType`] enum that specifies the type of relationship to modify. + /// * [`RelationshipType::None`]: Removes the relationship between the two users. + /// * [`RelationshipType::Friends`] | [`RelationshipType::Incoming`] | [`RelationshipType::Outgoing`]: + /// Either accepts an incoming friend request, or sends a new friend request, if there is no + /// incoming friend request from the specified `user_id`. + /// * [`RelationshipType::Blocked`]: Blocks the specified user_id. + /// + /// # Returns + /// This function returns an [`Option`] that holds a [`ChorusLibError`] if the request fails. + pub async fn modify_user_relationship( + &mut self, + user_id: &str, + relationship_type: RelationshipType, + ) -> Option { + let belongs_to = self.belongs_to.borrow(); + let api_url = belongs_to.urls.api.clone(); + drop(belongs_to); + match relationship_type { + RelationshipType::None => { + let request = Client::new() + .delete(format!("{}/users/@me/relationships/{}/", api_url, user_id)) + .bearer_auth(self.token()); + handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await + } + RelationshipType::Friends | RelationshipType::Incoming | RelationshipType::Outgoing => { + let body = CreateUserRelationshipSchema { + relationship_type: None, // Selecting 'None' here will accept an incoming FR or send a new FR. + from_friend_suggestion: None, + friend_token: None, + }; + let request = Client::new() + .put(format!("{}/users/@me/relationships/{}/", api_url, user_id)) + .bearer_auth(self.token()) + .body(to_string(&body).unwrap()); + handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await + } + RelationshipType::Blocked => { + let body = CreateUserRelationshipSchema { + relationship_type: Some(RelationshipType::Blocked), + from_friend_suggestion: None, + friend_token: None, + }; + let request = Client::new() + .put(format!("{}/users/@me/relationships/{}/", api_url, user_id)) + .bearer_auth(self.token()) + .body(to_string(&body).unwrap()); + handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await + } + RelationshipType::Suggestion | RelationshipType::Implicit => None, + } + } + + /// Removes the relationship between the authenticated user and the specified user. + /// + /// # Arguments + /// + /// * `user_id` - A string slice that holds the ID of the user to remove the relationship with. + /// + /// # Returns + /// This function returns an [`Option`] that holds a [`ChorusLibError`] if the request fails. + pub async fn remove_relationship(&mut self, user_id: &str) -> Option { + let belongs_to = self.belongs_to.borrow(); + let url = format!( + "{}/users/@me/relationships/{}/", + belongs_to.urls.get_api(), + user_id + ); + drop(belongs_to); + let request = Client::new().post(url).bearer_auth(self.token()); + handle_request_as_option(request, self, crate::api::limits::LimitType::Global).await + } } diff --git a/src/errors.rs b/src/errors.rs index 0f65669..057f57f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -18,13 +18,14 @@ custom_error! { CantGetInfoError{error:String} = "Something seems to be wrong with the instance. Cannot get information about the instance: {error}", InvalidFormBodyError{error_type: String, error:String} = "The server responded with: {error_type}: {error}", RateLimited{bucket:String} = "Ratelimited on Bucket {bucket}", - MultipartCreationError{error: String} = "Got an error whilst creating the form: {}", - FormCreationError{error: String} = "Got an error whilst creating the form: {}", + MultipartCreationError{error: String} = "Got an error whilst creating the form: {error}", + FormCreationError{error: String} = "Got an error whilst creating the form: {error}", TokenExpired = "Token expired, invalid or not found.", NoPermission = "You do not have the permissions needed to perform this action.", - NotFound{error: String} = "The provided resource hasn't been found: {}", + NotFound{error: String} = "The provided resource hasn't been found: {error}", PasswordRequiredError = "You need to provide your current password to authenticate for this action.", - InvalidResponseError{error: String} = "The response is malformed and cannot be processed. Error: {}", + InvalidResponseError{error: String} = "The response is malformed and cannot be processed. Error: {error}", + InvalidArgumentsError{error: String} = "Invalid arguments were provided. Error: {error}" } custom_error! { diff --git a/src/types/entities/relationship.rs b/src/types/entities/relationship.rs index fb8acee..b965907 100644 --- a/src/types/entities/relationship.rs +++ b/src/types/entities/relationship.rs @@ -30,9 +30,3 @@ pub enum RelationshipType { Friends = 1, None = 0, } - -#[derive(Deserialize, Serialize, Debug)] -pub struct FriendRequestSendSchema { - pub username: String, - pub discriminator: Option, -} diff --git a/src/types/schema/relationship.rs b/src/types/schema/relationship.rs index 8b13789..b0a60d6 100644 --- a/src/types/schema/relationship.rs +++ b/src/types/schema/relationship.rs @@ -1 +1,25 @@ +use serde::{Deserialize, Serialize}; +use crate::types::RelationshipType; + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct FriendRequestSendSchema { + pub username: String, + pub discriminator: Option, +} + +/// Represents the schema for the Create User Relationship route. +/// # Arguments +/// +/// * relationship_type: The [`RelationshipType`] to create (defaults to -1, which accepts an existing or creates a new friend request) +/// * from_friend_suggestion: Whether the relationship was created from a friend suggestion (default false) +/// * friend_token: The friend token of the user to add a direct friend relationship to +/// +/// See: [https://discord-userdoccers.vercel.app/resources/user#create-user-relationship](https://discord-userdoccers.vercel.app/resources/user#create-user-relationship) +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct CreateUserRelationshipSchema { + #[serde(rename = "type")] + pub relationship_type: Option, + pub from_friend_suggestion: Option, + pub friend_token: Option, +} diff --git a/tests/relationships.rs b/tests/relationships.rs index 08f7efb..b575046 100644 --- a/tests/relationships.rs +++ b/tests/relationships.rs @@ -18,13 +18,49 @@ async fn test_get_mutual_relationships() { ) .unwrap(); - let bundle = common::setup().await; - let mut belongs_to = bundle.instance; - let mut user = bundle.user; - let other_user = belongs_to.register_account(®ister_schema).await.unwrap(); + let mut bundle = common::setup().await; + let belongs_to = &mut bundle.instance; + let user = &mut bundle.user; + let mut other_user = belongs_to.register_account(®ister_schema).await.unwrap(); + let friend_request_schema = types::FriendRequestSendSchema { + username: user.object.username.clone(), + discriminator: Some(user.object.discriminator.clone()), + }; + other_user.send_friend_request(friend_request_schema).await; let relationships = user .get_mutual_relationships(&other_user.object.id.to_string()) .await .unwrap(); - println!("{:?}", relationships.unwrap()); + println!("{:?}", relationships); + common::teardown(bundle).await +} + +#[tokio::test] +async fn test_get_relationships() { + let register_schema = types::RegisterSchema::new( + "integrationtestuser2".to_string(), + None, + true, + None, + None, + None, + Some("2000-01-01".to_string()), + None, + None, + None, + ) + .unwrap(); + + let mut bundle = common::setup().await; + let belongs_to = &mut bundle.instance; + let user = &mut bundle.user; + let mut other_user = belongs_to.register_account(®ister_schema).await.unwrap(); + let friend_request_schema = types::FriendRequestSendSchema { + username: user.object.username.clone(), + discriminator: Some(user.object.discriminator.clone()), + }; + other_user.send_friend_request(friend_request_schema).await; + let relationships = user.get_relationships().await.unwrap(); + println!("{:?}", relationships); + common::teardown(bundle).await }