diff --git a/sqlx-data.json b/sqlx-data.json index 43739169b..6ab81bbb9 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -2189,6 +2189,57 @@ ] } }, + "64570e9cadd7391ad45a1029a0e5212e17720c4f65682384d7493fc350114228": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int8" + }, + { + "name": "team_id", + "ordinal": 1, + "type_info": "Int8" + }, + { + "name": "user_id", + "ordinal": 2, + "type_info": "Int8" + }, + { + "name": "role", + "ordinal": 3, + "type_info": "Varchar" + }, + { + "name": "permissions", + "ordinal": 4, + "type_info": "Int8" + }, + { + "name": "accepted", + "ordinal": 5, + "type_info": "Bool" + } + ], + "nullable": [ + false, + false, + false, + false, + false, + false + ], + "parameters": { + "Left": [ + "Int8Array", + "Int8" + ] + } + }, + "query": "\n SELECT id, team_id, user_id, role, permissions, accepted\n FROM team_members\n WHERE (team_id = ANY($1) AND user_id = $2 AND accepted = TRUE)\n " + }, "67d021f0776276081d3c50ca97afa6b78b98860bf929009e845e9c00a192e3b5": { "query": "\n SELECT id FROM report_types\n WHERE name = $1\n ", "describe": { @@ -5383,6 +5434,104 @@ "nullable": [] } }, + "d553bb44ac600047656962bd1c44378fb32e74f0e7f07d1c26336dc80dffe78a": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int8" + }, + { + "name": "team_id", + "ordinal": 1, + "type_info": "Int8" + }, + { + "name": "member_role", + "ordinal": 2, + "type_info": "Varchar" + }, + { + "name": "permissions", + "ordinal": 3, + "type_info": "Int8" + }, + { + "name": "accepted", + "ordinal": 4, + "type_info": "Bool" + }, + { + "name": "user_id", + "ordinal": 5, + "type_info": "Int8" + }, + { + "name": "github_id", + "ordinal": 6, + "type_info": "Int8" + }, + { + "name": "user_name", + "ordinal": 7, + "type_info": "Varchar" + }, + { + "name": "email", + "ordinal": 8, + "type_info": "Varchar" + }, + { + "name": "avatar_url", + "ordinal": 9, + "type_info": "Varchar" + }, + { + "name": "username", + "ordinal": 10, + "type_info": "Varchar" + }, + { + "name": "bio", + "ordinal": 11, + "type_info": "Varchar" + }, + { + "name": "created", + "ordinal": 12, + "type_info": "Timestamptz" + }, + { + "name": "user_role", + "ordinal": 13, + "type_info": "Varchar" + } + ], + "nullable": [ + false, + false, + false, + false, + false, + false, + true, + true, + true, + true, + false, + true, + false, + false + ], + "parameters": { + "Left": [ + "Int8Array" + ] + } + }, + "query": "\n SELECT tm.id id, tm.team_id team_id, tm.role member_role, tm.permissions permissions, tm.accepted accepted,\n u.id user_id, u.github_id github_id, u.name user_name, u.email email,\n u.avatar_url avatar_url, u.username username, u.bio bio,\n u.created created, u.role user_role\n FROM team_members tm\n INNER JOIN users u ON u.id = tm.user_id\n WHERE tm.team_id = ANY($1)\n ORDER BY tm.team_id\n " + }, "d5a496a0e17c5784f98ca2067bff996b23bb0a798609c4d4928df8080e4e1758": { "query": "\n SELECT v.id, v.mod_id, v.author_id, v.name, v.version_number,\n v.changelog, v.changelog_url, v.date_published, v.downloads,\n v.version_type, v.featured\n FROM versions v\n WHERE v.id = ANY($1)\n ORDER BY v.date_published ASC\n ", "describe": { diff --git a/src/database/models/team_item.rs b/src/database/models/team_item.rs index d9c72cea2..9e4661c98 100644 --- a/src/database/models/team_item.rs +++ b/src/database/models/team_item.rs @@ -202,6 +202,71 @@ impl TeamMember { Ok(team_members) } + pub async fn get_from_team_full_many<'a, E>( + team_ids: Vec, + exec: E, + ) -> Result, super::DatabaseError> + where + E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy, + { + use futures::stream::TryStreamExt; + + let team_ids_parsed: Vec = + team_ids.into_iter().map(|x| x.0).collect(); + + let teams = sqlx::query!( + " + SELECT tm.id id, tm.team_id team_id, tm.role member_role, tm.permissions permissions, tm.accepted accepted, + u.id user_id, u.github_id github_id, u.name user_name, u.email email, + u.avatar_url avatar_url, u.username username, u.bio bio, + u.created created, u.role user_role + FROM team_members tm + INNER JOIN users u ON u.id = tm.user_id + WHERE tm.team_id = ANY($1) + ORDER BY tm.team_id + ", + &team_ids_parsed + ) + .fetch_many(exec) + .try_filter_map(|e| async { + if let Some(m) = e.right() { + let permissions = Permissions::from_bits(m.permissions as u64); + if let Some(perms) = permissions { + Ok(Some(Ok(QueryTeamMember { + id: TeamMemberId(m.id), + team_id: TeamId(m.team_id), + role: m.member_role, + permissions: perms, + accepted: m.accepted, + user: User { + id: UserId(m.user_id), + github_id: m.github_id, + name: m.user_name, + email: m.email, + avatar_url: m.avatar_url, + username: m.username, + bio: m.bio, + created: m.created, + role: m.user_role, + }, + }))) + } else { + Ok(Some(Err(super::DatabaseError::Bitflag))) + } + } else { + Ok(None) + } + }) + .try_collect::>>() + .await?; + + let team_members = teams + .into_iter() + .collect::, super::DatabaseError>>()?; + + Ok(team_members) + } + /// Lists the team members for a user. Does not list pending requests. pub async fn get_from_user_public<'a, 'b, E>( id: UserId, @@ -334,6 +399,59 @@ impl TeamMember { } } + /// Gets team members from user ids and team ids. Does not return pending members. + pub async fn get_from_user_id_many<'a, 'b, E>( + team_ids: Vec, + user_id: UserId, + executor: E, + ) -> Result, super::DatabaseError> + where + E: sqlx::Executor<'a, Database = sqlx::Postgres>, + { + use futures::stream::TryStreamExt; + + let team_ids_parsed: Vec = + team_ids.into_iter().map(|x| x.0).collect(); + + let team_members = sqlx::query!( + " + SELECT id, team_id, user_id, role, permissions, accepted + FROM team_members + WHERE (team_id = ANY($1) AND user_id = $2 AND accepted = TRUE) + ", + &team_ids_parsed, + user_id as UserId + ) + .fetch_many(executor) + .try_filter_map(|e| async { + if let Some(m) = e.right() { + let permissions = Permissions::from_bits(m.permissions as u64); + if let Some(perms) = permissions { + Ok(Some(Ok(TeamMember { + id: TeamMemberId(m.id), + team_id: TeamId(m.team_id), + user_id, + role: m.role, + permissions: perms, + accepted: m.accepted, + }))) + } else { + Ok(Some(Err(super::DatabaseError::Bitflag))) + } + } else { + Ok(None) + } + }) + .try_collect::>>() + .await?; + + let team_members = team_members + .into_iter() + .collect::, super::DatabaseError>>()?; + + Ok(team_members) + } + /// Gets a team member from a user id and team id, including pending members. pub async fn get_from_user_id_pending<'a, 'b, E>( id: TeamId, diff --git a/src/routes/mod.rs b/src/routes/mod.rs index df7075627..ccc495ba9 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -126,6 +126,8 @@ pub fn users_config(cfg: &mut web::ServiceConfig) { } pub fn teams_config(cfg: &mut web::ServiceConfig) { + cfg.service(teams::teams_get); + cfg.service( web::scope("team") .service(teams::team_members_get) diff --git a/src/routes/teams.rs b/src/routes/teams.rs index bf5fccd4f..5ba7bcb10 100644 --- a/src/routes/teams.rs +++ b/src/routes/teams.rs @@ -101,6 +101,63 @@ pub async fn team_members_get( Ok(HttpResponse::Ok().json(team_members)) } +#[derive(Serialize, Deserialize)] +pub struct TeamIds { + pub ids: String, +} + +#[get("teams")] +pub async fn teams_get( + req: HttpRequest, + web::Query(ids): web::Query, + pool: web::Data, +) -> Result { + use itertools::Itertools; + + let team_ids = serde_json::from_str::>(&*ids.ids)? + .into_iter() + .map(|x| x.into()) + .collect::>(); + + let teams_data = + TeamMember::get_from_team_full_many(team_ids.clone(), &**pool).await?; + + let current_user = get_user_from_headers(req.headers(), &**pool).await.ok(); + let accepted = if let Some(user) = current_user { + TeamMember::get_from_user_id_many(team_ids, user.id.into(), &**pool) + .await? + .into_iter() + .map(|m| m.team_id.0) + .collect() + } else { + std::collections::HashSet::new() + }; + + let teams_groups = teams_data.into_iter().group_by(|data| data.team_id.0); + + let mut teams: Vec> = vec![]; + + for (id, member_data) in &teams_groups { + if accepted.contains(&id) { + let team_members = member_data.map(|data| { + crate::models::teams::TeamMember::from(data, false) + }); + + teams.push(team_members.collect()); + + continue; + } + + let team_members = member_data + .filter(|x| x.accepted) + .map(|data| crate::models::teams::TeamMember::from(data, true)); + + teams.push(team_members.collect()); + } + + Ok(HttpResponse::Ok().json(teams)) +} + #[post("{id}/join")] pub async fn join_team( req: HttpRequest,