From 5c6dd6b24ff84caa3c30ad8a0c012a3f749aace3 Mon Sep 17 00:00:00 2001 From: Trivernis Date: Mon, 14 Oct 2019 12:29:50 +0200 Subject: [PATCH] Encapsulated group membership change - added method to change group membership to dataccess - added action to perform for group membership as enum to dataccess --- src/graphql/resolvers.ts | 79 ++++++++++-------------- src/lib/dataaccess.ts | 43 +++++++++++++ src/lib/errors/GroupNotFoundError.ts | 8 +++ src/lib/errors/NoActionSpecifiedError.ts | 11 ++++ src/lib/errors/UserNotFoundError.ts | 2 +- 5 files changed, 96 insertions(+), 47 deletions(-) create mode 100644 src/lib/errors/GroupNotFoundError.ts create mode 100644 src/lib/errors/NoActionSpecifiedError.ts diff --git a/src/graphql/resolvers.ts b/src/graphql/resolvers.ts index 7e8cbc4..c4d68ef 100644 --- a/src/graphql/resolvers.ts +++ b/src/graphql/resolvers.ts @@ -1,8 +1,7 @@ import {GraphQLError} from "graphql"; import * as status from "http-status"; import dataaccess from "../lib/dataaccess"; -import {GroupNotFoundGqlError, NotLoggedInGqlError, PostNotFoundGqlError} from "../lib/errors/graphqlErrors"; -import {UserNotFoundError} from "../lib/errors/UserNotFoundError"; +import {NotLoggedInGqlError, PostNotFoundGqlError} from "../lib/errors/graphqlErrors"; import globals from "../lib/globals"; import {InternalEvents} from "../lib/InternalEvents"; import * as models from "../lib/models"; @@ -239,15 +238,12 @@ export function resolver(req: any, res: any): any { }, async joinGroup({id}: {id: number}) { if (req.session.userId) { - const group = await models.Group.findByPk(id); - if (group) { - const user = await models.User.findByPk(req.session.userId); - await group.$add("rMembers", user); - await (await group.chat()).$add("rMembers", user); - return group; - } else { + try { + return await dataaccess + .changeGroupMembership(id, req.session.userId, dataaccess.MembershipChangeAction.ADD); + } catch (err) { res.status(status.BAD_REQUEST); - return new GroupNotFoundGqlError(id); + return err.graphqlError; } } else { res.status(status.UNAUTHORIZED); @@ -256,15 +252,12 @@ export function resolver(req: any, res: any): any { }, async leaveGroup({id}: {id: number}) { if (req.session.userId) { - const group = await models.Group.findByPk(id); - if (group) { - const user = await models.User.findByPk(req.session.userId); - await group.$remove("rMembers", user); - await (await group.chat()).$remove("rMembers", user); - return group; - } else { + try { + return await dataaccess + .changeGroupMembership(id, req.session.userId, dataaccess.MembershipChangeAction.REMOVE); + } catch (err) { res.status(status.BAD_REQUEST); - return new GroupNotFoundGqlError(id); + return err.graphqlError; } } else { res.status(status.UNAUTHORIZED); @@ -274,22 +267,18 @@ export function resolver(req: any, res: any): any { async addGroupAdmin({groupId, userId}: {groupId: number, userId: number}) { if (req.session.userId) { const group = await models.Group.findByPk(groupId); - const user = await models.User.findByPk(userId); const self = await models.User.findByPk(req.session.userId); - if (!group) { - res.status(status.BAD_REQUEST); - return new GroupNotFoundGqlError(groupId); - } - if (!user) { - res.status(status.BAD_REQUEST); - return new UserNotFoundError(userId.toString()).graphqlError; - } - if (!(await group.$has("rAdmins", self)) && (await group.creator()) !== self.id) { + if (group && !(await group.$has("rAdmins", self)) && (await group.creator()) !== self.id) { res.status(status.FORBIDDEN); return new GraphQLError("You are not a group admin!"); } - await group.$add("rAdmins", user); - return group; + try { + return await dataaccess + .changeGroupMembership(groupId, userId, dataaccess.MembershipChangeAction.OP); + } catch (err) { + res.status(status.BAD_REQUEST); + return err.graphqlError; + } } else { res.status(status.UNAUTHORIZED); @@ -299,25 +288,23 @@ export function resolver(req: any, res: any): any { async removeGroupAdmin({groupId, userId}: {groupId: number, userId: number}) { if (req.session.userId) { const group = await models.Group.findByPk(groupId); - const user = await models.User.findByPk(userId); - if (!group) { - res.status(status.BAD_REQUEST); - return new GroupNotFoundGqlError(groupId); - } - if (!user) { - res.status(status.BAD_REQUEST); - return new UserNotFoundError(userId.toString()).graphqlError; - } - if ((await group.creator()).id === userId) { + const isCreator = Number(group.creatorId) === Number(req.session.userId); + const userIsCreator = Number(group.creatorId) === Number(userId) ; + if (group && !isCreator && Number(userId) !== Number(req.session.userId)) { res.status(status.FORBIDDEN); - return new GraphQLError("You can't remove the creator of a group."); - } - if ((await group.creator()).id !== req.session.userId) { + return new GraphQLError("You are not the group creator!"); + } else if (userIsCreator) { res.status(status.FORBIDDEN); - return new GraphQLError("You are not a group admin!"); + return new GraphQLError("You are not allowed to remove a creator as an admin."); + } + + try { + return await dataaccess + .changeGroupMembership(groupId, userId, dataaccess.MembershipChangeAction.DEOP); + } catch (err) { + res.status(status.BAD_REQUEST); + return err.graphqlError; } - await group.$remove("rAdmins", user); - return group; } else { res.status(status.UNAUTHORIZED); diff --git a/src/lib/dataaccess.ts b/src/lib/dataaccess.ts index 1fbf1f0..3393697 100644 --- a/src/lib/dataaccess.ts +++ b/src/lib/dataaccess.ts @@ -1,7 +1,11 @@ import * as crypto from "crypto"; +import * as status from "http-status"; import {Sequelize} from "sequelize-typescript"; import {ChatNotFoundError} from "./errors/ChatNotFoundError"; import {EmailAlreadyRegisteredError} from "./errors/EmailAlreadyRegisteredError"; +import {GroupNotFoundGqlError, NotLoggedInGqlError} from "./errors/graphqlErrors"; +import {GroupNotFoundError} from "./errors/GroupNotFoundError"; +import {NoActionSpecifiedError} from "./errors/NoActionSpecifiedError"; import {UserNotFoundError} from "./errors/UserNotFoundError"; import globals from "./globals"; import {InternalEvents} from "./InternalEvents"; @@ -234,6 +238,38 @@ namespace dataaccess { }); } + /** + * Changes the membership of a user + * @param groupId + * @param userId + * @param action + */ + export async function changeGroupMembership(groupId: number, userId: number, action: MembershipChangeAction): + Promise { + const group = await models.Group.findByPk(groupId); + if (group) { + const user = await models.User.findByPk(userId); + if (user) { + if (action === MembershipChangeAction.ADD) { + await group.$add("rMembers", user); + } else if (action === MembershipChangeAction.REMOVE) { + await group.$remove("rMembers", user); + } else if (action === MembershipChangeAction.OP) { + await group.$add("rAdmins", user); + } else if (action === MembershipChangeAction.DEOP) { + await group.$remove("rAdmins", user); + } else { + throw new NoActionSpecifiedError(MembershipChangeAction); + } + return group; + } else { + throw new UserNotFoundError(userId); + } + } else { + throw new GroupNotFoundError(groupId); + } + } + /** * Enum representing the types of votes that can be performed on a post. */ @@ -258,6 +294,13 @@ namespace dataaccess { TOP = "TOP", NEW = "NEW", } + + export enum MembershipChangeAction { + ADD, + REMOVE, + OP, + DEOP, + } } export default dataaccess; diff --git a/src/lib/errors/GroupNotFoundError.ts b/src/lib/errors/GroupNotFoundError.ts new file mode 100644 index 0000000..99d89a5 --- /dev/null +++ b/src/lib/errors/GroupNotFoundError.ts @@ -0,0 +1,8 @@ +import {BaseError} from "./BaseError"; + +export class GroupNotFoundError extends BaseError { + constructor(groupId: number) { + super(`Group ${groupId} not found!`); + } + +} diff --git a/src/lib/errors/NoActionSpecifiedError.ts b/src/lib/errors/NoActionSpecifiedError.ts new file mode 100644 index 0000000..ee4eedb --- /dev/null +++ b/src/lib/errors/NoActionSpecifiedError.ts @@ -0,0 +1,11 @@ +import {BaseError} from "./BaseError"; + +export class NoActionSpecifiedError extends BaseError { + constructor(actions?: any) { + if (actions) { + super(`No action of '${Object.keys(actions).join(", ")}'`); + } else { + super("No action specified!"); + } + } +} diff --git a/src/lib/errors/UserNotFoundError.ts b/src/lib/errors/UserNotFoundError.ts index 7869242..f327549 100644 --- a/src/lib/errors/UserNotFoundError.ts +++ b/src/lib/errors/UserNotFoundError.ts @@ -1,7 +1,7 @@ import {BaseError} from "./BaseError"; export class UserNotFoundError extends BaseError { - constructor(username: string) { + constructor(username: (string|number)) { super(`User ${username} not found!`); } }