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