From 6e88e9a8a1158ad28450124021cfd6294c63ab44 Mon Sep 17 00:00:00 2001 From: L4168 <L4168@student.jamk.fi> Date: Thu, 27 Jun 2019 11:42:55 +0300 Subject: [PATCH 01/12] added relation to task entity --- src/game/game.entity.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/game/game.entity.ts b/src/game/game.entity.ts index 3b36297..036b31c 100644 --- a/src/game/game.entity.ts +++ b/src/game/game.entity.ts @@ -12,10 +12,8 @@ import { import { PersonEntity } from '../user/user.entity'; import { GameGroupEntity } from './group.entity'; import { FactionEntity } from './faction.entity'; -import { - MapDrawingEntity, - Game_Person_MapDrawingEntity, -} from './coordinate.entity'; +import { MapDrawingEntity } from './coordinate.entity'; +import { TaskEntity } from '../task/task.entity'; // table that stores all created games @Entity('Game') @@ -40,6 +38,8 @@ export class GameEntity { objective_points => objective_points.game, ) objective_points: ObjectivePointEntity[]; + @OneToMany(type => TaskEntity, tasks => tasks.taskGame) + tasks: TaskEntity[]; gameObject() { const { id, name } = this; -- GitLab From 29305d7545637b4cce3f8e4f3f9da0417ee8319c Mon Sep 17 00:00:00 2001 From: L4168 <L4168@student.jamk.fi> Date: Thu, 27 Jun 2019 11:43:51 +0300 Subject: [PATCH 02/12] custom validation for uuid and null --- src/shared/uuid.validation.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/shared/uuid.validation.ts diff --git a/src/shared/uuid.validation.ts b/src/shared/uuid.validation.ts new file mode 100644 index 0000000..c8c791a --- /dev/null +++ b/src/shared/uuid.validation.ts @@ -0,0 +1,19 @@ +import { + ValidatorConstraint, + ValidatorConstraintInterface, + ValidationArguments, + Validator, +} from 'class-validator'; + +// check if input is null or valid uuid +@ValidatorConstraint({ name: 'uuid', async: true }) +export class Uuid implements ValidatorConstraintInterface { + validate(uuid: string, args: ValidationArguments) { + const validator = new Validator(); + return validator.isUUID(uuid, '4') || uuid == null; // for async validations you must return a Promise<boolean> here + } + + defaultMessage(args: ValidationArguments) { + return 'Not valid uuid'; + } +} -- GitLab From 39da24f7177b783c0e3dbb8a048d51d2a3ebadb8 Mon Sep 17 00:00:00 2001 From: L4168 <L4168@student.jamk.fi> Date: Thu, 27 Jun 2019 11:44:51 +0300 Subject: [PATCH 03/12] added route for edit-task --- src/task/task.controller.ts | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/task/task.controller.ts b/src/task/task.controller.ts index 6f272a0..db6be91 100644 --- a/src/task/task.controller.ts +++ b/src/task/task.controller.ts @@ -10,32 +10,27 @@ import { import { TaskService } from './task.service'; import { Roles } from '../shared/roles.decorator'; import { ValidationPipe } from '../shared/validation.pipe'; -import { TaskDTO } from './task.dto'; +import { CreateTaskDTO, EditTaskDTO } from './task.dto'; @Controller('task') export class TaskController { constructor(private taskService: TaskService) {} - /* @Post('new') - @UseGuards(new AuthGuard()) - @UsePipes(new ValidationPipe()) - async newGame(@User('id') person, @Body() body: GameDTO) { - return this.gameservice.createNewGame(person, body); - } - - @Put(':id') - @Roles('admin') - @UsePipes(new ValidationPipe()) - async editGame(@Param('id') id: string, @Body() body: GameDTO) { - return this.gameservice.editGame(id, body); - } */ - // creates a new task if the user has admin role in the game // :id is the id of the game @Post('new-task/:id') - //@Roles('admin') + @Roles('admin') @UsePipes(new ValidationPipe()) - async newTask(@Param('id') id: string, @Body() task: TaskDTO) { + async newTask(@Param('id') id: string, @Body() task: CreateTaskDTO) { return this.taskService.newTask(task); } + + // edits a created task if the user has admin role in the game + // :id is the id of the game + @Post('edit-task/:id') + @Roles('admin') + @UsePipes(new ValidationPipe()) + async editTask(@Param('id') id: string, @Body() data: EditTaskDTO) { + return this.taskService.editTask(data); + } } -- GitLab From 3bd5bcb383bddbd5299d29c844de0a87516c5977 Mon Sep 17 00:00:00 2001 From: L4168 <L4168@student.jamk.fi> Date: Thu, 27 Jun 2019 11:45:09 +0300 Subject: [PATCH 04/12] validation for services --- src/task/task.dto.ts | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/task/task.dto.ts b/src/task/task.dto.ts index aa3238f..723ddd7 100644 --- a/src/task/task.dto.ts +++ b/src/task/task.dto.ts @@ -1,30 +1,38 @@ import { IsString, Length, - IsNumber, IsBoolean, - Min, - Max, + Validate, + IsUUID, + Equals, } from 'class-validator'; -import { FactionEntity } from 'src/game/faction.entity'; +import { FactionEntity } from '../game/faction.entity'; +import { Uuid } from '../shared/uuid.validation'; +import { GameEntity } from 'src/game/game.entity'; -export class TaskDTO { +export class CreateTaskDTO { @IsString() @Length(3, 31) taskName: string; @IsString() @Length(0, 255) taskDescription: string; - @IsNumber() - @Min(1) - @Max(99) - taskScore: number; - @IsString() - @Length(3, 31) - taskWinner?: string; @IsBoolean() taskIsActive: boolean; - // faction unique id - @IsString() + @Validate(Uuid) faction: FactionEntity; + @Equals(null) + taskWinner: FactionEntity; + // faction unique id + @IsUUID('4') + taskGame: GameEntity; +} + +export class EditTaskDTO { + @IsUUID('4') + taskId: string; + @IsUUID('4') + taskWinner: FactionEntity; + @IsUUID('4') + taskGame: GameEntity; } -- GitLab From a387343eed56a478723aac356b47d920e9a39d17 Mon Sep 17 00:00:00 2001 From: L4168 <L4168@student.jamk.fi> Date: Thu, 27 Jun 2019 11:45:32 +0300 Subject: [PATCH 05/12] updated table to match db design --- src/task/task.entity.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/task/task.entity.ts b/src/task/task.entity.ts index 1e74701..e6fd618 100644 --- a/src/task/task.entity.ts +++ b/src/task/task.entity.ts @@ -1,15 +1,25 @@ -import { PrimaryGeneratedColumn, Column, Entity, ManyToOne } from 'typeorm'; +import { + PrimaryGeneratedColumn, + Column, + Entity, + ManyToOne, + JoinColumn, +} from 'typeorm'; import { FactionEntity } from 'src/game/faction.entity'; +import { GameEntity } from 'src/game/game.entity'; @Entity('Task') export class TaskEntity { @PrimaryGeneratedColumn('uuid') taskId: string; @Column({ type: 'text' }) taskName: string; @Column({ type: 'text' }) taskDescription: string; - @Column({ type: 'float' }) taskScore: number; - @Column({ type: 'text' }) taskWinner: string; @Column({ type: 'bool' }) taskIsActive: boolean; @ManyToOne(type => FactionEntity, faction => faction.factionId) faction: FactionEntity; + @ManyToOne(type => FactionEntity, faction => faction.factionId) + taskWinner: FactionEntity; + @ManyToOne(type => GameEntity, game => game.id) + @JoinColumn({ name: 'taskGame' }) + taskGame: GameEntity; } -- GitLab From d640fafc6bf4c527d3594fc0c5097eefd8420903 Mon Sep 17 00:00:00 2001 From: L4168 <L4168@student.jamk.fi> Date: Thu, 27 Jun 2019 11:45:44 +0300 Subject: [PATCH 06/12] imported FactionEntity --- src/task/task.module.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/task/task.module.ts b/src/task/task.module.ts index ea32148..b71b4d9 100644 --- a/src/task/task.module.ts +++ b/src/task/task.module.ts @@ -4,9 +4,10 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { TaskService } from './task.service'; import { TaskController } from './task.controller'; import { TaskEntity } from './task.entity'; +import { FactionEntity } from 'src/game/faction.entity'; @Module({ - imports: [TypeOrmModule.forFeature([TaskEntity])], + imports: [TypeOrmModule.forFeature([TaskEntity, FactionEntity])], controllers: [TaskController], providers: [TaskService], }) -- GitLab From 3eafdd7f406d6c6d28644defb80874d83a2ddfb0 Mon Sep 17 00:00:00 2001 From: L4168 <L4168@student.jamk.fi> Date: Thu, 27 Jun 2019 11:46:11 +0300 Subject: [PATCH 07/12] updated newTask and created editTask service --- src/task/task.service.ts | 42 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/task/task.service.ts b/src/task/task.service.ts index 830e615..7df6dee 100644 --- a/src/task/task.service.ts +++ b/src/task/task.service.ts @@ -2,20 +2,58 @@ import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import { Repository } from 'typeorm'; import { InjectRepository } from '@nestjs/typeorm'; import { TaskEntity } from './task.entity'; -import { TaskDTO } from './task.dto'; +import { CreateTaskDTO, EditTaskDTO } from './task.dto'; +import { FactionEntity } from '../game/faction.entity'; @Injectable() export class TaskService { constructor( @InjectRepository(TaskEntity) private taskRepository: Repository<TaskEntity>, + @InjectRepository(FactionEntity) + private factionRepository: Repository<FactionEntity>, ) {} - async newTask(task: TaskDTO) { + async newTask(task: CreateTaskDTO) { + // check if is not null, check that the faction exists in the game + if ( + task.faction != null && + !(await this.factionRepository.findOne({ + factionId: task.faction.toString(), + game: task.taskGame, + })) + ) { + throw new HttpException('Faction not found', HttpStatus.BAD_REQUEST); + } const createdTask = await this.taskRepository.create(task); + console.log(createdTask); await this.taskRepository.insert(createdTask); return { message: 'Task added', }; } + + async editTask(data: EditTaskDTO) { + const task = await this.taskRepository.findOne(data.taskId); + console.log(task); + // checks if task is already closed + if (!task.taskIsActive) { + throw new HttpException('Task is not active', HttpStatus.BAD_REQUEST); + } + // checks if faction is valid + if ( + !(await this.factionRepository.findOne({ + factionId: data.taskWinner.toString(), + game: data.taskGame, + })) + ) { + throw new HttpException('Faction not found', HttpStatus.BAD_REQUEST); + } + task.taskWinner = data.taskWinner; + task.taskIsActive = false; + await this.taskRepository.save(task); + return { + message: 'Task updated and closed', + }; + } } -- GitLab From 5a2f2dc4bafe8b932a4689e4231b3af0a7778b44 Mon Sep 17 00:00:00 2001 From: L4168 <L4168@student.jamk.fi> Date: Thu, 27 Jun 2019 11:56:01 +0300 Subject: [PATCH 08/12] customvalidation in one file, added rolevalidator --- src/shared/array-validation.ts | 17 ----------------- ...{uuid.validation.ts => custom-validation.ts} | 13 +++++++++++++ src/task/task.dto.ts | 4 ++-- 3 files changed, 15 insertions(+), 19 deletions(-) delete mode 100644 src/shared/array-validation.ts rename src/shared/{uuid.validation.ts => custom-validation.ts} (59%) diff --git a/src/shared/array-validation.ts b/src/shared/array-validation.ts deleted file mode 100644 index 494ab42..0000000 --- a/src/shared/array-validation.ts +++ /dev/null @@ -1,17 +0,0 @@ -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 diff --git a/src/shared/uuid.validation.ts b/src/shared/custom-validation.ts similarity index 59% rename from src/shared/uuid.validation.ts rename to src/shared/custom-validation.ts index c8c791a..a920e71 100644 --- a/src/shared/uuid.validation.ts +++ b/src/shared/custom-validation.ts @@ -17,3 +17,16 @@ export class Uuid implements ValidatorConstraintInterface { return 'Not valid uuid'; } } + +// checks if role is valid +@ValidatorConstraint({ name: 'roleValidation', async: true }) +export class RoleValidation implements ValidatorConstraintInterface { + validate(role: string, args: ValidationArguments) { + const validRoles = ['admin', 'soldier', 'factionleader']; + return validRoles.includes(role); + } + + defaultMessage(args: ValidationArguments) { + return 'Not valid uuid'; + } +} diff --git a/src/task/task.dto.ts b/src/task/task.dto.ts index 723ddd7..3a363b5 100644 --- a/src/task/task.dto.ts +++ b/src/task/task.dto.ts @@ -7,8 +7,8 @@ import { Equals, } from 'class-validator'; import { FactionEntity } from '../game/faction.entity'; -import { Uuid } from '../shared/uuid.validation'; -import { GameEntity } from 'src/game/game.entity'; +import { GameEntity } from '../game/game.entity'; +import { Uuid } from '../shared/custom-validation'; export class CreateTaskDTO { @IsString() -- GitLab From 151f84780e9b854b24a1ab46aa49ded27f52f09a Mon Sep 17 00:00:00 2001 From: L4168 <L4168@student.jamk.fi> Date: Thu, 27 Jun 2019 12:41:53 +0300 Subject: [PATCH 09/12] added fetch tasks service, updated socket --- src/task/task.controller.ts | 11 ++++++++++- src/task/task.module.ts | 9 +++++++-- src/task/task.service.ts | 21 +++++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/task/task.controller.ts b/src/task/task.controller.ts index db6be91..2e04789 100644 --- a/src/task/task.controller.ts +++ b/src/task/task.controller.ts @@ -7,10 +7,13 @@ import { UseGuards, Param, } from '@nestjs/common'; + import { TaskService } from './task.service'; +import { CreateTaskDTO, EditTaskDTO } from './task.dto'; +import { AuthGuard } from '../shared/auth.guard'; import { Roles } from '../shared/roles.decorator'; import { ValidationPipe } from '../shared/validation.pipe'; -import { CreateTaskDTO, EditTaskDTO } from './task.dto'; +import { User } from 'src/user/user.decorator'; @Controller('task') export class TaskController { @@ -33,4 +36,10 @@ export class TaskController { async editTask(@Param('id') id: string, @Body() data: EditTaskDTO) { return this.taskService.editTask(data); } + + @Get('get-tasks/:id') + @UseGuards(new AuthGuard()) + async fetchTasks(@User('id') person, @Param('id') id: string) { + return this.taskService.fetchTasks(person, id); + } } diff --git a/src/task/task.module.ts b/src/task/task.module.ts index b71b4d9..12112f9 100644 --- a/src/task/task.module.ts +++ b/src/task/task.module.ts @@ -4,10 +4,15 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { TaskService } from './task.service'; import { TaskController } from './task.controller'; import { TaskEntity } from './task.entity'; -import { FactionEntity } from 'src/game/faction.entity'; +import { FactionEntity } from '../game/faction.entity'; +import { Game_PersonEntity } from '../game/game.entity'; +import { NotificationModule } from '../notifications/notifications.module'; @Module({ - imports: [TypeOrmModule.forFeature([TaskEntity, FactionEntity])], + imports: [ + TypeOrmModule.forFeature([TaskEntity, FactionEntity, Game_PersonEntity]), + NotificationModule, + ], controllers: [TaskController], providers: [TaskService], }) diff --git a/src/task/task.service.ts b/src/task/task.service.ts index 7df6dee..9d4fe9a 100644 --- a/src/task/task.service.ts +++ b/src/task/task.service.ts @@ -1,9 +1,12 @@ import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import { Repository } from 'typeorm'; import { InjectRepository } from '@nestjs/typeorm'; + import { TaskEntity } from './task.entity'; import { CreateTaskDTO, EditTaskDTO } from './task.dto'; import { FactionEntity } from '../game/faction.entity'; +import { Game_PersonEntity } from '../game/game.entity'; +import { NotificationGateway } from '../notifications/notifications.gateway'; @Injectable() export class TaskService { @@ -12,6 +15,9 @@ export class TaskService { private taskRepository: Repository<TaskEntity>, @InjectRepository(FactionEntity) private factionRepository: Repository<FactionEntity>, + @InjectRepository(Game_PersonEntity) + private gamePersonRepository: Repository<Game_PersonEntity>, + private notificationGateway: NotificationGateway, ) {} async newTask(task: CreateTaskDTO) { @@ -28,6 +34,12 @@ export class TaskService { const createdTask = await this.taskRepository.create(task); console.log(createdTask); await this.taskRepository.insert(createdTask); + // notify subscribers about a new task + // if faction was set it notifies only faction members, else everyone + this.notificationGateway.server.emit( + task.faction != null ? task.faction.toString() : 'global', + 'new task', + ); return { message: 'Task added', }; @@ -56,4 +68,13 @@ export class TaskService { message: 'Task updated and closed', }; } + + async fetchTasks(user, taskGame) { + // to do: limit fetch for factions + const gamePerson = await this.gamePersonRepository.findOne({ + person: user, + game: taskGame, + }); + return await this.taskRepository.find({ taskGame: taskGame }); + } } -- GitLab From e62d3fa83ab2e8d6e3229ff78f051bfbaae83653 Mon Sep 17 00:00:00 2001 From: L4168 <L4168@student.jamk.fi> Date: Thu, 27 Jun 2019 13:53:18 +0300 Subject: [PATCH 10/12] fixed undefined id bug --- src/shared/roles.guard.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shared/roles.guard.ts b/src/shared/roles.guard.ts index e65916a..73693f9 100644 --- a/src/shared/roles.guard.ts +++ b/src/shared/roles.guard.ts @@ -32,9 +32,9 @@ export class RolesGuard implements CanActivate { return false; } const gameId = request.params.id; - const user = await this.getUserObject(request.headers.authorization); + request.user = await this.getUserObject(request.headers.authorization); const role = await this.game_PersonRepository.findOne({ - person: user['id'], + person: request.user['id'], game: gameId, }); // check that the role matches the criteria and that token is valid for this game -- GitLab From 0aed08e974bff09dc5437c58861d8a48ae5a6e71 Mon Sep 17 00:00:00 2001 From: L4168 <L4168@student.jamk.fi> Date: Thu, 27 Jun 2019 14:51:12 +0300 Subject: [PATCH 11/12] fetchTasks returns active and associated taks --- src/task/task.service.ts | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/task/task.service.ts b/src/task/task.service.ts index 9d4fe9a..082f5b0 100644 --- a/src/task/task.service.ts +++ b/src/task/task.service.ts @@ -32,7 +32,6 @@ export class TaskService { throw new HttpException('Faction not found', HttpStatus.BAD_REQUEST); } const createdTask = await this.taskRepository.create(task); - console.log(createdTask); await this.taskRepository.insert(createdTask); // notify subscribers about a new task // if faction was set it notifies only faction members, else everyone @@ -70,11 +69,31 @@ export class TaskService { } async fetchTasks(user, taskGame) { - // to do: limit fetch for factions const gamePerson = await this.gamePersonRepository.findOne({ - person: user, - game: taskGame, + where: { + person: user, + game: taskGame, + }, + relations: ['faction'], }); - return await this.taskRepository.find({ taskGame: taskGame }); + if (gamePerson.role == 'admin') { + return await this.taskRepository.find({ taskGame: taskGame }); + } else { + return await this.taskRepository.find({ + relations: ['faction'], + where: [ + { + taskGame: taskGame, + faction: gamePerson.faction.factionId, + taskIsActive: true, + }, + { + taskGame: taskGame, + faction: null, + taskIsActive: true, + }, + ], + }); + } } } -- GitLab From 875666b6e61ae24db950173454105cac7a66b48e Mon Sep 17 00:00:00 2001 From: L4168 <L4168@student.jamk.fi> Date: Thu, 27 Jun 2019 14:51:48 +0300 Subject: [PATCH 12/12] added roles auth --- src/task/task.controller.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/task/task.controller.ts b/src/task/task.controller.ts index 2e04789..32766ff 100644 --- a/src/task/task.controller.ts +++ b/src/task/task.controller.ts @@ -37,8 +37,10 @@ export class TaskController { return this.taskService.editTask(data); } + // lists all the tasks for the game if user has game_person entry + // :id is the id of the game @Get('get-tasks/:id') - @UseGuards(new AuthGuard()) + @Roles('soldier', 'factionleader', 'admin') async fetchTasks(@User('id') person, @Param('id') id: string) { return this.taskService.fetchTasks(person, id); } -- GitLab