diff --git a/schema.graphql b/schema.graphql index 2a0c2b2..9060d1d 100644 --- a/schema.graphql +++ b/schema.graphql @@ -6,6 +6,8 @@ input EqualityConstraint { type Mutation { createUser(user: CreateUserInput!): User! updateUser(user: UpdateUserInput!): Success! + addUserToGroup(userId: String!, groupId: Int!): Success! + removeUserFromGroup(userId: String!, groupId: Int!): Success! } type Group { diff --git a/server/src/domain/handler.rs b/server/src/domain/handler.rs index 904de24..19ccb09 100644 --- a/server/src/domain/handler.rs +++ b/server/src/domain/handler.rs @@ -87,6 +87,7 @@ pub trait BackendHandler: Clone + Send { async fn delete_user(&self, user_id: &str) -> Result<()>; async fn create_group(&self, group_name: &str) -> Result; async fn add_user_to_group(&self, user_id: &str, group_id: GroupId) -> Result<()>; + async fn remove_user_from_group(&self, user_id: &str, group_id: GroupId) -> Result<()>; async fn get_user_groups(&self, user: &str) -> Result>; } @@ -107,6 +108,7 @@ mockall::mock! { async fn create_group(&self, group_name: &str) -> Result; async fn get_user_groups(&self, user: &str) -> Result>; async fn add_user_to_group(&self, user_id: &str, group_id: GroupId) -> Result<()>; + async fn remove_user_from_group(&self, user_id: &str, group_id: GroupId) -> Result<()>; } #[async_trait] impl LoginHandler for TestBackendHandler { diff --git a/server/src/domain/sql_backend_handler.rs b/server/src/domain/sql_backend_handler.rs index 797a366..49afa09 100644 --- a/server/src/domain/sql_backend_handler.rs +++ b/server/src/domain/sql_backend_handler.rs @@ -247,7 +247,17 @@ impl BackendHandler for SqlBackendHandler { let query = Query::insert() .into_table(Memberships::Table) .columns(vec![Memberships::UserId, Memberships::GroupId]) - .values_panic(vec![user_id.into(), group_id.0.into()]) + .values_panic(vec![user_id.into(), group_id.into()]) + .to_string(DbQueryBuilder {}); + sqlx::query(&query).execute(&self.sql_pool).await?; + Ok(()) + } + + async fn remove_user_from_group(&self, user_id: &str, group_id: GroupId) -> Result<()> { + let query = Query::delete() + .from_table(Memberships::Table) + .and_where(Expr::col(Memberships::GroupId).eq(group_id)) + .and_where(Expr::col(Memberships::UserId).eq(user_id)) .to_string(DbQueryBuilder {}); sqlx::query(&query).execute(&self.sql_pool).await?; Ok(()) diff --git a/server/src/domain/sql_tables.rs b/server/src/domain/sql_tables.rs index 57d05d3..35f349d 100644 --- a/server/src/domain/sql_tables.rs +++ b/server/src/domain/sql_tables.rs @@ -1,3 +1,4 @@ +use super::handler::GroupId; use sea_query::*; pub type Pool = sqlx::sqlite::SqlitePool; @@ -5,6 +6,12 @@ pub type PoolOptions = sqlx::sqlite::SqlitePoolOptions; pub type DbRow = sqlx::sqlite::SqliteRow; pub type DbQueryBuilder = SqliteQueryBuilder; +impl From for Value { + fn from(group_id: GroupId) -> Self { + group_id.0.into() + } +} + #[derive(Iden)] pub enum Users { Table, diff --git a/server/src/infra/graphql/mutation.rs b/server/src/infra/graphql/mutation.rs index 67801d7..1ec4d29 100644 --- a/server/src/infra/graphql/mutation.rs +++ b/server/src/infra/graphql/mutation.rs @@ -1,4 +1,4 @@ -use crate::domain::handler::{BackendHandler, CreateUserRequest, UpdateUserRequest}; +use crate::domain::handler::{BackendHandler, CreateUserRequest, GroupId, UpdateUserRequest}; use juniper::{graphql_object, FieldResult, GraphQLInputObject, GraphQLObject}; use super::api::Context; @@ -93,4 +93,34 @@ impl Mutation { .await?; Ok(Success::new()) } + + async fn add_user_to_group( + context: &Context, + user_id: String, + group_id: i32, + ) -> FieldResult { + if !context.validation_result.is_admin { + return Err("Unauthorized group membership modification".into()); + } + context + .handler + .add_user_to_group(&user_id, GroupId(group_id)) + .await?; + Ok(Success::new()) + } + + async fn remove_user_from_group( + context: &Context, + user_id: String, + group_id: i32, + ) -> FieldResult { + if !context.validation_result.is_admin { + return Err("Unauthorized group membership modification".into()); + } + context + .handler + .remove_user_from_group(&user_id, GroupId(group_id)) + .await?; + Ok(Success::new()) + } } diff --git a/server/src/infra/tcp_backend_handler.rs b/server/src/infra/tcp_backend_handler.rs index 08935ee..03f019e 100644 --- a/server/src/infra/tcp_backend_handler.rs +++ b/server/src/infra/tcp_backend_handler.rs @@ -35,6 +35,7 @@ mockall::mock! { async fn delete_user(&self, user_id: &str) -> DomainResult<()>; async fn create_group(&self, group_name: &str) -> DomainResult; async fn add_user_to_group(&self, user_id: &str, group_id: GroupId) -> DomainResult<()>; + async fn remove_user_from_group(&self, user_id: &str, group_id: GroupId) -> DomainResult<()>; } #[async_trait] impl TcpBackendHandler for TestTcpBackendHandler {