diff --git a/Cargo.toml b/Cargo.toml index b7c7207..6b9724f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,4 +17,7 @@ custom_error = "1.9.2" native-tls = "0.2.11" tokio-tungstenite = {version = "0.19.0", features = ["native-tls"]} futures-util = "0.3.28" -http = "0.2.9" \ No newline at end of file +http = "0.2.9" + +[dev-dependencies] +lazy_static = "1.4.0" \ No newline at end of file diff --git a/src/api/auth/login.rs b/src/api/auth/login.rs index 67647f7..453ff1b 100644 --- a/src/api/auth/login.rs +++ b/src/api/auth/login.rs @@ -1,4 +1,7 @@ pub mod login { + use std::cell::RefCell; + use std::rc::Rc; + use reqwest::Client; use serde_json::{from_str, json}; @@ -7,13 +10,14 @@ pub mod login { use crate::api::types::{ErrorResponse, LoginResult}; use crate::errors::InstanceServerError; use crate::instance::Instance; + use crate::limit::LimitedRequester; impl Instance { pub async fn login_account( &mut self, login_schema: &LoginSchema, ) -> Result { - let requester = &mut self.requester; + let mut requester = LimitedRequester::new().await; let json_schema = json!(login_schema); let client = Client::new(); let endpoint_url = self.urls.get_api().to_string() + "/auth/login"; @@ -56,7 +60,7 @@ pub mod login { .await .unwrap(); let user = crate::api::types::User::new( - self, + Rc::new(RefCell::new(self.clone())), login_result.token, cloned_limits, login_result.settings, diff --git a/src/api/auth/register.rs b/src/api/auth/register.rs index 28c9c96..edafc5d 100644 --- a/src/api/auth/register.rs +++ b/src/api/auth/register.rs @@ -1,4 +1,6 @@ pub mod register { + use std::{cell::RefCell, rc::Rc}; + use reqwest::Client; use serde_json::{from_str, json}; @@ -6,6 +8,7 @@ pub mod register { api::{limits::LimitType, schemas::RegisterSchema, types::ErrorResponse, Token}, errors::InstanceServerError, instance::Instance, + limit::LimitedRequester, }; impl Instance { @@ -21,7 +24,7 @@ pub mod register { register_schema: &RegisterSchema, ) -> Result { let json_schema = json!(register_schema); - let limited_requester = &mut self.requester; + let mut limited_requester = LimitedRequester::new().await; let client = Client::new(); let endpoint_url = self.urls.get_api().to_string() + "/auth/register"; let request_builder = client.post(endpoint_url).body(json_schema.to_string()); @@ -67,7 +70,7 @@ pub mod register { .await .unwrap(); let user: crate::api::types::User = crate::api::types::User::new( - self, + Rc::new(RefCell::new(self.clone())), token.clone(), cloned_limits, settings, @@ -93,9 +96,7 @@ mod test { "http://localhost:3001".to_string(), ); let limited_requester = LimitedRequester::new().await; - let mut test_instance = Instance::new(urls.clone(), limited_requester) - .await - .unwrap(); + let mut test_instance = Instance::new(urls.clone()).await.unwrap(); let reg = RegisterSchema::new( AuthUsername::new("Hiiii".to_string()).unwrap(), None, diff --git a/src/api/channels/messages.rs b/src/api/channels/messages.rs index ffa3f06..0aaf8d1 100644 --- a/src/api/channels/messages.rs +++ b/src/api/channels/messages.rs @@ -4,6 +4,7 @@ pub mod messages { use reqwest::{multipart, Client}; use serde_json::to_string; + use crate::api::limits::Limits; use crate::api::types::{Message, PartialDiscordFileAttachment, User}; use crate::limit::LimitedRequester; @@ -20,16 +21,15 @@ pub mod messages { * [`InstanceServerError`] - If the message cannot be sent. */ pub async fn send<'a>( - url_api: &String, - channel_id: &String, + url_api: String, + channel_id: String, message: &mut crate::api::schemas::MessageSendSchema, files: Option>, - token: &String, - user: &mut User<'a>, + token: String, + limits_user: &mut Limits, + limits_instance: &mut Limits, ) -> Result { let mut requester = LimitedRequester::new().await; - let user_rate_limits = &mut user.limits; - let instance_rate_limits = &mut user.belongs_to.limits; if files.is_none() { let message_request = Client::new() @@ -40,8 +40,8 @@ pub mod messages { .send_request( message_request, crate::api::limits::LimitType::Channel, - instance_rate_limits, - user_rate_limits, + limits_instance, + limits_user, ) .await } else { @@ -83,29 +83,31 @@ pub mod messages { .send_request( message_request, crate::api::limits::LimitType::Channel, - instance_rate_limits, - user_rate_limits, + limits_instance, + limits_user, ) .await } } } - impl<'a> User<'a> { + impl User { pub async fn send_message( &mut self, message: &mut crate::api::schemas::MessageSendSchema, - channel_id: &String, + channel_id: String, files: Option>, ) -> Result { let token = self.token().clone(); + let mut belongs_to = self.belongs_to.borrow_mut(); Message::send( - &self.belongs_to.urls.get_api().to_string(), + belongs_to.urls.get_api().to_string(), channel_id, message, files, - &token, - self, + token, + &mut self.limits, + &mut belongs_to.limits, ) .await } @@ -120,9 +122,9 @@ mod test { limit::LimitedRequester, }; - use std::fs::File; - use std::io::BufReader; use std::io::Read; + use std::{cell::RefCell, fs::File}; + use std::{io::BufReader, rc::Rc}; #[tokio::test] async fn send_message() { @@ -139,14 +141,11 @@ mod test { None, None, ); - let mut instance = Instance::new( - crate::URLBundle { - api: "http://localhost:3001/api".to_string(), - wss: "ws://localhost:3001/".to_string(), - cdn: "http://localhost:3001".to_string(), - }, - LimitedRequester::new().await, - ) + let mut instance = Instance::new(crate::URLBundle { + api: "http://localhost:3001/api".to_string(), + wss: "ws://localhost:3001/".to_string(), + cdn: "http://localhost:3001".to_string(), + }) .await .unwrap(); let login_schema: LoginSchema = LoginSchema::new( @@ -163,9 +162,15 @@ mod test { println!("TOKEN: {}", token); let settings = login_result.settings; let limits = instance.limits.clone(); - let mut user = crate::api::types::User::new(&mut instance, token, limits, settings, None); + let mut user = crate::api::types::User::new( + Rc::new(RefCell::new(instance)), + token, + limits, + settings, + None, + ); let _ = user - .send_message(&mut message, &channel_id, None) + .send_message(&mut message, channel_id, None) .await .unwrap(); } @@ -208,14 +213,11 @@ mod test { None, Some(vec![attachment.clone()]), ); - let mut instance = Instance::new( - crate::URLBundle { - api: "http://localhost:3001/api".to_string(), - wss: "ws://localhost:3001/".to_string(), - cdn: "http://localhost:3001".to_string(), - }, - LimitedRequester::new().await, - ) + let mut instance = Instance::new(crate::URLBundle { + api: "http://localhost:3001/api".to_string(), + wss: "ws://localhost:3001/".to_string(), + cdn: "http://localhost:3001".to_string(), + }) .await .unwrap(); let login_schema: LoginSchema = LoginSchema::new( @@ -231,11 +233,17 @@ mod test { let token = login_result.token; let settings = login_result.settings; let limits = instance.limits.clone(); - let mut user = crate::api::types::User::new(&mut instance, token, limits, settings, None); + let mut user = crate::api::types::User::new( + Rc::new(RefCell::new(instance)), + token, + limits, + settings, + None, + ); let vec_attach = vec![attachment.clone()]; let _arg = Some(&vec_attach); let response = user - .send_message(&mut message, &channel_id, Some(vec![attachment.clone()])) + .send_message(&mut message, channel_id, Some(vec![attachment.clone()])) .await .unwrap(); println!("[Response:] {}", response.text().await.unwrap()); diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs index 23249b9..5bbbd23 100644 --- a/src/api/guilds/guilds.rs +++ b/src/api/guilds/guilds.rs @@ -35,13 +35,13 @@ impl<'a> types::Guild { /// } /// ``` pub async fn create( - user: &mut types::User<'a>, + user: &mut types::User, url_api: &str, guild_create_schema: schemas::GuildCreateSchema, ) -> Result { let url = format!("{}/guilds/", url_api); let limits_user = user.limits.get_as_mut(); - let limits_instance = &mut user.belongs_to.limits; + let limits_instance = &mut user.belongs_to.borrow_mut().limits; let request = reqwest::Client::new() .post(url.clone()) .bearer_auth(user.token.clone()) @@ -88,13 +88,13 @@ impl<'a> types::Guild { /// } /// ``` pub async fn delete( - user: &mut types::User<'a>, + user: &mut types::User, url_api: &str, guild_id: String, ) -> Option { let url = format!("{}/guilds/{}/delete/", url_api, guild_id); let limits_user = user.limits.get_as_mut(); - let limits_instance = &mut user.belongs_to.limits; + let limits_instance = &mut user.belongs_to.borrow_mut().limits; let request = reqwest::Client::new() .post(url.clone()) .bearer_auth(user.token.clone()); @@ -114,56 +114,3 @@ impl<'a> types::Guild { } } } - -#[cfg(test)] -mod test { - use crate::api::schemas; - use crate::api::types; - use crate::instance::Instance; - - #[tokio::test] - async fn guild_creation_deletion() { - let mut instance = Instance::new( - crate::URLBundle { - api: "http://localhost:3001/api".to_string(), - wss: "ws://localhost:3001/".to_string(), - cdn: "http://localhost:3001".to_string(), - }, - crate::limit::LimitedRequester::new().await, - ) - .await - .unwrap(); - let login_schema: schemas::LoginSchema = schemas::LoginSchema::new( - schemas::AuthUsername::new("user@test.xyz".to_string()).unwrap(), - "transrights".to_string(), - None, - None, - None, - None, - ) - .unwrap(); - let mut user = instance.login_account(&login_schema).await.unwrap(); - - let guild_create_schema = schemas::GuildCreateSchema { - name: Some("test".to_string()), - region: None, - icon: None, - channels: None, - guild_template_code: None, - system_channel_id: None, - rules_channel_id: None, - }; - - let guild = - types::Guild::create(&mut user, "http://localhost:3001/api", guild_create_schema) - .await - .unwrap(); - - println!("{}", guild); - - match types::Guild::delete(&mut user, "http://localhost:3001/api", guild).await { - None => assert!(true), - Some(_) => assert!(false), - } - } -} diff --git a/src/api/policies/instance/instance.rs b/src/api/policies/instance/instance.rs index b864e54..2196b80 100644 --- a/src/api/policies/instance/instance.rs +++ b/src/api/policies/instance/instance.rs @@ -47,9 +47,7 @@ mod instance_policies_schema_test { "http://localhost:3001".to_string(), ); let limited_requester = LimitedRequester::new().await; - let test_instance = Instance::new(urls.clone(), limited_requester) - .await - .unwrap(); + let test_instance = Instance::new(urls.clone()).await.unwrap(); let _schema = test_instance.instance_policies_schema().await.unwrap(); } diff --git a/src/api/types.rs b/src/api/types.rs index 00ac020..9433150 100644 --- a/src/api/types.rs +++ b/src/api/types.rs @@ -4,8 +4,7 @@ https://discord.com/developers/docs . I do not feel like re-documenting all of this, as everything is already perfectly explained there. */ -use std::collections::HashMap; - +use std::{cell::RefCell, rc::Rc, collections::HashMap}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_json::from_value; @@ -394,19 +393,15 @@ pub struct UserObject { } #[derive(Debug)] -pub struct User<'a> { - pub belongs_to: &'a mut Instance, +pub struct User { + pub belongs_to: Rc>, pub token: String, pub limits: Limits, pub settings: UserSettings, pub object: Option, } -impl<'a> User<'a> { - pub fn belongs_to(&mut self) -> &mut Instance { - self.belongs_to - } - +impl User { pub fn token(&self) -> String { self.token.clone() } @@ -416,12 +411,12 @@ impl<'a> User<'a> { } pub fn new( - belongs_to: &'a mut Instance, + belongs_to: Rc>, token: String, limits: Limits, settings: UserSettings, object: Option, - ) -> User<'a> { + ) -> User { User { belongs_to, token, diff --git a/src/api/users/users.rs b/src/api/users/users.rs index 7d97734..cf052aa 100644 --- a/src/api/users/users.rs +++ b/src/api/users/users.rs @@ -12,7 +12,7 @@ use crate::{ limit::LimitedRequester, }; -impl<'a> User<'a> { +impl User { /** Get a user object by id, or get the current user. # Arguments @@ -99,7 +99,10 @@ impl<'a> User<'a> { return Err(InstanceServerError::PasswordRequiredError); } let request = Client::new() - .patch(format!("{}/users/@me/", self.belongs_to.urls.get_api())) + .patch(format!( + "{}/users/@me/", + self.belongs_to.borrow_mut().urls.get_api() + )) .body(to_string(&modify_schema).unwrap()) .bearer_auth(self.token()); let result = match LimitedRequester::new() @@ -107,7 +110,7 @@ impl<'a> User<'a> { .send_request( request, crate::api::limits::LimitType::Global, - &mut self.belongs_to.limits, + &mut self.belongs_to.borrow_mut().limits, &mut self.limits, ) .await @@ -122,6 +125,35 @@ impl<'a> User<'a> { ); Ok(user_updated) } + + /// Sends a request to the server which deletes the user from the Instance. + /// + /// # Arguments + /// + /// * `self` - The `User` object to delete. + /// + /// # Returns + /// + /// Returns `None` if the user was successfully deleted, or an `InstanceServerError` if an error occurred. + pub async fn delete(mut self) -> Option { + let mut belongs_to = self.belongs_to.borrow_mut(); + let request = Client::new() + .post(format!("{}/users/@me/delete/", belongs_to.urls.get_api())) + .bearer_auth(self.token); + match LimitedRequester::new() + .await + .send_request( + request, + crate::api::limits::LimitType::Global, + &mut belongs_to.limits, + &mut self.limits, + ) + .await + { + Ok(_) => None, + Err(e) => Some(e), + } + } } impl Instance { diff --git a/src/instance.rs b/src/instance.rs index ee33961..ab4b4b2 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -1,20 +1,17 @@ use crate::api::limits::Limits; -use crate::api::types::{InstancePolicies}; +use crate::api::types::InstancePolicies; use crate::errors::{FieldFormatError, InstanceServerError}; -use crate::limit::LimitedRequester; use crate::URLBundle; - use std::fmt; -#[derive(Debug)] +#[derive(Debug, Clone)] /** The [`Instance`] what you will be using to perform all sorts of actions on the Spacebar server. */ pub struct Instance { pub urls: URLBundle, pub instance_info: InstancePolicies, - pub requester: LimitedRequester, pub limits: Limits, } @@ -25,10 +22,7 @@ impl Instance { /// * `requester` - The [`LimitedRequester`] that will be used to make requests to the Spacebar server. /// # Errors /// * [`InstanceError`] - If the instance cannot be created. - pub async fn new( - urls: URLBundle, - requester: LimitedRequester, - ) -> Result { + pub async fn new(urls: URLBundle) -> Result { let mut instance = Instance { urls: urls.clone(), instance_info: InstancePolicies::new( @@ -43,7 +37,6 @@ impl Instance { None, ), limits: Limits::check_limits(urls.api).await, - requester, }; instance.instance_info = match instance.instance_policies_schema().await { Ok(schema) => schema, diff --git a/tests/integration.rs b/tests/integration.rs new file mode 100644 index 0000000..e004496 --- /dev/null +++ b/tests/integration.rs @@ -0,0 +1,74 @@ +use chorus::{ + api::{AuthUsername, RegisterSchema, User}, + instance::Instance, + URLBundle, +}; + +#[derive(Debug)] +struct TestBundle { + urls: URLBundle, + user: User, +} + +// Set up a test by creating an Instance and a User. Reduces Test boilerplate. +async fn setup() -> TestBundle { + let urls = URLBundle::new( + "http://localhost:3001/api".to_string(), + "ws://localhost:3001".to_string(), + "http://localhost:3001".to_string(), + ); + let mut instance = Instance::new(urls.clone()).await.unwrap(); + // Requires the existance of the below user. + let reg = RegisterSchema::new( + AuthUsername::new("integrationtestuser".to_string()).unwrap(), + None, + true, + None, + None, + None, + Some("2000-01-01".to_string()), + None, + None, + None, + ) + .unwrap(); + let user = instance.register_account(®).await.unwrap(); + + TestBundle { urls, user } +} + +// Teardown method to clean up after a test. +async fn teardown(bundle: TestBundle) { + bundle.user.delete().await; +} + +mod guild { + use chorus::api::{schemas, types}; + + #[tokio::test] + async fn guild_creation_deletion() { + let mut bundle = crate::setup().await; + + let guild_create_schema = schemas::GuildCreateSchema { + name: Some("test".to_string()), + region: None, + icon: None, + channels: None, + guild_template_code: None, + system_channel_id: None, + rules_channel_id: None, + }; + + let guild = + types::Guild::create(&mut bundle.user, bundle.urls.get_api(), guild_create_schema) + .await + .unwrap(); + + println!("{}", guild); + + match types::Guild::delete(&mut bundle.user, bundle.urls.get_api(), guild).await { + None => assert!(true), + Some(_) => assert!(false), + } + } +}