fedimovies/src/models/profiles/queries.rs
2021-09-14 12:24:05 +00:00

273 lines
7.3 KiB
Rust

use tokio_postgres::GenericClient;
use uuid::Uuid;
use crate::errors::DatabaseError;
use super::types::{DbActorProfile, ProfileCreateData, ProfileUpdateData};
/// Create new profile using given Client or Transaction.
pub async fn create_profile(
db_client: &impl GenericClient,
profile_data: &ProfileCreateData,
) -> Result<DbActorProfile, DatabaseError> {
let profile_id = Uuid::new_v4();
let result = db_client.query_one(
"
INSERT INTO actor_profile (
id, username, display_name, acct, bio, bio_source,
avatar_file_name, banner_file_name,
actor_json
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING actor_profile
",
&[
&profile_id,
&profile_data.username,
&profile_data.display_name,
&profile_data.acct,
&profile_data.bio,
&profile_data.bio,
&profile_data.avatar,
&profile_data.banner,
&profile_data.actor,
],
).await;
let profile = match result {
Ok(row) => row.try_get("actor_profile")?,
Err(err) => {
// TODO: catch profile already exists error
log::warn!("{}", err);
return Err(DatabaseError::AlreadyExists("profile"));
},
};
Ok(profile)
}
pub async fn update_profile(
db_client: &impl GenericClient,
profile_id: &Uuid,
data: ProfileUpdateData,
) -> Result<DbActorProfile, DatabaseError> {
let maybe_row = db_client.query_opt(
"
UPDATE actor_profile
SET
display_name = $1,
bio = $2,
bio_source = $3,
avatar_file_name = $4,
banner_file_name = $5
WHERE id = $6
RETURNING actor_profile
",
&[
&data.display_name,
&data.bio,
&data.bio_source,
&data.avatar,
&data.banner,
&profile_id,
],
).await?;
let profile = match maybe_row {
Some(row) => row.try_get("actor_profile")?,
None => return Err(DatabaseError::NotFound("profile")),
};
Ok(profile)
}
pub async fn get_profile_by_id(
db_client: &impl GenericClient,
profile_id: &Uuid,
) -> Result<DbActorProfile, DatabaseError> {
let result = db_client.query_opt(
"
SELECT actor_profile
FROM actor_profile
WHERE id = $1
",
&[&profile_id],
).await?;
let profile = match result {
Some(row) => row.try_get("actor_profile")?,
None => return Err(DatabaseError::NotFound("profile")),
};
Ok(profile)
}
pub async fn get_profile_by_actor_id(
db_client: &impl GenericClient,
actor_id: &str,
) -> Result<DbActorProfile, DatabaseError> {
let result = db_client.query_opt(
"
SELECT actor_profile
FROM actor_profile
WHERE actor_profile.actor_json ->> 'id' = $1
",
&[&actor_id],
).await?;
let profile = match result {
Some(row) => row.try_get("actor_profile")?,
None => return Err(DatabaseError::NotFound("profile")),
};
Ok(profile)
}
pub async fn get_profile_by_acct(
db_client: &impl GenericClient,
account_uri: &str,
) -> Result<DbActorProfile, DatabaseError> {
let result = db_client.query_opt(
"
SELECT actor_profile
FROM actor_profile
WHERE actor_profile.acct = $1
",
&[&account_uri],
).await?;
let profile = match result {
Some(row) => row.try_get("actor_profile")?,
None => return Err(DatabaseError::NotFound("profile")),
};
Ok(profile)
}
pub async fn get_profiles(
db_client: &impl GenericClient,
) -> Result<Vec<DbActorProfile>, DatabaseError> {
let rows = db_client.query(
"
SELECT actor_profile
FROM actor_profile
ORDER BY username
",
&[],
).await?;
let profiles = rows.iter()
.map(|row| row.try_get("actor_profile"))
.collect::<Result<Vec<DbActorProfile>, _>>()?;
Ok(profiles)
}
pub async fn get_followers(
db_client: &impl GenericClient,
profile_id: &Uuid,
) -> Result<Vec<DbActorProfile>, DatabaseError> {
let rows = db_client.query(
"
SELECT actor_profile
FROM actor_profile
JOIN relationship
ON (actor_profile.id = relationship.source_id)
WHERE relationship.target_id = $1
",
&[&profile_id],
).await?;
let profiles = rows.iter()
.map(|row| row.try_get("actor_profile"))
.collect::<Result<Vec<DbActorProfile>, _>>()?;
Ok(profiles)
}
pub async fn delete_profile(
db_client: &impl GenericClient,
profile_id: &Uuid,
) -> Result<(), DatabaseError> {
let deleted_count = db_client.execute(
"DELETE FROM actor_profile WHERE id = $1",
&[&profile_id],
).await?;
if deleted_count == 0 {
return Err(DatabaseError::NotFound("profile"));
}
Ok(())
}
pub async fn search_profile(
db_client: &impl GenericClient,
username: &String,
instance: &Option<String>,
) -> Result<Vec<DbActorProfile>, DatabaseError> {
let db_search_query = match &instance {
Some(instance) => {
// Search for exact profile name.
// Fetch from remote server if not found
format!("{}@{}", username, instance)
},
None => {
// Search for username
format!("%{}%", username)
},
};
let rows = db_client.query(
"
SELECT actor_profile
FROM actor_profile
WHERE acct LIKE $1
",
&[&db_search_query],
).await?;
let profiles: Vec<DbActorProfile> = rows.iter()
.map(|row| row.try_get("actor_profile"))
.collect::<Result<_, _>>()?;
Ok(profiles)
}
pub async fn update_follower_count(
db_client: &impl GenericClient,
profile_id: &Uuid,
change: i32,
) -> Result<DbActorProfile, DatabaseError> {
let maybe_row = db_client.query_opt(
"
UPDATE actor_profile
SET follower_count = follower_count + $1
WHERE id = $2
RETURNING actor_profile
",
&[&change, &profile_id],
).await?;
let row = maybe_row.ok_or(DatabaseError::NotFound("profile"))?;
let profile = row.try_get("actor_profile")?;
Ok(profile)
}
pub async fn update_following_count(
db_client: &impl GenericClient,
profile_id: &Uuid,
change: i32,
) -> Result<DbActorProfile, DatabaseError> {
let maybe_row = db_client.query_opt(
"
UPDATE actor_profile
SET following_count = following_count + $1
WHERE id = $2
RETURNING actor_profile
",
&[&change, &profile_id],
).await?;
let row = maybe_row.ok_or(DatabaseError::NotFound("profile"))?;
let profile = row.try_get("actor_profile")?;
Ok(profile)
}
pub async fn update_post_count(
db_client: &impl GenericClient,
profile_id: &Uuid,
change: i32,
) -> Result<DbActorProfile, DatabaseError> {
let maybe_row = db_client.query_opt(
"
UPDATE actor_profile
SET post_count = post_count + $1
WHERE id = $2
RETURNING actor_profile
",
&[&change, &profile_id],
).await?;
let row = maybe_row.ok_or(DatabaseError::NotFound("profile"))?;
let profile = row.try_get("actor_profile")?;
Ok(profile)
}