Skip to content
Snippets Groups Projects
Commit 9085bca7 authored by L4721's avatar L4721
Browse files

Merge branch 'gamecreation' into 'Development'

Gamecreation

See merge request !10
parents 93c4a04b 8b26709d
No related branches found
No related tags found
2 merge requests!59Development to master,!10Gamecreation
...@@ -38,10 +38,37 @@ Name .env.example to .env and ormconfig.json.example to ormconfig.json and add v ...@@ -38,10 +38,37 @@ Name .env.example to .env and ormconfig.json.example to ormconfig.json and add v
**.env names are case sensitive!** **.env names are case sensitive!**
Needed postgresql modules: **Configuring a database with Docker for this application:**
```bash ```bash
# Inside the database that you're connecting to: # first run
CREATE EXTENSION "uuid-ossp"; docker run --name postgis -p 5432:5432 -d -v /home/postgres:/var/lib/postgresql/data mdillon/postgis
# stopping the container
docker stop postgis
# starting the container
docker start postgis
# you can also have the container boot on computer startup with --restart option
--restart=always
# for example:
docker run --name postgis -p 5432:5432 -d -v /home/postgres:/var/lib/postgresql/data --restart=always mdillon/postgis
# starting bash inside the container
docker exec -it postgis bash
# connecting to the postgis service inside docker
psql -U postgres
# Inside the database:
# Creating database
create database ehasa;
# Connect to created database
\c ehasa;
# Create user for database
create user ehasa
alter user ehasa with encrypted password 'salasana';
# Give privileges to use database
grant all privileges on database ehasa to ehasa;
# Needed extensions
create extension "uuid-ossp";
# exit postgis
\q
``` ```
## Running the app ## Running the app
......
...@@ -9,7 +9,7 @@ services: ...@@ -9,7 +9,7 @@ services:
ports: ports:
- 5000:5000 - 5000:5000
postgres: postgres:
image: postgres image: mdillon/postgis
volumes: volumes:
- /home/postgres:/var/lib/postgresql/data - /home/postgres:/var/lib/postgresql/data
ports: ports:
......
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get<AppController>(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
@Injectable() @Injectable()
export class AppService { export class AppService {}
getHello(): string {
return 'Hello World!';
}
}
import { Test, TestingModule } from '@nestjs/testing';
import { GameController } from './game.controller';
describe('Game Controller', () => {
let controller: GameController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [GameController],
}).compile();
controller = module.get<GameController>(GameController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
import { Controller, Post, UseGuards, Body, Get, Param, UsePipes } from '@nestjs/common';
import { GameService } from './game.service';
import { AuthGuard } from '../shared/auth.guard';
import { User } from '../user/user.decorator';
import { GameDTO } from './game.dto';
import { ValidationPipe } from '../shared/validation.pipe';
@Controller('game')
export class GameController {
constructor(private gameservice: GameService) { }
@Post('new')
@UseGuards(new AuthGuard())
@UsePipes(new ValidationPipe())
async newGame(@User('id') person, @Body() body: GameDTO ) {
return this.gameservice.createNewGame(person, body);
}
@UseGuards(new AuthGuard())
@UsePipes(new ValidationPipe())
@Post(':id')
async joinGame(@User('id') person, @Param('id') id: string, @Body() password: string) {
return this.gameservice.joinGame(person, id, password);
}
@Get('listgames')
async listGames() {
return this.gameservice.listGames();
}
@Get(':id')
async returnGameInfo(@Param('id') id: string) {
return this.gameservice.returnGameInfo(id);
}
}
import {
IsString,
IsDateString,
IsJSON,
IsNotEmpty,
Length,
IsArray,
Validate,
} from 'class-validator';
import { ArrayLength } from 'src/shared/array-validation';
export class GameDTO {
// uses class-validator built in validations
// see https://github.com/typestack/class-validator
@IsString()
@IsNotEmpty()
@Length(2, 31)
name: string;
@IsString()
@IsNotEmpty()
@Length(1, 255)
desc: string;
//@IsJSON()
// doesn't accept with IsJSON, WIP to get validation for map
map: JSON;
@IsDateString()
@IsNotEmpty()
startdate: string;
@IsDateString()
@IsNotEmpty()
enddate: string;
@IsArray()
@IsNotEmpty()
@Length(5, 15, {
each: true,
})
// custom validation for array length (arr>min, arr<max)
@Validate(ArrayLength, [4, 8])
passwords: string[];
factions: FactionDTO[];
}
export class FactionDTO {
@IsString()
@IsNotEmpty()
@Length(2, 15)
name: string;
id: string;
game: GameDTO;
}
import {
Entity,
Column,
PrimaryGeneratedColumn,
ManyToOne,
OneToMany,
Timestamp,
} from 'typeorm';
import { PersonEntity } from 'src/user/user.entity';
// table that stores all created games
@Entity('Game')
export class GameEntity {
@PrimaryGeneratedColumn('uuid') id: string;
@Column('text') name: string;
@Column('text') desc: string;
@Column('json') map: JSON;
@Column('timestamp') startdate: Timestamp;
@Column('timestamp') enddate: Timestamp;
@Column("text", {array: true}) passwords: string[];
@OneToMany(type => FactionEntity, faction => faction.game)
factions: FactionEntity[];
@OneToMany(type => Game_PersonEntity, game_persons => game_persons.game)
game_persons: Game_PersonEntity[];
gameObject() {
const { id, name } = this;
return { id, name };
}
}
// table that stores all factions created for games
@Entity('Faction')
export class FactionEntity {
@PrimaryGeneratedColumn('uuid') id: string;
@Column('text') name: string;
@ManyToOne(type => GameEntity, game => game.factions)
game: GameEntity;
@OneToMany(type => Game_PersonEntity, game_persons => game_persons.faction)
game_persons: Game_PersonEntity[];
}
// table that stores players associated with particular game
@Entity('Game_Person')
export class Game_PersonEntity {
@PrimaryGeneratedColumn('uuid') gameId: string;
@ManyToOne(type => FactionEntity, faction => faction.game_persons)
faction: FactionEntity;
@ManyToOne(type => GameEntity, game => game.game_persons)
game: GameEntity;
@ManyToOne(type => PersonEntity, person => person.game_persons)
person: PersonEntity;
/*
@ManyToOne(type => PersonRoleEntity, person_role => person_role.game_persons)
person_role: PersonRoleEntity;
@ManyToMany(type => CoordinateEntity, game_person_coordinates => game_person_coordinates.game_persons)
game_person_coordinates: CoordinateEntity[]; */
}
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { GameController } from './game.controller';
import { GameService } from './game.service';
import { GameEntity, FactionEntity, Game_PersonEntity } from './game.entity';
import { PersonEntity } from 'src/user/user.entity';
@Module({
imports: [TypeOrmModule.forFeature([GameEntity, FactionEntity, Game_PersonEntity, PersonEntity])],
controllers: [GameController],
providers: [GameService]
})
export class GameModule {}
import { Test, TestingModule } from '@nestjs/testing';
import { GameService } from './game.service';
describe('GameService', () => {
let service: GameService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [GameService],
}).compile();
service = module.get<GameService>(GameService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
import { Injectable, Logger, HttpException, HttpStatus } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, In } from 'typeorm';
import { GameEntity, FactionEntity, Game_PersonEntity } from './game.entity';
import { GameDTO } from './game.dto';
import { PersonEntity } from '../user/user.entity';
@Injectable()
export class GameService {
constructor(
@InjectRepository(GameEntity)
private gameRepository: Repository<GameEntity>,
@InjectRepository(FactionEntity)
private factionRepository: Repository<FactionEntity>,
@InjectRepository(PersonEntity)
private personRepository: Repository<PersonEntity>,
@InjectRepository(Game_PersonEntity)
private game_PersonRepository: Repository<Game_PersonEntity>,
) {}
// create a new game
async createNewGame(personId: string, gameData: GameDTO) {
// checks if a game with the same name exists already
const { name } = gameData;
if (await this.gameRepository.findOne({ where: { name } })) {
throw new HttpException('Game already exists', HttpStatus.BAD_REQUEST);
}
// else add the game to the database
const game = await this.gameRepository.create({
...gameData,
factions: gameData.factions,
});
await this.gameRepository.insert(game);
// get the id of the game created to pass it to factions table
const gameid = await this.gameRepository.findOne({
where: { name: gameData.name },
});
gameData.factions.map(async faction => {
let name = await this.factionRepository.create({
...faction,
game: gameid,
});
await this.factionRepository.insert(name);
});
return 'success';
}
// checks the password, creates an entry in GamePerson table with associated role&faction
async joinGame(person, gameId, json) {
const user = await this.personRepository.findOne({
where: { id: person },
});
const game = await this.gameRepository.findOne({ where: { id: gameId } });
const index = game.passwords.indexOf(json.password);
// create game_Person entry
/* const gamePerson = await this.game_PersonRepository.create({
faction,
gameId,
person,
});
*/
return 'WIP';
}
// returns name and id of each game
async listGames() {
const games = await this.gameRepository.find({ relations: ['factions'] });
return games.map(game => {
return game.gameObject();
});
}
// returns information about a game identified by id
async returnGameInfo(id: string) {
const game = await this.gameRepository.findOne({
where: { id: id },
relations: ['factions'],
});
return game;
}
}
import {ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments} from "class-validator";
import { Logger } from "@nestjs/common";
// validates array length
@ValidatorConstraint({ name: "arrayLength", async: true })
export class ArrayLength implements ValidatorConstraintInterface {
validate(array: string[], args: ValidationArguments) {
Logger.log(array.length)
return array.length > args.constraints[0] && array.length < args.constraints[1]; // for async validations you must return a Promise<boolean> here
}
defaultMessage(args: ValidationArguments) { // here you can provide default error message if validation failed
return "Please input all passwords";
}
}
\ No newline at end of file
import { createParamDecorator } from "@nestjs/common"; import { createParamDecorator } from "@nestjs/common";
// used to pass user information to controllers
export const User = createParamDecorator((data, req) => { export const User = createParamDecorator((data, req) => {
return data ? req.user[data] : req.user; return data ? req.user[data] : req.user;
}) })
\ No newline at end of file
import { Entity, Column, PrimaryGeneratedColumn, BeforeInsert, OneToMany } from 'typeorm'; import { Entity, Column, PrimaryGeneratedColumn, BeforeInsert, OneToMany } from 'typeorm';
import * as bcrypt from 'bcryptjs'; import * as bcrypt from 'bcryptjs';
import * as jwt from 'jsonwebtoken'; import * as jwt from 'jsonwebtoken';
import { MapMarkerEntity } from '../mapmarkers/mapmarker.entity'; import { MapMarkerEntity } from 'src/mapmarkers/mapmarker.entity';
import { Game_PersonEntity } from 'src/game/game.entity';
@Entity('Person') @Entity('Person')
export class PersonEntity { export class PersonEntity {
...@@ -10,12 +11,17 @@ export class PersonEntity { ...@@ -10,12 +11,17 @@ export class PersonEntity {
@Column('text') password: string; @Column('text') password: string;
@OneToMany(type => MapMarkerEntity, marker => marker.player) @OneToMany(type => MapMarkerEntity, marker => marker.player)
markers: MapMarkerEntity[]; markers: MapMarkerEntity[];
@OneToMany(type => Game_PersonEntity, game_persons => game_persons.person)
game_persons: Game_PersonEntity[];
// hashes the password before inserting it to database
@BeforeInsert() @BeforeInsert()
async hashPassword() { async hashPassword() {
this.password = await bcrypt.hash(this.password, 10); this.password = await bcrypt.hash(this.password, 10);
} }
// returns username and associated token
tokenObject() { tokenObject() {
const {name, token} = this; const {name, token} = this;
return {name, token}; return {name, token};
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment