From 71533b9860ec593500e7594879a1b9c4393078ae Mon Sep 17 00:00:00 2001 From: Aravinth Manivannan Date: Wed, 9 Nov 2022 14:01:15 +0530 Subject: [PATCH] feat: db: add, rm, list and get site --- .../20220921122103_librepages_sites.sql | 10 ++ sqlx-data.json | 120 ++++++++++++- src/db.rs | 165 +++++++++++++++++- 3 files changed, 286 insertions(+), 9 deletions(-) create mode 100644 migrations/20220921122103_librepages_sites.sql diff --git a/migrations/20220921122103_librepages_sites.sql b/migrations/20220921122103_librepages_sites.sql new file mode 100644 index 0000000..bb959f8 --- /dev/null +++ b/migrations/20220921122103_librepages_sites.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS librepages_sites ( + site_secret VARCHAR(32) NOT NULL UNIQUE, + repo_url VARCHAR(3000) NOT NULL UNIQUE, + branch TEXT NOT NULL, + hostname VARCHAR(3000) NOT NULL UNIQUE, + ID SERIAL PRIMARY KEY NOT NULL, + owned_by INTEGER NOT NULL references librepages_users(ID) ON DELETE CASCADE +); + +CREATE UNIQUE INDEX librepages_sites_site_secret ON librepages_sites(site_secret); diff --git a/sqlx-data.json b/sqlx-data.json index e0b11f3..773c9ce 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -39,6 +39,19 @@ }, "query": "UPDATE librepages_users set name = $1\n WHERE name = $2" }, + "416b9f0412f0d7ee05d4a350839c5a6d1e06c1d7f8942744f6d806ddc47084c2": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Text", + "Text" + ] + } + }, + "query": "DELETE FROM librepages_sites\n WHERE hostname = ($1)\n AND owned_by = ( SELECT ID FROM librepages_users WHERE name = $2);\n " + }, "5c5d774bde06c0ab83c3616a56a28f12dfd9c546cbaac9f246d3b350c587823e": { "describe": { "columns": [], @@ -51,6 +64,22 @@ }, "query": "DELETE FROM librepages_users WHERE name = ($1)" }, + "67311c6196639edd153b7b7dd56a37703b67abe750b88f5afdcf0d3d779432e7": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Text", + "Varchar", + "Text" + ] + } + }, + "query": "\n INSERT INTO librepages_sites\n (site_secret, repo_url, branch, hostname, owned_by)\n VALUES ($1, $2, $3, $4, ( SELECT ID FROM librepages_users WHERE name = $5 ));\n " + }, "6a557f851d4f47383b864085093beb0954e79779f21b655978f07e285281e0ac": { "describe": { "columns": [], @@ -97,19 +126,82 @@ }, "query": "UPDATE librepages_users set password = $1\n WHERE name = $2" }, - "aa0e7d72a4542f28db816a8cc20e3f7b778e90e3cbe982a5a24af0b682adaf7d": { + "9fd163d10860ad4519f9398582aaa0615d6d7b784e844ee71038f77dcd069eed": { "describe": { - "columns": [], - "nullable": [], + "columns": [ + { + "name": "site_secret", + "ordinal": 0, + "type_info": "Varchar" + }, + { + "name": "repo_url", + "ordinal": 1, + "type_info": "Varchar" + }, + { + "name": "branch", + "ordinal": 2, + "type_info": "Text" + }, + { + "name": "hostname", + "ordinal": 3, + "type_info": "Varchar" + } + ], + "nullable": [ + false, + false, + false, + false + ], "parameters": { "Left": [ - "Varchar", - "Text", - "Varchar" + "Text" ] } }, - "query": "insert into librepages_users\n (name , password, email) values ($1, $2, $3)" + "query": "SELECT site_secret, repo_url, branch, hostname\n FROM librepages_sites\n WHERE owned_by = (SELECT ID FROM librepages_users WHERE name = $1 );\n " + }, + "aad26d1f932001cbe49b147348aa528eca5101ec6ef83cb034e1ccd0dbd17878": { + "describe": { + "columns": [ + { + "name": "site_secret", + "ordinal": 0, + "type_info": "Varchar" + }, + { + "name": "repo_url", + "ordinal": 1, + "type_info": "Varchar" + }, + { + "name": "branch", + "ordinal": 2, + "type_info": "Text" + }, + { + "name": "hostname", + "ordinal": 3, + "type_info": "Varchar" + } + ], + "nullable": [ + false, + false, + false, + false + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + } + }, + "query": "SELECT site_secret, repo_url, branch, hostname\n FROM librepages_sites\n WHERE owned_by = (SELECT ID FROM librepages_users WHERE name = $1 )\n AND hostname = $2;\n " }, "b48c77db6e663d97df44bf9ec2ee92fd3e02f2dcbcdbd1d491e09fab2da68494": { "describe": { @@ -176,5 +268,19 @@ } }, "query": "SELECT email FROM librepages_users WHERE name = $1" + }, + "faa4170a309f19a4abf1ca3f8dd3c0526945aa00f028ebf8bd7063825d448f5b": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Varchar", + "Text", + "Varchar" + ] + } + }, + "query": "INSERT INTO librepages_users\n (name , password, email) VALUES ($1, $2, $3)" } } \ No newline at end of file diff --git a/src/db.rs b/src/db.rs index f2a33fd..af154ab 100644 --- a/src/db.rs +++ b/src/db.rs @@ -22,6 +22,7 @@ use sqlx::types::time::OffsetDateTime; //use sqlx::types::Json; use sqlx::ConnectOptions; use sqlx::PgPool; +use url::quirks::hostname; use crate::errors::*; @@ -96,8 +97,8 @@ impl Database { /// register a new user pub async fn register(&self, p: &Register<'_>) -> ServiceResult<()> { sqlx::query!( - "insert into librepages_users - (name , password, email) values ($1, $2, $3)", + "INSERT INTO librepages_users + (name , password, email) VALUES ($1, $2, $3)", &p.username, &p.hash, &p.email, @@ -248,6 +249,107 @@ impl Database { Ok(()) } + + pub async fn add_site(&self, msg: &Site) -> ServiceResult<()> { + sqlx::query!( + " + INSERT INTO librepages_sites + (site_secret, repo_url, branch, hostname, owned_by) + VALUES ($1, $2, $3, $4, ( SELECT ID FROM librepages_users WHERE name = $5 )); + ", + msg.site_secret, + msg.repo_url, + msg.branch, + msg.hostname, + msg.owner, + ) + .execute(&self.pool) + .await + .map_err(|e| map_row_not_found_err(e, ServiceError::AccountNotFound))?; + + Ok(()) + } + + pub async fn get_site(&self, owner: &str, hostname: &str) -> ServiceResult { + let site = sqlx::query_as!( + InnerSite, + "SELECT site_secret, repo_url, branch, hostname + FROM librepages_sites + WHERE owned_by = (SELECT ID FROM librepages_users WHERE name = $1 ) + AND hostname = $2; + ", + owner, + hostname + ) + .fetch_one(&self.pool) + .await + .map_err(|e| map_row_not_found_err(e, ServiceError::AccountNotFound))?; + + let res = site.to_site(owner.into()); + + Ok(res) + } + + pub async fn list_all_sites(&self, owner: &str) -> ServiceResult> { + let mut sites = sqlx::query_as!( + InnerSite, + "SELECT site_secret, repo_url, branch, hostname + FROM librepages_sites + WHERE owned_by = (SELECT ID FROM librepages_users WHERE name = $1 ); + ", + owner, + ) + .fetch_all(&self.pool) + .await + .map_err(|e| map_row_not_found_err(e, ServiceError::AccountNotFound))?; + + let res = sites.drain(0..).map(|s| s.to_site(owner.into())).collect(); + + Ok(res) + } + + pub async fn delete_site(&self, owner: &str, hostname: &str) -> ServiceResult<()> { + sqlx::query!( + "DELETE FROM librepages_sites + WHERE hostname = ($1) + AND owned_by = ( SELECT ID FROM librepages_users WHERE name = $2); + ", + hostname, + owner + ) + .execute(&self.pool) + .await + .map_err(|e| map_row_not_found_err(e, ServiceError::AccountNotFound))?; + Ok(()) + } +} +struct InnerSite { + site_secret: String, + repo_url: String, + branch: String, + hostname: String, +} + +impl InnerSite { + fn to_site(self, owner: String) -> Site { + Site { + site_secret: self.site_secret, + repo_url: self.repo_url, + branch: self.branch, + hostname: self.hostname, + owner, + } + } +} + +#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)] +/// Data required to add a new site +pub struct Site { + pub site_secret: String, + pub repo_url: String, + pub branch: String, + pub hostname: String, + pub owner: String, } #[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)] @@ -454,4 +556,63 @@ mod tests { "user is deleted so username shouldn't exist" ); } + + #[actix_rt::test] + pub async fn test_db_sites() { + let settings = Settings::new().unwrap(); + let pool_options = PgPoolOptions::new().max_connections(1); + let db = ConnectionOptions::Fresh(Fresh { + pool_options, + url: settings.database.url.clone(), + disable_logging: !settings.debug, + }) + .connect() + .await + .unwrap(); + assert!(db.ping().await); + + const EMAIL: &str = "postgresdbsiteuser@foo.com"; + const NAME: &str = "postgresdbsiteuser"; + const PASSWORD: &str = "pasdfasdfasdfadf"; + + db.migrate().await.unwrap(); + let p = super::Register { + username: NAME, + email: EMAIL, + hash: PASSWORD, + }; + + if db.username_exists(p.username).await.unwrap() { + db.delete_user(p.username).await.unwrap(); + assert!( + !db.username_exists(p.username).await.unwrap(), + "user is deleted so username shouldn't exist" + ); + } + + db.register(&p).await.unwrap(); + + // testing adding site + let site = Site { + site_secret: "foobar".into(), + repo_url: "https://git.batsense.net/LibrePages/librepages.git".into(), + branch: "librepages".into(), + hostname: "db_works.tests.librepages.librepages.org".into(), + owner: p.username.into(), + }; + db.add_site(&site).await.unwrap(); + + // get site + let db_site = db.get_site(p.username, &site.hostname).await.unwrap(); + assert_eq!(db_site, site); + + // list all sites owned by user + let db_sites = db.list_all_sites(p.username).await.unwrap(); + assert_eq!(db_sites.len(), 1); + assert_eq!(db_sites, vec![site.clone()]); + + // delete site + db.delete_site(p.username, &site.hostname).await.unwrap(); + assert!(db.list_all_sites(p.username).await.unwrap().is_empty()); + } }