Implemented Request API

- implemented sendRequest, acceptRequest, denyRequest
pull/1/head
Trivernis 5 years ago
parent 770c8a35fa
commit b9655acc12

@ -3,8 +3,11 @@ import {Chatroom} from "./Chatroom";
import {User} from "./User"; import {User} from "./User";
export class ChatMessage { export class ChatMessage {
constructor(public author: User, public chat: Chatroom, public createdAt: number, public content: string) { constructor(
} public readonly author: User,
public readonly chat: Chatroom,
public readonly createdAt: number,
public readonly content: string) {}
/** /**
* The content rendered by markdown-it. * The content rendered by markdown-it.

@ -108,7 +108,7 @@ export class Post extends DataObject {
} else { } else {
if (uVote) { if (uVote) {
await queryHelper.first({ await queryHelper.first({
text: "UPDATE votes SET vote_type = $1 WHERE user_id = $1 AND item_id = $3", text: "UPDATE votes SET vote_type = $1 WHERE user_id = $2 AND item_id = $3",
values: [type, userId, this.id], values: [type, userId, this.id],
}); });
} else { } else {

@ -1,6 +1,8 @@
import {RequestNotFoundError} from "../errors/RequestNotFoundError";
import {Chatroom} from "./Chatroom"; import {Chatroom} from "./Chatroom";
import {queryHelper} from "./index"; import dataaccess, {queryHelper} from "./index";
import {User} from "./User"; import {User} from "./User";
import {Request} from "./Request";
export class Profile extends User { export class Profile extends User {
@ -28,6 +30,30 @@ export class Profile extends User {
} }
} }
/**
* Returns all open requests the user has send.
*/
public async sentRequests() {
const result = await queryHelper.all({
cache: true,
text: "SELECT * FROM requests WHERE sender = $1",
values: [this.id],
});
return this.getRequests(result);
}
/**
* Returns all received requests of the user.
*/
public async receivedRequests() {
const result = await queryHelper.all({
cache: true,
text: "SELECT * FROM requests WHERE receiver = $1",
values: [this.id],
});
return this.getRequests(result);
}
/** /**
* Sets the greenpoints of a user. * Sets the greenpoints of a user.
* @param points * @param points
@ -46,7 +72,7 @@ export class Profile extends User {
*/ */
public async setEmail(email: string): Promise<string> { public async setEmail(email: string): Promise<string> {
const result = await queryHelper.first({ const result = await queryHelper.first({
text: "UPDATE TABLE users SET email = $1 WHERE users.id = $2 RETURNING email", text: "UPDATE users SET email = $1 WHERE users.id = $2 RETURNING email",
values: [email, this.id], values: [email, this.id],
}); });
return result.email; return result.email;
@ -57,7 +83,7 @@ export class Profile extends User {
*/ */
public async setHandle(handle: string): Promise<string> { public async setHandle(handle: string): Promise<string> {
const result = await queryHelper.first({ const result = await queryHelper.first({
text: "UPDATE TABLE users SET handle = $1 WHERE id = $2", text: "UPDATE users SET handle = $1 WHERE id = $2",
values: [handle, this.id], values: [handle, this.id],
}); });
return result.handle; return result.handle;
@ -69,9 +95,69 @@ export class Profile extends User {
*/ */
public async setName(name: string): Promise<string> { public async setName(name: string): Promise<string> {
const result = await queryHelper.first({ const result = await queryHelper.first({
text: "UPDATE TABLE users SET name = $1 WHERE id = $2", text: "UPDATE users SET name = $1 WHERE id = $2",
values: [name, this.id], values: [name, this.id],
}); });
return result.name; return result.name;
} }
/**
* Denys a request.
* @param sender
* @param type
*/
public async denyRequest(sender: number, type: dataaccess.RequestType) {
await queryHelper.first({
text: "DELETE FROM requests WHERE receiver = $1 AND sender = $2 AND type = $3",
values: [this.id, sender, type],
});
}
/**
* Accepts a request.
* @param sender
* @param type
*/
public async acceptRequest(sender: number, type: dataaccess.RequestType) {
const exists = await queryHelper.first({
cache: true,
text: "SELECT 1 FROM requests WHERE receiver = $1 AND sender = $2 AND type = $3",
values: [this.id, sender, type],
});
if (exists) {
if (type === dataaccess.RequestType.FRIENDREQUEST) {
await queryHelper.first({
text: "INSERT INTO user_friends (user_id, friend_id) VALUES ($1, $2)",
values: [this.id, sender],
});
}
} else {
throw new RequestNotFoundError(sender, this.id, type);
}
}
/**
* Returns request wrapper for a row database request result.
* @param rows
*/
private getRequests(rows: any) {
const requests = [];
const requestUsers: any = {};
for (const row of rows) {
let sender = requestUsers[row.sender];
if (!sender) {
sender = new User(row.sender);
requestUsers[row.sender] = sender;
}
let receiver = requestUsers[row.receiver];
if (!receiver) {
receiver = new User(row.receiver);
requestUsers[row.receiver] = receiver;
}
requests.push(new Request(sender, receiver, row.type));
}
return requests;
}
} }

@ -0,0 +1,12 @@
import dataaccess from "./index";
import {User} from "./User";
/**
* Represents a request to a user.
*/
export class Request {
constructor(
public readonly sender: User,
public readonly receiver: User,
public readonly type: dataaccess.RequestType) {}
}

@ -7,6 +7,7 @@ import {ChatMessage} from "./ChatMessage";
import {Chatroom} from "./Chatroom"; import {Chatroom} from "./Chatroom";
import {Post} from "./Post"; import {Post} from "./Post";
import {Profile} from "./Profile"; import {Profile} from "./Profile";
import {Request} from "./Request";
import {User} from "./User"; import {User} from "./User";
const config = globals.config; const config = globals.config;
@ -184,6 +185,22 @@ namespace dataaccess {
} }
} }
/**
* Sends a request to a user.
* @param sender
* @param receiver
* @param type
*/
export async function createRequest(sender: number, receiver: number, type?: RequestType) {
type = type || RequestType.FRIENDREQUEST;
const result = await queryHelper.first({
text: "INSERT INTO requests (sender, receiver, type) VALUES ($1, $2, $3) RETURNING *",
values: [sender, receiver, type],
});
return new Request(new User(result.sender), new User(result.receiver), result.type);
}
/** /**
* 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.
*/ */

@ -0,0 +1,9 @@
import dataaccess from "../dataaccess";
import {BaseError} from "./BaseError";
export class RequestNotFoundError extends BaseError {
constructor(sender: number, receiver: number, type: dataaccess.RequestType) {
super(`Request with sender '${sender}' and receiver '${receiver}' of type '${type}' not found.`);
}
}

@ -11,9 +11,6 @@ type Query {
"returns the chat object for a chat id" "returns the chat object for a chat id"
getChat(chatId: ID!): ChatRoom getChat(chatId: ID!): ChatRoom
"returns the request object for a request id"
getRequest(requestId: ID!): Request
"find a post by the posted date or content" "find a post by the posted date or content"
findPost(first: Int, offset: Int, text: String!, postedDate: String): [Post] findPost(first: Int, offset: Int, text: String!, postedDate: String): [Post]
@ -41,10 +38,10 @@ type Mutation {
report(postId: ID!): Boolean report(postId: ID!): Boolean
"send a request" "send a request"
sendRequest(reciever: ID!, type: RequestType): Boolean sendRequest(receiver: ID!, type: RequestType): Request
"lets you accept a request for a given request id" "lets you accept a request for a given request id"
acceptRequest(requestId: ID!): Boolean acceptRequest(sender: ID!, type: RequestType): Boolean
"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
@ -143,8 +140,11 @@ type Profile implements UserData {
"all friends of the user" "all friends of the user"
friends: [User] friends: [User]
"all request for groupChats/friends/events" "all sent request for groupChats/friends/events"
requests: [Request] sentRequests: [Request]
"all received request for groupChats/friends/events"
receivedRequests: [Request]
} }
"represents a single user post" "represents a single user post"
@ -177,9 +177,6 @@ type Post {
"represents a request of any type" "represents a request of any type"
type Request { type Request {
"id of the request"
id: ID!
"Id of the user who sended the request" "Id of the user who sended the request"
sender: User! sender: User!
@ -187,7 +184,7 @@ type Request {
receiver: User! receiver: User!
"type of the request" "type of the request"
requestType: RequestType! type: RequestType!
} }
"represents a chatroom" "represents a chatroom"
@ -225,7 +222,10 @@ enum VoteType {
DOWNVOTE DOWNVOTE
} }
"represents the type of request that the user has received" """
represents the type of request that the user has received
Currently on Friend Requests are implemented.
"""
enum RequestType { enum RequestType {
FRIENDREQUEST FRIENDREQUEST
GROUPINVITE GROUPINVITE

@ -183,6 +183,7 @@ class HomeRoute extends Route {
}, },
async sendMessage({chatId, content}: {chatId: number, content: string}) { async sendMessage({chatId, content}: {chatId: number, content: string}) {
if (!req.session.userId) { if (!req.session.userId) {
res.status(status.UNAUTHORIZED);
return new NotLoggedInGqlError(); return new NotLoggedInGqlError();
} }
if (chatId && content) { if (chatId && content) {
@ -197,6 +198,51 @@ class HomeRoute extends Route {
return new GraphQLError("No chatId or content given."); return new GraphQLError("No chatId or content given.");
} }
}, },
async sendRequest({receiver, type}: {receiver: number, type: dataaccess.RequestType}) {
if (!req.session.userId) {
res.status(status.UNAUTHORIZED);
return new NotLoggedInGqlError();
}
if (receiver && type) {
return await dataaccess.createRequest(req.session.userId, receiver, type);
} else {
res.status(status.BAD_REQUEST);
return new GraphQLError("No receiver or type given.");
}
},
async denyRequest({sender, type}: {sender: number, type: dataaccess.RequestType}) {
if (!req.session.userId) {
res.status(status.UNAUTHORIZED);
return new NotLoggedInGqlError();
}
if (sender && type) {
const profile = new Profile(req.session.userId);
await profile.denyRequest(sender, type);
return true;
} else {
res.status(status.BAD_REQUEST);
return new GraphQLError("No sender or type given.");
}
},
async acceptRequest({sender, type}: {sender: number, type: dataaccess.RequestType}) {
if (!req.session.userId) {
res.status(status.UNAUTHORIZED);
return new NotLoggedInGqlError();
}
if (sender && type) {
try {
const profile = new Profile(req.session.userId);
await profile.acceptRequest(sender, type);
return true;
} catch (err) {
res.status(status.BAD_REQUEST);
return err.graphqlError;
}
} else {
res.status(status.BAD_REQUEST);
return new GraphQLError("No sender or type given.");
}
},
}; };
} }
} }

@ -81,7 +81,8 @@ DO $$ BEGIN
CREATE TABLE IF NOT EXISTS votes ( CREATE TABLE IF NOT EXISTS votes (
user_id SERIAL REFERENCES users (id) ON DELETE CASCADE, user_id SERIAL REFERENCES users (id) ON DELETE CASCADE,
item_id BIGSERIAL REFERENCES posts (id) ON DELETE CASCADE, item_id BIGSERIAL REFERENCES posts (id) ON DELETE CASCADE,
vote_type votetype DEFAULT 'DOWNVOTE' vote_type votetype DEFAULT 'DOWNVOTE',
PRIMARY KEY (user_id, item_id)
); );
CREATE TABLE IF NOT EXISTS events ( CREATE TABLE IF NOT EXISTS events (
@ -92,7 +93,8 @@ DO $$ BEGIN
CREATE TABLE IF NOT EXISTS event_members ( CREATE TABLE IF NOT EXISTS event_members (
event BIGSERIAL REFERENCES events (id), event BIGSERIAL REFERENCES events (id),
member SERIAL REFERENCES users (id) member SERIAL REFERENCES users (id),
PRIMARY KEY (event, member)
); );
CREATE TABLE IF NOT EXISTS chats ( CREATE TABLE IF NOT EXISTS chats (
@ -109,17 +111,21 @@ DO $$ BEGIN
CREATE TABLE IF NOT EXISTS chat_members ( CREATE TABLE IF NOT EXISTS chat_members (
chat BIGSERIAL REFERENCES chats (id) ON DELETE CASCADE, chat BIGSERIAL REFERENCES chats (id) ON DELETE CASCADE,
member SERIAL REFERENCES users (id) ON DELETE CASCADE member SERIAL REFERENCES users (id) ON DELETE CASCADE,
PRIMARY KEY (chat, member)
); );
CREATE TABLE IF NOT EXISTS user_friends ( CREATE TABLE IF NOT EXISTS user_friends (
user_id SERIAL REFERENCES users (id) ON DELETE CASCADE, user_id SERIAL REFERENCES users (id) ON DELETE CASCADE,
friend_id SERIAL REFERENCES users (id) ON DELETE CASCADE friend_id SERIAL REFERENCES users (id) ON DELETE CASCADE,
PRIMARY KEY (user_id, friend_id)
); );
CREATE TABLE IF NOT EXISTS requests ( CREATE TABLE IF NOT EXISTS requests (
sender SERIAL REFERENCES users (id) ON DELETE CASCADE, sender SERIAL REFERENCES users (id) ON DELETE CASCADE,
receiver SERIAL REFERENCES users (id) ON DELETE CASCADE receiver SERIAL REFERENCES users (id) ON DELETE CASCADE,
type requesttype DEFAULT 'FRIENDREQUEST',
PRIMARY KEY (sender, receiver, type)
); );
END $$; END $$;

@ -13,4 +13,7 @@ DO $$ BEGIN
DROP COLUMN IF EXISTS upvotes, DROP COLUMN IF EXISTS upvotes,
DROP COLUMN IF EXISTS downvotes; DROP COLUMN IF EXISTS downvotes;
ALTER TABLE requests
ADD COLUMN IF NOT EXISTS type requesttype DEFAULT 'FRIENDREQUEST';
END $$; END $$;

Loading…
Cancel
Save