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

pull/1/head
Trivernis 5 years ago committed by Gitea
commit 3a31d34de0

33
package-lock.json generated

@ -1039,9 +1039,9 @@
}
},
"chokidar": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz",
"integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==",
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
"integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
"dev": true,
"requires": {
"anymatch": "^2.0.0",
@ -1858,9 +1858,9 @@
}
},
"es5-ext": {
"version": "0.10.50",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.50.tgz",
"integrity": "sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==",
"version": "0.10.51",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.51.tgz",
"integrity": "sha512-oRpWzM2WcLHVKpnrcyB7OW8j/s67Ba04JCm0WnNv3RiABSvs7mrQlutB8DBv793gKcp0XENR8Il8WxGTlZ73gQ==",
"dev": true,
"requires": {
"es6-iterator": "~2.0.3",
@ -1880,13 +1880,13 @@
}
},
"es6-symbol": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
"integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.2.tgz",
"integrity": "sha512-/ZypxQsArlv+KHpGvng52/Iz8by3EQPxhmbuz8yFG89N/caTFBSbcXONDw0aMjy827gQg26XAjP4uXFvnfINmQ==",
"dev": true,
"requires": {
"d": "1",
"es5-ext": "~0.10.14"
"d": "^1.0.1",
"es5-ext": "^0.10.51"
}
},
"es6-weak-map": {
@ -3021,6 +3021,11 @@
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"g": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/g/-/g-2.0.1.tgz",
"integrity": "sha1-C1lj69DKcOO8jGdmk0oCGCHIuFc="
},
"gauge": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
@ -7008,9 +7013,9 @@
}
},
"upath": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz",
"integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
"integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
"dev": true
},
"uri-js": {

@ -49,6 +49,7 @@
"express-session": "^1.16.2",
"express-socket.io-session": "^1.3.5",
"fs-extra": "^8.1.0",
"g": "^2.0.1",
"graphql": "^14.4.2",
"graphql-import": "^0.7.1",
"js-yaml": "^3.13.1",

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

@ -68,9 +68,10 @@ export class QueryHelper {
/**
* Constructor.
* @param pgPool
* @param tableCreationFile
* @param [tableCreationFile]
* @param [tableUpdateFile]
*/
constructor(pgPool: Pool, private tableCreationFile?: string) {
constructor(pgPool: Pool, private tableCreationFile?: string, private tableUpdateFile?: string) {
this.pool = pgPool;
}
@ -85,6 +86,17 @@ export class QueryHelper {
}
}
/**
* Updates the definition of the tables if the table update file was passed in the constructor
*/
public async updateTableDefinitions() {
if (this.tableUpdateFile) {
logger.info("Updating table definitions...");
const tableSql = await fsx.readFile(this.tableUpdateFile, "utf-8");
await this.query({text: tableSql});
}
}
/**
* executes the sql query with values and returns all results.
* @param query

@ -0,0 +1,20 @@
/**
* abstact DataObject class
*/
export abstract class DataObject {
protected dataLoaded: boolean = false;
constructor(public id: number, protected row?: any) {
}
protected abstract loadData(): Promise<void>;
/**
* Loads data from the database if data has not been loaded
*/
protected loadDataIfNotExists() {
if (this.dataLoaded) {
this.loadData();
}
}
}

@ -0,0 +1,103 @@
import {DataObject} from "./DataObject";
import {queryHelper} from "./index";
import dataaccess 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<dataaccess.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,137 @@
import {DataObject} from "./DataObject";
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,66 @@
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 tableUpdateFile = __dirname + "/../../sql/update-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, tableUpdateFile);
namespace dataaccess {
/**
* Initializes everything that needs to be initialized asynchronous.
*/
export async function init() {
await queryHelper.updateTableDefinitions();
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,100 +1,145 @@
type Query {
"returns the user object for a given user id"
getUser(userId: ID): User
"returns the post object for a post id"
getPost(postId: ID): Post
"returns the chat object for a chat id"
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"
findPost(first: Int, offset: Int, text: String!, postedDate: String): [Post]
"find a user by user name or handle"
findUser(first: Int, offset: Int, name: String!, handle: String!): [User]
"returns the user object for a given user id"
getUser(userId: ID): User
"returns the post object for a post id"
getPost(postId: ID): Post
"returns the chat object for a chat id"
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"
findPost(first: Int, offset: Int, text: String!, postedDate: String): [Post]
"find a user by user name or handle"
findUser(first: Int, offset: Int, name: String!, handle: String!): [User]
}
type Mutation {
"Upvote/downvote a Post"
vote(postId: ID!, type: [VoteType!]!): Boolean
"Report the post"
report(postId: ID!): Boolean
"Upvote/downvote a Post"
vote(postId: ID!, type: [VoteType!]!): Boolean
"Report the post"
report(postId: ID!): Boolean
"send a request"
sendRequest(reciever: ID!, type: RequestType): Boolean
"lets you accept a request for a given request id"
acceptRequest(requestId: ID!): Boolean
acceptRequest(requestId: ID!): Boolean
"lets you deny a request for a given request id"
denyRequest(requestId: ID!): Boolean
"send a message in a Chatroom"
sendMessage(chatId: ID!, content: String!): Boolean
sendMessage(chatId: ID!, content: String!): Boolean
"create the post"
createPost(text: String, picture: String, tags: [String]): Boolean
"delete the post for a given post id"
deletePost(postId: ID!): Boolean
}
"represents a single user account"
type User {
"url for the Profile picture of the User"
profilePicture: String!
"name of the User"
name: String!
"unique identifier name from the User"
handle: String!
"Id of the User"
id: ID!
"the total number of posts the user posted"
numberOfPosts: Int
"returns a given number of posts of a user"
getAllPosts(first: Int=10, offset: Int): [Post]
"creation date of the user account"
joinedDate: String!
"returns chats the user pinned"
pinnedChats: [ChatRoom]
"returns all friends of the user"
friends: [User]
"all request for groupChats/friends/events"
requests: [Request]
"url for the Profile picture of the User"
profilePicture: String!
"name of the User"
name: String!
"unique identifier name from the User"
handle: String!
"Id of the User"
id: ID!
"the total number of posts the user posted"
numberOfPosts: Int
"returns a given number of posts of a user"
getAllPosts(first: Int=10, offset: Int): [Post]
"creation date of the user account"
joinedDate: String!
"returns chats the user pinned"
pinnedChats: [ChatRoom]
"returns all friends of the user"
friends: [User]
"all request for groupChats/friends/events"
requests: [Request]
}
"represents a single user post"
type Post {
"returns the path to the posts picture if it has one"
picture: String
"returns the text of the post"
text: String
"upvotes of the Post"
upvotes: Int!
"downvotes of the Post"
downvotes: Int!
"the user that is the author of the Post"
author: User!
"date the post was created"
creationDate: String!
"returns the type of vote the user performed on the post"
alreadyVoted: VoteType
"returns the tags of the post"
tags: [String]
"returns the path to the posts picture if it has one"
picture: String
"returns the text of the post"
text: String
"upvotes of the Post"
upvotes: Int!
"downvotes of the Post"
downvotes: Int!
"the user that is the author of the Post"
author: User!
"date the post was created"
creationDate: String!
"returns the type of vote the user performed on the post"
userVote: VoteType
"returns the tags of the post"
tags: [String]
}
"represents a request of any type"
type Request {
"id of the request"
id: ID!
"type of the request"
requestType: RequestType!
"id of the request"
id: ID!
"Id of the user who sended the request"
sender: User!
"Id of the user who received the request"
receiver: User!
"type of the request"
requestType: RequestType!
}
"represents a chatroom"
type ChatRoom {
"the members of the chatroom"
members: [User!]
"return a specfic range of messages posted in the chat"
getMessages(first: Int, offset: Int): [String]
"id of the chat"
id: ID!
"the members of the chatroom"
members: [User!]
"return a specfic range of messages posted in the chat"
getMessages(first: Int, offset: Int): [String]
"id of the chat"
id: ID!
}
"represents the type of vote performed on a post"
enum VoteType {
UPVOTE
DOWNVOTE
UPVOTE
DOWNVOTE
}
"represents the type of request that the user has received"
enum RequestType {
FRIENDREQUEST
GROUPINVITE
EVENTINVITE
FRIENDREQUEST
GROUPINVITE
EVENTINVITE
}

@ -20,7 +20,8 @@ CREATE TABLE IF NOT EXISTS posts (
CREATE TABLE IF NOT EXISTS votes (
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 'upvote'
);
CREATE TABLE IF NOT EXISTS events (

@ -0,0 +1,3 @@
ALTER TABLE IF EXISTS votes
ADD COLUMN IF NOT EXISTS vote_type varchar(8) DEFAULT 'upvote',
ALTER COLUMN vote_type SET DEFAULT 'upvote';
Loading…
Cancel
Save