added /game/add, API hopefully finished
This commit is contained in:
parent
fff55edf79
commit
5f9a484285
19 changed files with 143 additions and 100 deletions
|
|
@ -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, () => {
|
||||||
|
|
|
||||||
50
backend/api/src/gameRoute.ts
Normal file
50
backend/api/src/gameRoute.ts
Normal 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"}]})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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>;
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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>;
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
7
backend/api/src/model/Game.ts
Normal file
7
backend/api/src/model/Game.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
export interface Game {
|
||||||
|
id?: number,
|
||||||
|
score: number,
|
||||||
|
playtime: string,
|
||||||
|
date: Date,
|
||||||
|
userId: number,
|
||||||
|
}
|
||||||
5
backend/api/src/repositories/GameRepository.ts
Normal file
5
backend/api/src/repositories/GameRepository.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import {Game} from "../model/Game.js";
|
||||||
|
|
||||||
|
export abstract class GameRepository {
|
||||||
|
abstract insert(game: Game): Promise<Game>;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
import {HighscoreLeaderboard} from "../model/Leaderboard.js";
|
||||||
|
|
||||||
|
export abstract class HighscoreLeaderboardRepository {
|
||||||
|
abstract getAll(): Promise<HighscoreLeaderboard>;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
import {TimeLeaderboard} from "../model/Leaderboard.js";
|
||||||
|
|
||||||
|
export abstract class TimeLeaderboardRepository {
|
||||||
|
abstract getAll(): Promise<TimeLeaderboard>;
|
||||||
|
}
|
||||||
|
|
@ -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>;
|
||||||
|
|
@ -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>;
|
||||||
}
|
}
|
||||||
|
|
@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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> = {
|
||||||
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
@ -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.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue