added /game/add, API hopefully finished

This commit is contained in:
j-weissen 2023-01-03 23:42:07 +01:00
parent fff55edf79
commit 5f9a484285
19 changed files with 143 additions and 100 deletions

View file

@ -3,6 +3,7 @@ import helmet from "helmet";
import morgan from 'morgan'; import morgan from 'morgan';
import {leaderboardRoute} from "./leaderboardRoute.js"; import {leaderboardRoute} from "./leaderboardRoute.js";
import {userRoute} from "./userRoute.js"; import {userRoute} from "./userRoute.js";
import {gameRoute} from "./gameRoute.js";
const app = express() const app = express()
@ -10,24 +11,13 @@ const port = 3000
app.use(helmet()) app.use(helmet())
// configure & use logger // configure & use logger
let morganFormatted = morgan('[:date[iso]] :method :url - :status') let morganFormatted = morgan('[:date[iso]] :method :url - :status')
app.use(morganFormatted); app.use(morganFormatted);
app.use(express.json())
app.use('/leaderboard', leaderboardRoute) app.use('/leaderboard', leaderboardRoute)
app.use('/user', userRoute) app.use('/user', userRoute)
app.use('/game', gameRoute)
app.get('/helloworld', (req, res) => {
res.json({message: "Hello World!"})
})
app.post('/echo', async (req, res) => {
res.json(req.body)
})
app.listen(port, () => { app.listen(port, () => {

View file

@ -0,0 +1,50 @@
import express from "express";
import {GameRepository} from "./repositories/GameRepository.js";
import {GamePgPromiseRepository} from "./repositories/pgPromise/GamePgPromiseRepository.js";
import {Game} from "./model/Game.js";
import {body, CustomValidator, validationResult} from "express-validator";
import {UserRepository} from "./repositories/UserRepository.js";
import {UserPgPromiseRepository} from "./repositories/pgPromise/UserPgPromiseRepository.js";
export const gameRoute = express.Router()
gameRoute.use(express.json())
const userWithIdExists: CustomValidator = userId => {
try {
const userRepo: UserRepository = new UserPgPromiseRepository;
return userRepo.withIdExists(userId).then(exists => {
if (!exists) return Promise.reject("User does not exist");
});
} catch (error) {
console.log(error);
}
}
gameRoute.post(
'/add',
body('playtime')
.matches("([0-5]\\d:)?[0-5]\\d:[0-5]\\d"),
body('date')
.isDate(),
body('userId')
.isInt({min: 1})
.custom(userWithIdExists),
async (req, res) => {
try {
//region validate parameters
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
//endregion
let game: Game = req.body;
const gameRepo: GameRepository = new GamePgPromiseRepository;
const inserted: Game = await gameRepo.insert(game);
res.send(inserted);
} catch (error) {
// handle errors
console.log(error)
res.status(500).json({ errors: [{msg: "Internal server error"}]})
}
})

View file

@ -1,16 +1,15 @@
import express from 'express'; import express from 'express';
import {TimeLeaderboardManager} from "./manager/TimeLeaderboardManager.js"; import {TimeLeaderboardRepository} from "./repositories/TimeLeaderboardRepository.js";
import {TimeLeaderboardPgPromiseManager} from "./manager/pgPromise/TimeLeaderboardPgPromiseManager.js"; import {TimeLeaderboardPgPromiseRepository} from "./repositories/pgPromise/TimeLeaderboardPgPromiseRepository.js";
import {HighscoreLeaderboardPgPromiseManager} from "./manager/pgPromise/HighscoreLeaderboardPgPromiseManager.js"; import {HighscoreLeaderboardPgPromiseRepository} from "./repositories/pgPromise/HighscoreLeaderboardPgPromiseRepository.js";
import {HighscoreLeaderboardManager} from "./manager/HighscoreLeaderboardManager.js"; import {HighscoreLeaderboardRepository} from "./repositories/HighscoreLeaderboardRepository.js";
import {HighscoreLeaderboard, TimeLeaderboard} from "./model/Leaderboard.js"; import {HighscoreLeaderboard, TimeLeaderboard} from "./model/Leaderboard.js";
export const leaderboardRoute = express.Router() export const leaderboardRoute = express.Router()
leaderboardRoute.get('/highscore', async (req, res) => { leaderboardRoute.get('/highscore', async (req, res) => {
try { try {
const highscoreLeaderboardManager: HighscoreLeaderboardManager = new HighscoreLeaderboardPgPromiseManager; const highscoreLeaderboardManager: HighscoreLeaderboardRepository = new HighscoreLeaderboardPgPromiseRepository;
const highscoreLeaderboard: HighscoreLeaderboard = await highscoreLeaderboardManager.getAll(); const highscoreLeaderboard: HighscoreLeaderboard = await highscoreLeaderboardManager.getAll();
res.send(highscoreLeaderboard); res.send(highscoreLeaderboard);
} catch (error) { } catch (error) {
@ -22,7 +21,7 @@ leaderboardRoute.get('/highscore', async (req, res) => {
leaderboardRoute.get('/totalplaytime', async (req, res) => { leaderboardRoute.get('/totalplaytime', async (req, res) => {
try { try {
const timeLeaderboardManager: TimeLeaderboardManager = new TimeLeaderboardPgPromiseManager; const timeLeaderboardManager: TimeLeaderboardRepository = new TimeLeaderboardPgPromiseRepository;
const timeLeaderboard: TimeLeaderboard = await timeLeaderboardManager.getAll(); const timeLeaderboard: TimeLeaderboard = await timeLeaderboardManager.getAll();
res.send(timeLeaderboard); res.send(timeLeaderboard);
} catch (error) { } catch (error) {

View file

@ -1,6 +0,0 @@
import {HighscoreLeaderboard} from "../model/Leaderboard.js";
import {Manager} from "./Manager.js";
export abstract class HighscoreLeaderboardManager extends Manager<HighscoreLeaderboard>{
abstract getAll(): Promise<HighscoreLeaderboard>;
}

View file

@ -1,7 +0,0 @@
export abstract class Manager<T> {
//region getter&setter
protected abstract serialize(raw: any): T;
protected abstract deserialize(parsed: T): any;
//endregion
}

View file

@ -1,6 +0,0 @@
import {Manager} from "./Manager.js";
import {TimeLeaderboard} from "../model/Leaderboard.js";
export abstract class TimeLeaderboardManager extends Manager<TimeLeaderboard>{
abstract getAll(): Promise<TimeLeaderboard>;
}

View file

@ -1,29 +0,0 @@
import {HighscoreLeaderboardManager} from "../HighscoreLeaderboardManager.js";
import {HighscoreLeaderboard, LeaderboardEntry} from "../../model/Leaderboard.js";
import {Database} from "../../Database.js";
export class HighscoreLeaderboardPgPromiseManager extends HighscoreLeaderboardManager{
async getAll(): Promise<HighscoreLeaderboard> {
const raw: any = await Database.db.manyOrNone(
'SELECT * FROM lb_highscore INNER JOIN "user" ON user_id = id ORDER BY RANK;'
);
return this.serialize(raw);
}
//region serialization
protected serialize(raw: any): HighscoreLeaderboard {
return raw.map((item) => {
let newItem: LeaderboardEntry<number> = {
rank: item.rank,
username: item.name,
score: item.highscore,
}
return newItem;
});
}
protected deserialize(parsed: HighscoreLeaderboard): any {
throw new Error("Mthod not implemented.");
}
//endregion
}

View file

@ -0,0 +1,7 @@
export interface Game {
id?: number,
score: number,
playtime: string,
date: Date,
userId: number,
}

View file

@ -0,0 +1,5 @@
import {Game} from "../model/Game.js";
export abstract class GameRepository {
abstract insert(game: Game): Promise<Game>;
}

View file

@ -0,0 +1,5 @@
import {HighscoreLeaderboard} from "../model/Leaderboard.js";
export abstract class HighscoreLeaderboardRepository {
abstract getAll(): Promise<HighscoreLeaderboard>;
}

View file

@ -0,0 +1,5 @@
import {TimeLeaderboard} from "../model/Leaderboard.js";
export abstract class TimeLeaderboardRepository {
abstract getAll(): Promise<TimeLeaderboard>;
}

View file

@ -1,7 +1,6 @@
import {Manager} from "./Manager.js";
import {User} from "../model/User.js"; import {User} from "../model/User.js";
export abstract class UserManager extends Manager<User>{ export abstract class UserRepository {
abstract getById(id: number): Promise<User>; abstract getById(id: number): Promise<User>;
abstract getByName(name: string): Promise<User>; abstract getByName(name: string): Promise<User>;
abstract withIdExists(userId: number): Promise<boolean>; abstract withIdExists(userId: number): Promise<boolean>;

View file

@ -1,6 +1,5 @@
import {UserScores} from "../model/UserScores.js"; import {UserScores} from "../model/UserScores.js";
import {Manager} from "./Manager.js";
export abstract class UserScoresManager extends Manager<UserScores>{ export abstract class UserScoresRepository {
abstract getById(userId: number): Promise<UserScores>; abstract getById(userId: number): Promise<UserScores>;
} }

View file

@ -0,0 +1,24 @@
import {GameRepository} from "../GameRepository.js";
import {Game} from "../../model/Game.js";
import {Database} from "../../Database.js";
export class GamePgPromiseRepository extends GameRepository{
public async insert(game: Game): Promise<Game> {
const raw: any = await Database.db.oneOrNone(
'INSERT INTO game (score, playtime, date, user_id) VALUES ($(score), $(playtime), $(date), $(userId)) RETURNING *;',
game
);
return this.serialize(raw);
}
serialize(raw: any): Game {
return {
id: raw.id,
score: raw.score,
playtime: raw.playtime,
date: raw.date,
userId: raw.userId,
};
}
}

View file

@ -0,0 +1,23 @@
import {HighscoreLeaderboardRepository} from "../HighscoreLeaderboardRepository.js";
import {HighscoreLeaderboard, LeaderboardEntry} from "../../model/Leaderboard.js";
import {Database} from "../../Database.js";
export class HighscoreLeaderboardPgPromiseRepository extends HighscoreLeaderboardRepository {
async getAll(): Promise<HighscoreLeaderboard> {
const raw: any = await Database.db.manyOrNone(
'SELECT * FROM lb_highscore INNER JOIN "user" ON user_id = id ORDER BY RANK;'
);
return this.serialize(raw);
}
protected serialize(raw: any): HighscoreLeaderboard {
return raw.map((item) => {
let newItem: LeaderboardEntry<number> = {
rank: item.rank,
username: item.name,
score: item.highscore,
}
return newItem;
});
}
}

View file

@ -1,8 +1,8 @@
import {TimeLeaderboardManager} from "../TimeLeaderboardManager.js"; import {TimeLeaderboardRepository} from "../TimeLeaderboardRepository.js";
import {LeaderboardEntry, TimeLeaderboard} from "../../model/Leaderboard.js"; import {LeaderboardEntry, TimeLeaderboard} from "../../model/Leaderboard.js";
import {Database} from "../../Database.js"; import {Database} from "../../Database.js";
export class TimeLeaderboardPgPromiseManager extends TimeLeaderboardManager { export class TimeLeaderboardPgPromiseRepository extends TimeLeaderboardRepository {
async getAll(): Promise<TimeLeaderboard> { async getAll(): Promise<TimeLeaderboard> {
const raw: any = await Database.db.manyOrNone( const raw: any = await Database.db.manyOrNone(
'SELECT * FROM lb_total_playtime INNER JOIN "user" ON user_id = id ORDER BY RANK;' 'SELECT * FROM lb_total_playtime INNER JOIN "user" ON user_id = id ORDER BY RANK;'
@ -11,10 +11,6 @@ export class TimeLeaderboardPgPromiseManager extends TimeLeaderboardManager {
} }
//region serialization //region serialization
protected deserialize(parsed: TimeLeaderboard): any {
throw new Error("Method not implemented.")
}
protected serialize(raw: any): TimeLeaderboard { protected serialize(raw: any): TimeLeaderboard {
return raw.map((item) => { return raw.map((item) => {
let newItem: LeaderboardEntry<string> = { let newItem: LeaderboardEntry<string> = {

View file

@ -1,8 +1,8 @@
import {UserManager} from "../UserManager.js"; import {UserRepository} from "../UserRepository.js";
import {User} from "../../model/User.js"; import {User} from "../../model/User.js";
import {Database} from "../../Database.js"; import {Database} from "../../Database.js";
export class UserPgPromiseManager extends UserManager { export class UserPgPromiseRepository extends UserRepository {
async getById(id: number): Promise<User> { async getById(id: number): Promise<User> {
const raw = await Database.db.oneOrNone( const raw = await Database.db.oneOrNone(
'SELECT * FROM "user" WHERE id = $1;', id 'SELECT * FROM "user" WHERE id = $1;', id
@ -38,16 +38,10 @@ export class UserPgPromiseManager extends UserManager {
return this.serialize(raw); return this.serialize(raw);
} }
//region serialization
protected serialize(raw: any): User { protected serialize(raw: any): User {
return { return {
id: raw.id, id: raw.id,
name: raw.name name: raw.name
}; };
} }
protected deserialize(parsed: User): any {
throw new Error("Method not implemented.")
}
//endregion
} }

View file

@ -1,8 +1,8 @@
import {UserScoresManager} from "../UserScoresManager.js"; import {UserScoresRepository} from "../UserScoresRepository.js";
import {UserScores} from "../../model/UserScores.js"; import {UserScores} from "../../model/UserScores.js";
import {Database} from "../../Database.js"; import {Database} from "../../Database.js";
export class UserScoresPgPromiseManager extends UserScoresManager { export class UserScoresPgPromiseRepository extends UserScoresRepository {
public async getById(id: number): Promise<UserScores> { public async getById(id: number): Promise<UserScores> {
const raw = await Database.db.oneOrNone( const raw = await Database.db.oneOrNone(
'SELECT * FROM user_scores WHERE user_id = $1;', id 'SELECT * FROM user_scores WHERE user_id = $1;', id
@ -20,8 +20,4 @@ export class UserScoresPgPromiseManager extends UserScoresManager {
gamesPlayed: raw.games_played, gamesPlayed: raw.games_played,
}; };
} }
protected deserialize(parsed: UserScores): any {
throw new Error("Method not implemented.")
}
} }

View file

@ -1,13 +1,13 @@
import express from "express"; import express from "express";
import { body, param, validationResult } from 'express-validator'; import { body, param, validationResult } from 'express-validator';
import {UserScoresPgPromiseManager} from "./manager/pgPromise/UserScoresPgPromiseManager.js"; import {UserScoresPgPromiseRepository} from "./repositories/pgPromise/UserScoresPgPromiseRepository.js";
import {UserPgPromiseManager} from "./manager/pgPromise/UserPgPromiseManager.js"; import {UserPgPromiseRepository} from "./repositories/pgPromise/UserPgPromiseRepository.js";
import {UserManager} from "./manager/UserManager.js"; import {UserRepository} from "./repositories/UserRepository.js";
import {UserScoresManager} from "./manager/UserScoresManager.js"; import {UserScoresRepository} from "./repositories/UserScoresRepository.js";
import {User} from "./model/User.js"; import {User} from "./model/User.js";
export const userRoute = express.Router() export const userRoute = express.Router()
userRoute.use(express.json())
userRoute.post( userRoute.post(
'/register', '/register',
@ -23,9 +23,8 @@ userRoute.post(
return res.status(400).json({ errors: errors.array() }); return res.status(400).json({ errors: errors.array() });
} }
//endregion //endregion
const username: string = req.body.name; const username: string = req.body.name;
const userManager: UserManager = new UserPgPromiseManager(); const userManager: UserRepository = new UserPgPromiseRepository();
// check if username already exists // check if username already exists
if (await userManager.withNameExists(username)) { if (await userManager.withNameExists(username)) {
@ -53,7 +52,7 @@ userRoute.get('/:userId/scores',
//endregion //endregion
const userId: number = req.params.userId; const userId: number = req.params.userId;
const userManager: UserManager = new UserPgPromiseManager; const userManager: UserRepository = new UserPgPromiseRepository;
try { try {
// check if user with given id exists // check if user with given id exists
@ -61,7 +60,7 @@ userRoute.get('/:userId/scores',
return res.status(400).json({ errors: [{msg: `User with id ${userId} does not exist.`}] }) return res.status(400).json({ errors: [{msg: `User with id ${userId} does not exist.`}] })
} }
// get & return data // get & return data
const userScoresManager: UserScoresManager = new UserScoresPgPromiseManager; const userScoresManager: UserScoresRepository = new UserScoresPgPromiseRepository;
const userScores = await userScoresManager.getById(userId); const userScores = await userScoresManager.getById(userId);
res.json(userScores); res.json(userScores);
} catch (error) { } catch (error) {