Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
Route to view user's orgs (#742)
Browse files Browse the repository at this point in the history
  • Loading branch information
Geometrically authored Oct 30, 2023
1 parent 6cfd463 commit 89f1ddf
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 12 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/database/models/organization_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ impl Organization {
"
SELECT o.id, o.title, o.team_id, o.description, o.icon_url, o.color
FROM organizations o
WHERE o.id = ANY($1) OR o.title = ANY($2)
WHERE o.id = ANY($1) OR LOWER(o.title) = ANY($2)
GROUP BY o.id;
",
&organization_ids_parsed,
Expand Down
27 changes: 26 additions & 1 deletion src/database/models/user_item.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::ids::{ProjectId, UserId};
use super::CollectionId;
use crate::database::models::DatabaseError;
use crate::database::models::{DatabaseError, OrganizationId};
use crate::database::redis::RedisPool;
use crate::models::ids::base62_impl::{parse_base62, to_base62};
use crate::models::users::{Badges, RecipientStatus};
Expand Down Expand Up @@ -307,6 +307,31 @@ impl User {
Ok(db_projects)
}

pub async fn get_organizations<'a, E>(
user_id: UserId,
exec: E,
) -> Result<Vec<OrganizationId>, sqlx::Error>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
{
use futures::stream::TryStreamExt;

let orgs = sqlx::query!(
"
SELECT o.id FROM organizations o
INNER JOIN team_members tm ON tm.team_id = o.team_id AND tm.accepted = TRUE
WHERE tm.user_id = $1
",
user_id as UserId,
)
.fetch_many(exec)
.try_filter_map(|e| async { Ok(e.right().map(|m| OrganizationId(m.id))) })
.try_collect::<Vec<OrganizationId>>()
.await?;

Ok(orgs)
}

pub async fn get_collections<'a, E>(
user_id: UserId,
exec: E,
Expand Down
7 changes: 3 additions & 4 deletions src/routes/v2/organizations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ pub async fn organization_create(
let mut transaction = pool.begin().await?;

// Try title
let title_organization_id_option: Option<OrganizationId> =
serde_json::from_str(&format!("\"{}\"", new_organization.title)).ok();
let title_organization_id_option: Option<u64> = parse_base62(&new_organization.title).ok();
let mut organization_strings = vec![];
if let Some(title_organization_id) = title_organization_id_option {
organization_strings.push(title_organization_id.to_string());
Expand All @@ -93,7 +92,7 @@ pub async fn organization_create(
let team = team_item::TeamBuilder {
members: vec![team_item::TeamMemberBuilder {
user_id: current_user.id.into(),
role: crate::models::teams::OWNER_ROLE.to_owned(),
role: models::teams::OWNER_ROLE.to_owned(),
permissions: ProjectPermissions::all(),
organization_permissions: Some(OrganizationPermissions::all()),
accepted: true,
Expand Down Expand Up @@ -218,7 +217,7 @@ pub async fn organizations_get(
.collect::<Vec<_>>();

let teams_data = TeamMember::get_from_team_full_many(&team_ids, &**pool, &redis).await?;
let users = crate::database::models::User::get_many_ids(
let users = database::models::User::get_many_ids(
&teams_data.iter().map(|x| x.user_id).collect::<Vec<_>>(),
&**pool,
&redis,
Expand Down
7 changes: 3 additions & 4 deletions src/routes/v2/project_creation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::database::models::{self, image_item, User};
use crate::database::redis::RedisPool;
use crate::file_hosting::{FileHost, FileHostingError};
use crate::models::error::ApiError;
use crate::models::ids::base62_impl::parse_base62;
use crate::models::ids::ImageId;
use crate::models::images::{Image, ImageContext};
use crate::models::pats::Scopes;
Expand Down Expand Up @@ -417,16 +418,14 @@ async fn project_create_inner(
.validate()
.map_err(|err| CreateError::InvalidInput(validation_errors_to_string(err, None)))?;

let slug_project_id_option: Option<ProjectId> =
serde_json::from_str(&format!("\"{}\"", create_data.slug)).ok();
let slug_project_id_option: Option<u64> = parse_base62(&create_data.slug).ok();

if let Some(slug_project_id) = slug_project_id_option {
let slug_project_id: models::ids::ProjectId = slug_project_id.into();
let results = sqlx::query!(
"
SELECT EXISTS(SELECT 1 FROM mods WHERE id=$1)
",
slug_project_id as models::ids::ProjectId
slug_project_id as i64
)
.fetch_one(&mut **transaction)
.await
Expand Down
85 changes: 85 additions & 0 deletions src/routes/v2/users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use serde_json::json;
use sqlx::PgPool;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;
use validator::Validate;
Expand All @@ -32,6 +33,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
cfg.service(
web::scope("user")
.service(user_get)
.service(orgs_list)
.service(projects_list)
.service(collections_list)
.service(user_delete)
Expand Down Expand Up @@ -196,6 +198,89 @@ pub async fn collections_list(
}
}

#[get("{user_id}/organizatons")]
pub async fn orgs_list(
req: HttpRequest,
info: web::Path<(String,)>,
pool: web::Data<PgPool>,
redis: web::Data<RedisPool>,
session_queue: web::Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
let user = get_user_from_headers(
&req,
&**pool,
&redis,
&session_queue,
Some(&[Scopes::PROJECT_READ]),
)
.await
.map(|x| x.1)
.ok();

let id_option = User::get(&info.into_inner().0, &**pool, &redis).await?;

if let Some(id) = id_option.map(|x| x.id) {
let org_data = User::get_organizations(id, &**pool).await?;

let organizations_data =
crate::database::models::organization_item::Organization::get_many_ids(
&org_data, &**pool, &redis,
)
.await?;

let team_ids = organizations_data
.iter()
.map(|x| x.team_id)
.collect::<Vec<_>>();

let teams_data = crate::database::models::TeamMember::get_from_team_full_many(
&team_ids, &**pool, &redis,
)
.await?;
let users = User::get_many_ids(
&teams_data.iter().map(|x| x.user_id).collect::<Vec<_>>(),
&**pool,
&redis,
)
.await?;

let mut organizations = vec![];
let mut team_groups = HashMap::new();
for item in teams_data {
team_groups.entry(item.team_id).or_insert(vec![]).push(item);
}

for data in organizations_data {
let members_data = team_groups.remove(&data.team_id).unwrap_or(vec![]);
let logged_in = user
.as_ref()
.and_then(|user| {
members_data
.iter()
.find(|x| x.user_id == user.id.into() && x.accepted)
})
.is_some();

let team_members: Vec<_> = members_data
.into_iter()
.filter(|x| logged_in || x.accepted || id == x.user_id)
.flat_map(|data| {
users.iter().find(|x| x.id == data.user_id).map(|user| {
crate::models::teams::TeamMember::from(data, user.clone(), !logged_in)
})
})
.collect();

let organization = crate::models::organizations::Organization::from(data, team_members);
organizations.push(organization);
}

Ok(HttpResponse::Ok().json(organizations))
} else {
Ok(HttpResponse::NotFound().body(""))
}
}

lazy_static! {
static ref RE_URL_SAFE: Regex = Regex::new(r"^[a-zA-Z0-9_-]*$").unwrap();
}
Expand Down

0 comments on commit 89f1ddf

Please sign in to comment.