Merge branch 'julius-dev' of Software_Engineering_I/greenvironment-server into develop

pull/2/head
Trivernis 5 years ago committed by Gitea
commit 1300d8ed05

@ -1,12 +1,11 @@
import {AggregateError} from "bluebird";
import {GraphQLError} from "graphql"; import {GraphQLError} from "graphql";
import * as status from "http-status"; import * as status from "http-status";
import dataaccess from "../lib/dataaccess"; import dataaccess from "../lib/dataaccess";
import {UserNotFoundError} from "../lib/errors/UserNotFoundError"; import {NotLoggedInGqlError, PostNotFoundGqlError} from "../lib/errors/graphqlErrors";
import {Group} from "../lib/models";
import * as models from "../lib/models";
import {GroupNotFoundGqlError, NotLoggedInGqlError, PostNotFoundGqlError} from "../lib/errors/graphqlErrors";
import globals from "../lib/globals"; import globals from "../lib/globals";
import {InternalEvents} from "../lib/InternalEvents"; import {InternalEvents} from "../lib/InternalEvents";
import * as models from "../lib/models";
import {is} from "../lib/regex"; import {is} from "../lib/regex";
/** /**
@ -228,6 +227,15 @@ export function resolver(req: any, res: any): any {
return new GraphQLError("No sender or type given."); return new GraphQLError("No sender or type given.");
} }
}, },
async removeFriend({friendId}: {friendId: number}) {
if (req.session.userId) {
const self = await models.User.findByPk(req.session.userId);
return await self.removeFriend(friendId);
} else {
res.status(status.UNAUTHORIZED);
return new NotLoggedInGqlError();
}
},
async getPosts({first, offset, sort}: {first: number, offset: number, sort: dataaccess.SortType}) { async getPosts({first, offset, sort}: {first: number, offset: number, sort: dataaccess.SortType}) {
return await dataaccess.getPosts(first, offset, sort); return await dataaccess.getPosts(first, offset, sort);
}, },
@ -240,15 +248,12 @@ export function resolver(req: any, res: any): any {
}, },
async joinGroup({id}: {id: number}) { async joinGroup({id}: {id: number}) {
if (req.session.userId) { if (req.session.userId) {
const group = await models.Group.findByPk(id); try {
if (group) { return await dataaccess
const user = await models.User.findByPk(req.session.userId); .changeGroupMembership(id, req.session.userId, dataaccess.MembershipChangeAction.ADD);
await group.$add("rMembers", user); } catch (err) {
await (await group.chat()).$add("rMembers", user);
return group;
} else {
res.status(status.BAD_REQUEST); res.status(status.BAD_REQUEST);
return new GroupNotFoundGqlError(id); return err.graphqlError;
} }
} else { } else {
res.status(status.UNAUTHORIZED); res.status(status.UNAUTHORIZED);
@ -257,15 +262,12 @@ export function resolver(req: any, res: any): any {
}, },
async leaveGroup({id}: {id: number}) { async leaveGroup({id}: {id: number}) {
if (req.session.userId) { if (req.session.userId) {
const group = await models.Group.findByPk(id); try {
if (group) { return await dataaccess
const user = await models.User.findByPk(req.session.userId); .changeGroupMembership(id, req.session.userId, dataaccess.MembershipChangeAction.REMOVE);
await group.$remove("rMembers", user); } catch (err) {
await (await group.chat()).$remove("rMembers", user);
return group;
} else {
res.status(status.BAD_REQUEST); res.status(status.BAD_REQUEST);
return new GroupNotFoundGqlError(id); return err.graphqlError;
} }
} else { } else {
res.status(status.UNAUTHORIZED); res.status(status.UNAUTHORIZED);
@ -275,22 +277,18 @@ export function resolver(req: any, res: any): any {
async addGroupAdmin({groupId, userId}: {groupId: number, userId: number}) { async addGroupAdmin({groupId, userId}: {groupId: number, userId: number}) {
if (req.session.userId) { if (req.session.userId) {
const group = await models.Group.findByPk(groupId); const group = await models.Group.findByPk(groupId);
const user = await models.User.findByPk(userId);
const self = await models.User.findByPk(req.session.userId); const self = await models.User.findByPk(req.session.userId);
if (!group) { if (group && !(await group.$has("rAdmins", self)) && (await group.creator()) !== self.id) {
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) {
res.status(status.FORBIDDEN); res.status(status.FORBIDDEN);
return new GraphQLError("You are not a group admin!"); return new GraphQLError("You are not a group admin!");
} }
await group.$add("rAdmins", user); try {
return group; return await dataaccess
.changeGroupMembership(groupId, userId, dataaccess.MembershipChangeAction.OP);
} catch (err) {
res.status(status.BAD_REQUEST);
return err.graphqlError;
}
} else { } else {
res.status(status.UNAUTHORIZED); res.status(status.UNAUTHORIZED);
@ -300,25 +298,23 @@ export function resolver(req: any, res: any): any {
async removeGroupAdmin({groupId, userId}: {groupId: number, userId: number}) { async removeGroupAdmin({groupId, userId}: {groupId: number, userId: number}) {
if (req.session.userId) { if (req.session.userId) {
const group = await models.Group.findByPk(groupId); const group = await models.Group.findByPk(groupId);
const user = await models.User.findByPk(userId); const isCreator = Number(group.creatorId) === Number(req.session.userId);
if (!group) { const userIsCreator = Number(group.creatorId) === Number(userId) ;
res.status(status.BAD_REQUEST); if (group && !isCreator && Number(userId) !== Number(req.session.userId)) {
return new GroupNotFoundGqlError(groupId);
}
if (!user) {
res.status(status.BAD_REQUEST);
return new UserNotFoundError(userId.toString()).graphqlError;
}
if ((await group.creator()).id === userId) {
res.status(status.FORBIDDEN); res.status(status.FORBIDDEN);
return new GraphQLError("You can't remove the creator of a group."); return new GraphQLError("You are not the group creator!");
} } else if (userIsCreator) {
if ((await group.creator()).id !== req.session.userId) {
res.status(status.FORBIDDEN); 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 { } else {
res.status(status.UNAUTHORIZED); res.status(status.UNAUTHORIZED);

@ -49,6 +49,9 @@ type Mutation {
"lets you deny a request for a given request id" "lets you deny a request for a given request id"
denyRequest(requestId: ID!): Boolean denyRequest(requestId: ID!): Boolean
"removes a friend"
removeFriend(friendId: ID!): Boolean
"send a message in a Chatroom" "send a message in a Chatroom"
sendMessage(chatId: ID!, content: String!): ChatMessage sendMessage(chatId: ID!, content: String!): ChatMessage
@ -101,6 +104,12 @@ interface UserData {
"all friends of the user" "all friends of the user"
friends: [User] friends: [User]
"the points of the user"
points: Int
"the levels of the user depending on the points"
level: Int
} }
"represents a single user account" "represents a single user account"
@ -128,6 +137,15 @@ type User implements UserData{
"all friends of the user" "all friends of the user"
friends: [User] friends: [User]
"the points of the user"
points: Int
"the groups the user has joined"
groups: [Group]
"the levels of the user depending on the points"
level: Int
} }
type Profile implements UserData { type Profile implements UserData {
@ -173,6 +191,11 @@ type Profile implements UserData {
"all groups the user has joined" "all groups the user has joined"
groups: [Group] groups: [Group]
"the points of the user"
points: Int
"the levels of the user depending on the points"
level: Int
} }
"represents a single user post" "represents a single user post"

@ -1,7 +1,11 @@
import * as crypto from "crypto"; import * as crypto from "crypto";
import * as status from "http-status";
import {Sequelize} from "sequelize-typescript"; import {Sequelize} from "sequelize-typescript";
import {ChatNotFoundError} from "./errors/ChatNotFoundError"; import {ChatNotFoundError} from "./errors/ChatNotFoundError";
import {EmailAlreadyRegisteredError} from "./errors/EmailAlreadyRegisteredError"; 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 {UserNotFoundError} from "./errors/UserNotFoundError";
import globals from "./globals"; import globals from "./globals";
import {InternalEvents} from "./InternalEvents"; import {InternalEvents} from "./InternalEvents";
@ -40,6 +44,8 @@ namespace dataaccess {
models.Group, models.Group,
models.GroupAdmin, models.GroupAdmin,
models.GroupMember, models.GroupMember,
models.EventParticipant,
models.Event,
]); ]);
} catch (err) { } catch (err) {
globals.logger.error(err.message); globals.logger.error(err.message);
@ -207,7 +213,7 @@ namespace dataaccess {
requestType = requestType || RequestType.FRIENDREQUEST; requestType = requestType || RequestType.FRIENDREQUEST;
const request = await models.Request.create({senderId: sender, receiverId: receiver, requestType}); const request = await models.Request.create({senderId: sender, receiverId: receiver, requestType});
globals.internalEmitter.emit(InternalEvents.REQUESTCREATE, Request); globals.internalEmitter.emit(InternalEvents.REQUESTCREATE, request);
return request; return request;
} }
@ -232,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<models.Group> {
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. * Enum representing the types of votes that can be performed on a post.
*/ */
@ -256,6 +294,13 @@ namespace dataaccess {
TOP = "TOP", TOP = "TOP",
NEW = "NEW", NEW = "NEW",
} }
export enum MembershipChangeAction {
ADD,
REMOVE,
OP,
DEOP,
}
} }
export default dataaccess; export default dataaccess;

@ -0,0 +1,8 @@
import {BaseError} from "./BaseError";
export class GroupNotFoundError extends BaseError {
constructor(groupId: number) {
super(`Group ${groupId} not found!`);
}
}

@ -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!");
}
}
}

@ -1,7 +1,7 @@
import {BaseError} from "./BaseError"; import {BaseError} from "./BaseError";
export class UserNotFoundError extends BaseError { export class UserNotFoundError extends BaseError {
constructor(username: string) { constructor(username: (string|number)) {
super(`User ${username} not found!`); super(`User ${username} not found!`);
} }
} }

@ -0,0 +1,34 @@
import {BelongsTo, BelongsToMany, Column, ForeignKey, Model, NotNull, Table} from "sequelize-typescript";
import {EventParticipant} from "./EventParticipant";
import {Group} from "./Group";
import {User} from "./User";
@Table({underscored: true})
export class Event extends Model<Event> {
@NotNull
@Column({allowNull: false})
public name: string;
@NotNull
@Column({allowNull: false})
public dueDate: Date;
@NotNull
@ForeignKey(() => Group)
@Column({allowNull: false})
public groupId: number;
@BelongsTo(() => Group, "groupId")
public rGroup: Group;
@BelongsToMany(() => User, () => EventParticipant)
public rParticipants: User[];
public async group(): Promise<Group> {
return await this.$get("rGroup") as Group;
}
public async participants({first, offset}: {first: number, offset: number}): Promise<User[]> {
return await this.$get("rParticipants") as User[];
}
}

@ -0,0 +1,16 @@
import {BelongsTo, BelongsToMany, Column, ForeignKey, Model, NotNull, Table} from "sequelize-typescript";
import {Event} from "./Event";
import {User} from "./User";
@Table({underscored: true})
export class EventParticipant extends Model<EventParticipant> {
@NotNull
@ForeignKey(() => User)
@Column({allowNull: false})
public userId: number;
@NotNull
@ForeignKey(() => Event)
@Column({allowNull: false})
public eventId: number;
}

@ -1,15 +1,17 @@
import {Column, ForeignKey, Model, NotNull, Table} from "sequelize-typescript"; import {Column, ForeignKey, Model, NotNull, PrimaryKey, Table} from "sequelize-typescript";
import {User} from "./User"; import {User} from "./User";
@Table({underscored: true}) @Table({underscored: true})
export class Friendship extends Model<Friendship> { export class Friendship extends Model<Friendship> {
@ForeignKey(() => User) @ForeignKey(() => User)
@PrimaryKey
@NotNull @NotNull
@Column({allowNull: false}) @Column({allowNull: false})
public userId: number; public userId: number;
@ForeignKey(() => User) @ForeignKey(() => User)
@PrimaryKey
@NotNull @NotNull
@Column({allowNull: false}) @Column({allowNull: false})
public friendId: number; public friendId: number;

@ -1,18 +1,6 @@
import * as sqz from "sequelize"; import {BelongsTo, BelongsToMany, Column, ForeignKey, HasMany, Model, NotNull, Table} from "sequelize-typescript";
import {
BelongsTo,
BelongsToMany,
Column,
CreatedAt, ForeignKey,
HasMany,
Model,
NotNull,
Table,
Unique,
UpdatedAt,
} from "sequelize-typescript";
import {ChatMessage} from "./ChatMessage";
import {ChatRoom} from "./ChatRoom"; import {ChatRoom} from "./ChatRoom";
import {Event} from "./Event";
import {GroupAdmin} from "./GroupAdmin"; import {GroupAdmin} from "./GroupAdmin";
import {GroupMember} from "./GroupMember"; import {GroupMember} from "./GroupMember";
import {User} from "./User"; import {User} from "./User";
@ -45,6 +33,9 @@ export class Group extends Model<Group> {
@BelongsTo(() => ChatRoom) @BelongsTo(() => ChatRoom)
public rChat: ChatRoom; public rChat: ChatRoom;
@HasMany(() => Event, "groupId")
public rEvents: Event[];
public async creator(): Promise<User> { public async creator(): Promise<User> {
return await this.$get("rCreator") as User; return await this.$get("rCreator") as User;
} }
@ -62,4 +53,8 @@ export class Group extends Model<Group> {
public async chat(): Promise<ChatRoom> { public async chat(): Promise<ChatRoom> {
return await this.$get("rChat") as ChatRoom; return await this.$get("rChat") as ChatRoom;
} }
public async events(): Promise<Event[]> {
return await this.$get("rEvents") as Event[];
}
} }

@ -1,16 +1,4 @@
import * as sqz from "sequelize"; import {Column, ForeignKey, Model, NotNull, Table,} from "sequelize-typescript";
import {
BelongsTo,
BelongsToMany,
Column,
CreatedAt, ForeignKey,
HasMany,
Model, Not,
NotNull,
Table,
Unique,
UpdatedAt,
} from "sequelize-typescript";
import {Group} from "./Group"; import {Group} from "./Group";
import {User} from "./User"; import {User} from "./User";

@ -1,16 +1,4 @@
import * as sqz from "sequelize"; import {Column, ForeignKey, Model, NotNull, Table,} from "sequelize-typescript";
import {
BelongsTo,
BelongsToMany,
Column,
CreatedAt, ForeignKey,
HasMany,
Model, Not,
NotNull,
Table,
Unique,
UpdatedAt,
} from "sequelize-typescript";
import {Group} from "./Group"; import {Group} from "./Group";
import {User} from "./User"; import {User} from "./User";

@ -1,5 +1,6 @@
import * as sqz from "sequelize"; import * as sqz from "sequelize";
import { import {
BelongsTo,
BelongsToMany, BelongsToMany,
Column, Column,
CreatedAt, CreatedAt,
@ -11,9 +12,12 @@ import {
UpdatedAt, UpdatedAt,
} from "sequelize-typescript"; } from "sequelize-typescript";
import {RequestNotFoundError} from "../errors/RequestNotFoundError"; import {RequestNotFoundError} from "../errors/RequestNotFoundError";
import {UserNotFoundError} from "../errors/UserNotFoundError";
import {ChatMember} from "./ChatMember"; import {ChatMember} from "./ChatMember";
import {ChatMessage} from "./ChatMessage"; import {ChatMessage} from "./ChatMessage";
import {ChatRoom} from "./ChatRoom"; import {ChatRoom} from "./ChatRoom";
import {Event} from "./Event";
import {EventParticipant} from "./EventParticipant";
import {Friendship} from "./Friendship"; import {Friendship} from "./Friendship";
import {Group} from "./Group"; import {Group} from "./Group";
import {GroupAdmin} from "./GroupAdmin"; import {GroupAdmin} from "./GroupAdmin";
@ -46,9 +50,12 @@ export class User extends Model<User> {
@Column({defaultValue: 0, allowNull: false}) @Column({defaultValue: 0, allowNull: false})
public rankpoints: number; public rankpoints: number;
@BelongsToMany(() => User, () => Friendship) @BelongsToMany(() => User, () => Friendship, "userId")
public rFriends: User[]; public rFriends: User[];
@BelongsToMany(() => User, () => Friendship, "friendId")
public rFriendOf: User[];
@BelongsToMany(() => Post, () => PostVote) @BelongsToMany(() => Post, () => PostVote)
public votes: Array<Post & {PostVote: PostVote}>; public votes: Array<Post & {PostVote: PostVote}>;
@ -58,13 +65,16 @@ export class User extends Model<User> {
@BelongsToMany(() => Group, () => GroupAdmin) @BelongsToMany(() => Group, () => GroupAdmin)
public rAdministratedGroups: Group[]; public rAdministratedGroups: Group[];
@BelongsToMany(() => Event, () => EventParticipant)
public rEvents: Event[];
@BelongsToMany(() => Group, () => GroupMember) @BelongsToMany(() => Group, () => GroupMember)
public rGroups: Group[]; public rGroups: Group[];
@HasMany(() => Post, "authorId") @HasMany(() => Post, "authorId")
public rPosts: Post[]; public rPosts: Post[];
@HasMany(() => Request, "receiverId") @HasMany(() => Request, "senderId")
public rSentRequests: Request[]; public rSentRequests: Request[];
@HasMany(() => Request, "receiverId") @HasMany(() => Request, "receiverId")
@ -90,8 +100,16 @@ export class User extends Model<User> {
return this.getDataValue("createdAt"); return this.getDataValue("createdAt");
} }
public get points(): number {
return this.rankpoints;
}
public get level(): number {
return Math.ceil(this.rankpoints / 100);
}
public async friends(): Promise<User[]> { public async friends(): Promise<User[]> {
return await this.$get("rFriends") as User[]; return await this.$get("rFriendOf") as User[];
} }
public async chats(): Promise<ChatRoom[]> { public async chats(): Promise<ChatRoom[]> {
@ -126,6 +144,10 @@ export class User extends Model<User> {
return await this.$get("rGroups") as Group[]; return await this.$get("rGroups") as Group[];
} }
public async events(): Promise<Event[]> {
return await this.$get("rEvents") as Event[];
}
public async denyRequest(sender: number, type: RequestType) { public async denyRequest(sender: number, type: RequestType) {
const request = await this.$get("rReceivedRequests", const request = await this.$get("rReceivedRequests",
{where: {senderId: sender, requestType: type}}) as Request[]; {where: {senderId: sender, requestType: type}}) as Request[];
@ -140,11 +162,25 @@ export class User extends Model<User> {
if (requests.length > 0) { if (requests.length > 0) {
const request = requests[0]; const request = requests[0];
if (request.requestType === RequestType.FRIENDREQUEST) { if (request.requestType === RequestType.FRIENDREQUEST) {
await this.$add("friends", sender); await Friendship.bulkCreate([
{userId: this.id, friendId: sender},
{userId: sender, friendId: this.id},
], {ignoreDuplicates: true});
await request.destroy(); await request.destroy();
} }
} else { } else {
throw new RequestNotFoundError(sender, this.id, type); throw new RequestNotFoundError(sender, this.id, type);
} }
} }
public async removeFriend(friendId: number) {
const friend = await User.findByPk(friendId);
if (friend) {
await this.$remove("rFriends", friend);
await this.$remove("rFriendOf", friend);
return true;
} else {
throw new UserNotFoundError(friendId);
}
}
} }

@ -9,3 +9,5 @@ export {User} from "./User";
export {Group} from "./Group"; export {Group} from "./Group";
export {GroupAdmin} from "./GroupAdmin"; export {GroupAdmin} from "./GroupAdmin";
export {GroupMember} from "./GroupMember"; export {GroupMember} from "./GroupMember";
export {Event} from "./Event";
export {EventParticipant} from "./EventParticipant";

Loading…
Cancel
Save