diff --git a/backend/src/db/graphql/groups.js b/backend/src/db/graphql/groups.js index 640a70009..4350e19c9 100644 --- a/backend/src/db/graphql/groups.js +++ b/backend/src/db/graphql/groups.js @@ -106,6 +106,17 @@ export const joinGroupMutation = gql` } ` +export const leaveGroupMutation = gql` + mutation ($groupId: ID!, $userId: ID!) { + LeaveGroup(groupId: $groupId, userId: $userId) { + id + name + slug + myRoleInGroup + } + } +` + export const changeGroupMemberRoleMutation = gql` mutation ($groupId: ID!, $userId: ID!, $roleInGroup: GroupMemberRole!) { ChangeGroupMemberRole(groupId: $groupId, userId: $userId, roleInGroup: $roleInGroup) { diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index e588e1b62..8f3d6947a 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -191,6 +191,36 @@ const isAllowedToJoinGroup = rule({ } }) +const isAllowedToLeaveGroup = rule({ + cache: 'no_cache', +})(async (_parent, args, { user, driver }) => { + if (!(user && user.id)) return false + const { groupId, userId } = args + if (user.id !== userId) return false + const session = driver.session() + const readTxPromise = session.readTransaction(async (transaction) => { + const transactionResponse = await transaction.run( + ` + MATCH (member:User {id: $userId})-[membership:MEMBER_OF]->(group:Group {id: $groupId}) + RETURN group {.*}, member {.*, myRoleInGroup: membership.role} + `, + { groupId, userId }, + ) + return { + group: transactionResponse.records.map((record) => record.get('group'))[0], + member: transactionResponse.records.map((record) => record.get('member'))[0], + } + }) + try { + const { group, member } = await readTxPromise + return !!group && !!member && !!member.myRoleInGroup && member.myRoleInGroup !== 'owner' + } catch (error) { + throw new Error(error) + } finally { + session.close() + } +}) + const isAuthor = rule({ cache: 'no_cache', })(async (_parent, args, { user, driver }) => { @@ -284,6 +314,7 @@ export default shield( CreateGroup: isAuthenticated, UpdateGroup: isAllowedToChangeGroupSettings, JoinGroup: isAllowedToJoinGroup, + LeaveGroup: isAllowedToLeaveGroup, ChangeGroupMemberRole: isAllowedToChangeGroupMemberRole, CreatePost: isAuthenticated, UpdatePost: isAuthor, diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js index 73ce56c0c..6e942d60a 100644 --- a/backend/src/schema/resolvers/groups.js +++ b/backend/src/schema/resolvers/groups.js @@ -251,6 +251,27 @@ export default { session.close() } }, + LeaveGroup: async (_parent, params, context, _resolveInfo) => { + const { groupId, userId } = params + const session = context.driver.session() + const writeTxResultPromise = session.writeTransaction(async (transaction) => { + const leaveGroupCypher = ` + MATCH (member:User {id: $userId})-[membership:MEMBER_OF]->(group:Group {id: $groupId}) + DELETE membership + RETURN member {.*, myRoleInGroup: NULL} + ` + const transactionResponse = await transaction.run(leaveGroupCypher, { groupId, userId }) + const [member] = await transactionResponse.records.map((record) => record.get('member')) + return member + }) + try { + return await writeTxResultPromise + } catch (error) { + throw new Error(error) + } finally { + session.close() + } + }, ChangeGroupMemberRole: async (_parent, params, context, _resolveInfo) => { const { groupId, userId, roleInGroup } = params const session = context.driver.session() diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql index 8c881ef1d..d586f6b53 100644 --- a/backend/src/schema/types/type/Group.gql +++ b/backend/src/schema/types/type/Group.gql @@ -117,6 +117,11 @@ type Mutation { userId: ID! ): User + LeaveGroup( + groupId: ID! + userId: ID! + ): User + ChangeGroupMemberRole( groupId: ID! userId: ID! diff --git a/webapp/graphql/groups.js b/webapp/graphql/groups.js index 640a70009..4350e19c9 100644 --- a/webapp/graphql/groups.js +++ b/webapp/graphql/groups.js @@ -106,6 +106,17 @@ export const joinGroupMutation = gql` } ` +export const leaveGroupMutation = gql` + mutation ($groupId: ID!, $userId: ID!) { + LeaveGroup(groupId: $groupId, userId: $userId) { + id + name + slug + myRoleInGroup + } + } +` + export const changeGroupMemberRoleMutation = gql` mutation ($groupId: ID!, $userId: ID!, $roleInGroup: GroupMemberRole!) { ChangeGroupMemberRole(groupId: $groupId, userId: $userId, roleInGroup: $roleInGroup) {