Skip to content
Snippets Groups Projects
Commit 787a1c02 authored by Samuli Virtapohja's avatar Samuli Virtapohja
Browse files

merge development to access

parents f7d55d7e 39308d36
No related branches found
No related tags found
2 merge requests!59Development to master,!18Join game
Showing
with 936 additions and 455 deletions
......@@ -38,10 +38,37 @@ Name .env.example to .env and ormconfig.json.example to ormconfig.json and add v
**.env names are case sensitive!**
Needed postgresql modules:
**Configuring a database with Docker for this application:**
```bash
# Inside the database that you're connecting to:
CREATE EXTENSION "uuid-ossp";
# first run
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
......
......@@ -9,7 +9,7 @@ services:
ports:
- 5000:5000
postgres:
image: postgres
image: mdillon/postgis
volumes:
- /home/postgres:/var/lib/postgresql/data
ports:
......
This diff is collapsed.
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 { Module } from '@nestjs/common';
import { APP_FILTER, APP_INTERCEPTOR, APP_GUARD } from '@nestjs/core';
import { TypeOrmModule } from "@nestjs/typeorm";
import { Connection } from "typeorm";
import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { Connection } from 'typeorm';
import { UserModule } from './user/user.module';
import { HttpErrorFilter } from './shared/http-error.filter';
import { LoggingInterceptor } from './shared/logging.interceptor';
import { MapMarkerModule } from './mapmarkers/mapmarkers.module';
import { HttpErrorFilter } from './shared/http-error.filter';
import { RolesGuard } from './shared/roles.guard';
/*
Appmodule is the core of the system.
forRoot imports database data from ormconfig.json file
All the modules contain their respective controllers and service
*/
import { NotificationModule } from './notifications/notifications.module';
import { GameModule } from './game/game.module';
@Module({
imports: [TypeOrmModule.forRoot(), UserModule, MapMarkerModule],
imports: [
TypeOrmModule.forRoot(),
UserModule,
MapMarkerModule,
GameModule,
NotificationModule,
],
controllers: [AppController],
providers: [
AppService, {
AppService,
{
provide: APP_FILTER,
useClass: HttpErrorFilter,
useClass: HttpErrorFilter,
},
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor,
},{
provide: APP_GUARD,
useClass: RolesGuard,
}
},
],
})
export class AppModule {
......
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, Put } 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);
}
@Put(':id')
@UseGuards(new AuthGuard())
@UsePipes(new ValidationPipe())
async editGame(@Param('id') id: string, @Body() body: GameDTO) {
return this.gameservice.editGame(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;
center: JSON;
//@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') center: JSON;
@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);
return {
"message": "New game added"
};
}
// edit already created game
async editGame(gamedata) {
/* // 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 {
"message": "Game updated"
}
}
// 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;
}
}
......@@ -18,6 +18,7 @@ export class MapMarkerService {
// insert markers
async insertLocation(personId: string, data: MapMarkerDTO) {
try {
//get functions runtime as timestamp
data.timestamp = new Date(Date.now()).toLocaleString();
//check from database for the user who uploads the data
......
......@@ -4,12 +4,11 @@ import { MapMarkerService } from './mapmarker.service';
import { MapMarkerDTO } from './mapmarker.dto';
import { AuthGuard } from '../shared/auth.guard';
import { User } from '../user/user.decorator';
import { Roles } from '../shared/roles.decorator';
@Controller('mapmarkers')
export class MapMarkersController {
constructor(private mapmarkerservice: MapMarkerService){}
// Insert figure location, needs "authorization" header with valid Bearer token and content-type json
@Put('insertLocation')
@UseGuards(new AuthGuard())
......@@ -40,7 +39,6 @@ export class MapMarkersController {
}
}
@Get('testroles')
@Roles('gm')
async testroles(){
try {
return this.mapmarkerservice.test();
......
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from "typeorm";
// temporary table for warning notifications
@Entity('Notifications')
export class NotificationEntity {
@PrimaryGeneratedColumn('uuid') id: string;
@Column({type: 'text'}) message: string;
@CreateDateColumn() issued: Date;
// TODO:
// when game creation has been implemented, add logic so that the notifications are tied to games
}
\ No newline at end of file
import {
SubscribeMessage,
WebSocketGateway,
WebSocketServer,
OnGatewayInit,
OnGatewayConnection,
OnGatewayDisconnect,
} from '@nestjs/websockets';
import { Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Server, Socket } from 'socket.io';
import { Repository } from 'typeorm';
import { NotificationEntity } from './notification.entity';
@WebSocketGateway()
export class NotificationGateway
implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
constructor(
//create references to tables as repositories
@InjectRepository(NotificationEntity)
private notificationRepository: Repository<NotificationEntity>,
) {}
@WebSocketServer()
server: Server;
// for debugging: starting server, accepting connection, terminating connection
private logger: Logger = new Logger('NotificationGateway');
afterInit(server: Server) {
this.logger.log('Server has started');
}
handleConnection(client: Socket, ...args: any[]) {
this.logger.log(`Client ${client.id} connected`);
}
handleDisconnect(client: Socket) {
this.logger.log(`Client ${client.id} disconnected`);
}
// notifications for when game needs to be paused / stopped
@SubscribeMessage('shutItDown')
async handleMessage(client: Socket, text: string) {
this.logger.log(text);
// send the message to all clients listening to warningToPlayers branch
this.server.emit('warningToPlayers', text);
// create entity properties
const message = await this.notificationRepository.create({ message: text });
// insert created entity NOTE: insert method doesn't check for duplicates.
await this.notificationRepository.insert(message);
}
}
import { Module } from '@nestjs/common';
import { NotificationGateway } from './notifications.gateway';
import { NotificationEntity } from './notification.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [TypeOrmModule.forFeature([NotificationEntity])],
providers: [NotificationGateway],
})
export class NotificationModule {}
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
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