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

pull/1/head
Tomislav_K 5 years ago committed by Gitea
commit f196ca527a

@ -2,7 +2,7 @@ import * as express from "express";
import * as http from "http"; import * as http from "http";
import * as path from "path"; import * as path from "path";
import * as socketIo from "socket.io"; import * as socketIo from "socket.io";
import {DTO} from "./lib/DTO"; import dataaccess from "./lib/dataaccess";
import globals from "./lib/globals"; import globals from "./lib/globals";
import routes from "./routes"; import routes from "./routes";
@ -10,20 +10,18 @@ class App {
public app: express.Application; public app: express.Application;
public io: socketIo.Server; public io: socketIo.Server;
public server: http.Server; public server: http.Server;
public dto: DTO;
constructor() { constructor() {
this.app = express(); this.app = express();
this.server = new http.Server(this.app); this.server = new http.Server(this.app);
this.io = socketIo(this.server); this.io = socketIo(this.server);
this.dto = new DTO();
} }
/** /**
* initializes everything that needs to be initialized asynchronous. * initializes everything that needs to be initialized asynchronous.
*/ */
public async init() { public async init() {
await this.dto.init(); await dataaccess.init();
await routes.ioListeners(this.io); await routes.ioListeners(this.io);
this.app.set("views", path.join(__dirname, "views")); this.app.set("views", path.join(__dirname, "views"));
this.app.set("view engine", "pug"); this.app.set("view engine", "pug");

@ -1,248 +0,0 @@
import {Runtime} from "inspector";
import {Pool} from "pg";
import globals from "./globals";
import {QueryHelper} from "./QueryHelper";
const config = globals.config;
const tableCreationFile = __dirname + "/../sql/create-tables.sql";
const dbClient: Pool = new Pool({
database: config.database.database,
host: config.database.host,
password: config.database.password,
port: config.database.port,
user: config.database.user,
});
const queryHelper = new QueryHelper(dbClient, tableCreationFile);
export class DTO {
private queryHelper: QueryHelper;
constructor() {
this.queryHelper = queryHelper;
}
/**
* Initializes everything that needs to be initialized asynchronous.
*/
public async init() {
await this.queryHelper.createTables();
}
/**
* Returns the user by id
* @param userId
*/
public getUser(userId: number) {
return new User(userId);
}
/**
* Returns the user by handle.
* @param userHandle
*/
public async getUserByHandle(userHandle: string) {
const result = await this.queryHelper.first({
text: "SELECT * FROM users WHERE users.handle = $1",
values: [userHandle],
});
return new User(result.id, result);
}
}
export class User {
public readonly id: number;
private $name: string;
private $handle: string;
private $email: string;
private $greenpoints: number;
private $joinedAt: string;
private dataLoaded: boolean;
/**
* Constructor of the user
* @param id
* @param row
*/
constructor(id: number, private row?: any) {
this.id = id;
}
/**
* The name of the user
*/
public async name(): Promise<string> {
if (!this.dataLoaded) {
await this.loadData();
}
return this.$name;
}
/**
* Sets the username of the user
* @param name
*/
public async setName(name: string): Promise<string> {
const result = await queryHelper.first({
text: "UPDATE TABLE users SET name = $1 WHERE id = $2",
values: [name, this.id],
});
return result.name;
}
/**
* The unique handle of the user.
*/
public async handle(): Promise<string> {
if (!this.dataLoaded) {
await this.loadData();
}
return this.$handle;
}
/**
* Updates the handle of the user
*/
public async setHandle(handle: string): Promise<string> {
const result = await queryHelper.first({
text: "UPDATE TABLE users SET handle = $1 WHERE id = $2",
values: [handle, this.id],
});
return result.handle;
}
/**
* The email of the user
*/
public async email(): Promise<string> {
if (!this.dataLoaded) {
await this.loadData();
}
return this.$email;
}
/**
* Sets the email of the user
* @param email
*/
public async setEmail(email: string): Promise<string> {
const result = await queryHelper.first({
text: "UPDATE TABLE users SET email = $1 WHERE users.id = $2 RETURNING email",
values: [email, this.id],
});
return result.email;
}
/**
* The number of greenpoints of the user
*/
public async greenpoints(): Promise<number> {
if (!this.dataLoaded) {
await this.loadData();
}
return this.$greenpoints;
}
/**
* Sets the greenpoints of a user.
* @param points
*/
public async setGreenpoints(points: number): Promise<number> {
const result = await queryHelper.first({
text: "UPDATE users SET greenpoints = $1 WHERE id = $2 RETURNING greenpoints",
values: [points, this.id],
});
return result.greenpoints;
}
/**
* The date the user joined the platform
*/
public async joinedAt(): Promise<Date> {
if (!this.dataLoaded) {
await this.loadData();
}
return new Date(this.$joinedAt);
}
/**
* Fetches the data for the user.
*/
private async loadData(): Promise<void> {
let result: any;
if (this.row) {
result = this.row;
} else {
result = await queryHelper.first({
text: "SELECT * FROM users WHERE user.id = $1",
values: [this.id],
});
}
if (result) {
this.$name = result.name;
this.$handle = result.handle;
this.$email = result.email;
this.$greenpoints = result.greenpoints;
this.$joinedAt = result.joined_at;
this.dataLoaded = true;
}
}
}
export class Post {
public readonly id: number;
private $upvotes: number;
private $downvotes: number;
private $createdAt: string;
private $content: string;
private $author: number;
private $type: string;
private dataLoaded: boolean = false;
constructor(id: number, private row?: any) {
this.id = id;
}
/**
* Returns the upvotes of a post.
*/
public async upvotes() {
if (!this.dataLoaded) {
await this.loadData();
}
return this.$upvotes;
}
/**
* Returns the downvotes of the post
*/
public async downvotes() {
if (!this.dataLoaded) {
await this.loadData();
}
return this.$downvotes;
}
/**
* Loads the data from the database if needed.
*/
private async loadData(): Promise<void> {
let result: any;
if (this.row) {
result = this.row;
} else {
result = await queryHelper.first({
text: "SELECT * FROM posts WHERE posts.id = $1",
values: [this.id],
});
}
if (result) {
this.$author = result.author;
this.$content = result.content;
this.$downvotes = result.downvotes;
this.$upvotes = result.upvotes;
this.$createdAt = result.created_at;
this.$type = result.type;
this.dataLoaded = true;
}
}
}

@ -0,0 +1,14 @@
abstract class DataObject {
protected dataLoaded: boolean = false;
constructor(public id: number, protected row?: any) {
}
protected abstract loadData(): Promise<void>;
protected loadDataIfNotExists() {
if (this.dataLoaded) {
this.loadData();
}
}
}

@ -0,0 +1,101 @@
import {queryHelper, VoteType} from "./index";
import {User} from "./User";
export class Post extends DataObject {
public readonly id: number;
private $upvotes: number;
private $downvotes: number;
private $createdAt: string;
private $content: string;
private $author: number;
private $type: string;
/**
* Returns the upvotes of a post.
*/
public async upvotes(): Promise<number> {
this.loadDataIfNotExists();
return this.$upvotes;
}
/**
* Returns the downvotes of the post
*/
public async downvotes(): Promise<number> {
this.loadDataIfNotExists();
return this.$downvotes;
}
/**
* The content of the post (markdown)
*/
public async content(): Promise<string> {
this.loadDataIfNotExists();
return this.$content;
}
/**
* The date the post was created at.
*/
public async createdAt(): Promise<string> {
this.loadDataIfNotExists();
return this.$createdAt;
}
/**
* The autor of the post.
*/
public async author(): Promise<User> {
this.loadDataIfNotExists();
return new User(this.$author);
}
/**
* Deletes the post.
*/
public async delete(): Promise<void> {
const query = await queryHelper.first({
text: "DELETE FROM posts WHERE id = $1",
values: [this.id],
});
}
/**
* The type of vote the user performed on the post.
*/
public async userVote(userId: number): Promise<VoteType> {
const result = await queryHelper.first({
text: "SELECT vote_type FROM votes WHERE user_id = $1 AND item_id = $2",
values: [userId, this.id],
});
if (result) {
return result.vote_type;
} else {
return null;
}
}
/**
* Loads the data from the database if needed.
*/
protected async loadData(): Promise<void> {
let result: any;
if (this.row) {
result = this.row;
} else {
result = await queryHelper.first({
text: "SELECT * FROM posts WHERE posts.id = $1",
values: [this.id],
});
}
if (result) {
this.$author = result.author;
this.$content = result.content;
this.$downvotes = result.downvotes;
this.$upvotes = result.upvotes;
this.$createdAt = result.created_at;
this.$type = result.type;
this.dataLoaded = true;
}
}
}

@ -0,0 +1,136 @@
import {queryHelper} from "./index";
import {Post} from "./Post";
export class User extends DataObject {
private $name: string;
private $handle: string;
private $email: string;
private $greenpoints: number;
private $joinedAt: string;
/**
* The name of the user
*/
public async name(): Promise<string> {
this.loadDataIfNotExists();
return this.$name;
}
/**
* Sets the username of the user
* @param name
*/
public async setName(name: string): Promise<string> {
const result = await queryHelper.first({
text: "UPDATE TABLE users SET name = $1 WHERE id = $2",
values: [name, this.id],
});
return result.name;
}
/**
* The unique handle of the user.
*/
public async handle(): Promise<string> {
this.loadDataIfNotExists();
return this.$handle;
}
/**
* Updates the handle of the user
*/
public async setHandle(handle: string): Promise<string> {
const result = await queryHelper.first({
text: "UPDATE TABLE users SET handle = $1 WHERE id = $2",
values: [handle, this.id],
});
return result.handle;
}
/**
* The email of the user
*/
public async email(): Promise<string> {
this.loadDataIfNotExists();
return this.$email;
}
/**
* Sets the email of the user
* @param email
*/
public async setEmail(email: string): Promise<string> {
const result = await queryHelper.first({
text: "UPDATE TABLE users SET email = $1 WHERE users.id = $2 RETURNING email",
values: [email, this.id],
});
return result.email;
}
/**
* The number of greenpoints of the user
*/
public async greenpoints(): Promise<number> {
this.loadDataIfNotExists();
return this.$greenpoints;
}
/**
* Sets the greenpoints of a user.
* @param points
*/
public async setGreenpoints(points: number): Promise<number> {
const result = await queryHelper.first({
text: "UPDATE users SET greenpoints = $1 WHERE id = $2 RETURNING greenpoints",
values: [points, this.id],
});
return result.greenpoints;
}
/**
* The date the user joined the platform
*/
public async joinedAt(): Promise<Date> {
this.loadDataIfNotExists();
return new Date(this.$joinedAt);
}
/**
* Returns all posts for a user.
*/
public async posts(): Promise<Post[]> {
const result = await queryHelper.all({
text: "SELECT * FROM posts WHERE author = $1",
values: [this.id],
});
const posts = [];
for (const row of result) {
posts.push(new Post(row.id, row));
}
return posts;
}
/**
* Fetches the data for the user.
*/
protected async loadData(): Promise<void> {
let result: any;
if (this.row) {
result = this.row;
} else {
result = await queryHelper.first({
text: "SELECT * FROM users WHERE user.id = $1",
values: [this.id],
});
}
if (result) {
this.$name = result.name;
this.$handle = result.handle;
this.$email = result.email;
this.$greenpoints = result.greenpoints;
this.$joinedAt = result.joined_at;
this.dataLoaded = true;
}
}
}

@ -0,0 +1,63 @@
import {Pool} from "pg";
import globals from "../globals";
import {QueryHelper} from "../QueryHelper";
import {User} from "./User";
const config = globals.config;
const tableCreationFile = __dirname + "/../sql/create-tables.sql";
const dbClient: Pool = new Pool({
database: config.database.database,
host: config.database.host,
password: config.database.password,
port: config.database.port,
user: config.database.user,
});
export const queryHelper = new QueryHelper(dbClient, tableCreationFile);
namespace dataaccess {
/**
* Initializes everything that needs to be initialized asynchronous.
*/
export async function init() {
await queryHelper.createTables();
}
/**
* Returns the user by id
* @param userId
*/
export function getUser(userId: number) {
return new User(userId);
}
/**
* Returns the user by handle.
* @param userHandle
*/
export async function getUserByHandle(userHandle: string) {
const result = await this.queryHelper.first({
text: "SELECT * FROM users WHERE users.handle = $1",
values: [userHandle],
});
return new User(result.id, result);
}
/**
* Enum representing the types of votes that can be performed on a post.
*/
export enum VoteType {
UPVOTE = "UPVOTE",
DOWNVOTE = "DOWNVOTE",
}
/**
* Enum representing the types of request that can be created.
*/
export enum RequestType {
FRIENDREQUEST = "FRIENDREQUEST",
GROUPINVITE = "GROUPINVITE",
EVENTINVITE = "EVENTINVITE",
}
}
export default dataaccess;

@ -1,14 +1,19 @@
type Query { type Query {
"returns the user object for a given user id" "returns the user object for a given user id"
getUser(userId: ID): User getUser(userId: ID): User
"returns the post object for a post id" "returns the post object for a post id"
getPost(postId: ID): Post getPost(postId: ID): Post
"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" "returns the request object for a request id"
getRequest(requestId: ID): Request 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]
"find a user by user name or handle" "find a user by user name or handle"
findUser(first: Int, offset: Int, name: String!, handle: String!): [User] findUser(first: Int, offset: Int, name: String!, handle: String!): [User]
} }
@ -16,34 +21,48 @@ type Query {
type Mutation { type Mutation {
"Upvote/downvote a Post" "Upvote/downvote a Post"
vote(postId: ID!, type: [VoteType!]!): Boolean vote(postId: ID!, type: [VoteType!]!): Boolean
"Report the post" "Report the post"
report(postId: ID!): Boolean report(postId: ID!): Boolean
"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(requestId: ID!): Boolean
"send a message in a Chatroom" "send a message in a Chatroom"
sendMessage(chatId: ID!, content: String!): Boolean sendMessage(chatId: ID!, content: String!): Boolean
# TODO: createPost, deletePost, sendRequest, denyRequest
} }
"represents a single user account" "represents a single user account"
type User { type User {
"url for the Profile picture of the User" "url for the Profile picture of the User"
profilePicture: String! profilePicture: String!
"name of the User" "name of the User"
name: String! name: String!
"unique identifier name from the User" "unique identifier name from the User"
handle: String! handle: String!
"Id of the User" "Id of the User"
id: ID! id: ID!
"the total number of posts the user posted" "the total number of posts the user posted"
numberOfPosts: Int numberOfPosts: Int
"returns a given number of posts of a user" "returns a given number of posts of a user"
getAllPosts(first: Int=10, offset: Int): [Post] getAllPosts(first: Int=10, offset: Int): [Post]
"creation date of the user account" "creation date of the user account"
joinedDate: String! joinedDate: String!
"returns chats the user pinned" "returns chats the user pinned"
pinnedChats: [ChatRoom] pinnedChats: [ChatRoom]
"returns all friends of the user" "returns all friends of the user"
friends: [User] friends: [User]
"all request for groupChats/friends/events" "all request for groupChats/friends/events"
requests: [Request] requests: [Request]
} }
@ -52,18 +71,25 @@ type User {
type Post { type Post {
"returns the path to the posts picture if it has one" "returns the path to the posts picture if it has one"
picture: String picture: String
"returns the text of the post" "returns the text of the post"
text: String text: String
"upvotes of the Post" "upvotes of the Post"
upvotes: Int! upvotes: Int!
"downvotes of the Post" "downvotes of the Post"
downvotes: Int! downvotes: Int!
"the user that is the author of the Post" "the user that is the author of the Post"
author: User! author: User!
"date the post was created" "date the post was created"
creationDate: String! creationDate: String!
"returns the type of vote the user performed on the post" "returns the type of vote the user performed on the post"
alreadyVoted: VoteType userVote: VoteType
"returns the tags of the post" "returns the tags of the post"
tags: [String] tags: [String]
} }
@ -72,6 +98,7 @@ type Post {
type Request { type Request {
"id of the request" "id of the request"
id: ID! id: ID!
"type of the request" "type of the request"
requestType: RequestType! requestType: RequestType!
} }
@ -80,8 +107,10 @@ type Request {
type ChatRoom { type ChatRoom {
"the members of the chatroom" "the members of the chatroom"
members: [User!] members: [User!]
"return a specfic range of messages posted in the chat" "return a specfic range of messages posted in the chat"
getMessages(first: Int, offset: Int): [String] getMessages(first: Int, offset: Int): [String]
"id of the chat" "id of the chat"
id: ID! id: ID!
} }

@ -20,7 +20,8 @@ CREATE TABLE IF NOT EXISTS posts (
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 varchar(8) DEFAULT 1
); );
CREATE TABLE IF NOT EXISTS events ( CREATE TABLE IF NOT EXISTS events (

Loading…
Cancel
Save