use anyhow::{Context, Result};
use uuid::Uuid;
pub async fn add_user<'a, E>(executor: E, project_id: Uuid, user_id: Uuid) -> Result<()>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{
sqlx::query!(
r#"
INSERT INTO project_app_users (project_id, user_id)
VALUES ($1, $2)
ON CONFLICT (project_id, user_id) DO NOTHING
"#,
project_id,
user_id
)
.execute(executor)
.await
.context("Failed to add app user to project")?;
Ok(())
}
pub async fn remove_user<'a, E>(executor: E, project_id: Uuid, user_id: Uuid) -> Result<()>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{
sqlx::query!(
r#"
DELETE FROM project_app_users
WHERE project_id = $1 AND user_id = $2
"#,
project_id,
user_id
)
.execute(executor)
.await
.context("Failed to remove app user from project")?;
Ok(())
}
pub async fn add_team<'a, E>(executor: E, project_id: Uuid, team_id: Uuid) -> Result<()>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{
sqlx::query!(
r#"
INSERT INTO project_app_teams (project_id, team_id)
VALUES ($1, $2)
ON CONFLICT (project_id, team_id) DO NOTHING
"#,
project_id,
team_id
)
.execute(executor)
.await
.context("Failed to add app team to project")?;
Ok(())
}
pub async fn remove_team<'a, E>(executor: E, project_id: Uuid, team_id: Uuid) -> Result<()>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{
sqlx::query!(
r#"
DELETE FROM project_app_teams
WHERE project_id = $1 AND team_id = $2
"#,
project_id,
team_id
)
.execute(executor)
.await
.context("Failed to remove app team from project")?;
Ok(())
}
pub async fn list_users<'a, E>(executor: E, project_id: Uuid) -> Result<Vec<Uuid>>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{
let records = sqlx::query!(
r#"
SELECT user_id
FROM project_app_users
WHERE project_id = $1
ORDER BY created_at ASC
"#,
project_id
)
.fetch_all(executor)
.await
.context("Failed to list app users")?;
Ok(records.into_iter().map(|r| r.user_id).collect())
}
pub async fn list_teams<'a, E>(executor: E, project_id: Uuid) -> Result<Vec<Uuid>>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{
let records = sqlx::query!(
r#"
SELECT team_id
FROM project_app_teams
WHERE project_id = $1
ORDER BY created_at ASC
"#,
project_id
)
.fetch_all(executor)
.await
.context("Failed to list app teams")?;
Ok(records.into_iter().map(|r| r.team_id).collect())
}
pub async fn user_can_access_app(
pool: &sqlx::PgPool,
project_id: Uuid,
user_id: Uuid,
) -> Result<bool> {
let result = sqlx::query!(
r#"
SELECT EXISTS(
-- Direct app user
SELECT 1 FROM project_app_users pau
WHERE pau.project_id = $1 AND pau.user_id = $2
UNION
-- Team member of an app team
SELECT 1 FROM project_app_teams pat
INNER JOIN team_members tm ON tm.team_id = pat.team_id
WHERE pat.project_id = $1 AND tm.user_id = $2
) as "exists!"
"#,
project_id,
user_id
)
.fetch_one(pool)
.await
.context("Failed to check app user access")?;
Ok(result.exists)
}