diff --git a/src/app.module.ts b/src/app.module.ts index dbb03cf7baa35d2d4deafdbff6ba67bd64381c92..2c2773b3c7a80e6e20b316dd2a753e32b310a49a 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -2,19 +2,22 @@ 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 { AppController } from './app.controller'; import { AppService } from './app.service'; -import { UserModule } from './user/user.module'; -import { HttpErrorFilter } from './shared/http-error.filter'; + +import { RolesGuard } from './shared/roles.guard'; import { LoggingInterceptor } from './shared/logging.interceptor'; +import { StatesGuard } from './shared/states.guard'; +import { HttpErrorFilter } from './shared/http-error.filter'; + import { NotificationModule } from './notifications/notifications.module'; -import { GameModule } from './game/game.module'; -import { RolesGuard } from './shared/roles.guard'; import { TaskModule } from './task/task.module'; +import { TrackingModule } from './tracking/tracking.module'; +import { UserModule } from './user/user.module'; import { DrawModule } from './draw/draw.module'; import { FactionModule } from './faction/faction.module'; -import { TrackingModule } from './tracking/tracking.module'; - +import { GameModule } from './game/game.module'; @Module({ imports: [ @@ -42,6 +45,10 @@ import { TrackingModule } from './tracking/tracking.module'; provide: APP_GUARD, useClass: RolesGuard, }, + { + provide: APP_GUARD, + useClass: StatesGuard, + }, ], }) export class AppModule { diff --git a/src/draw/coordinate.entity.ts b/src/draw/coordinate.entity.ts index 9cee16c6a496d5e9c68a33eabf57514e88cdf7c1..8f5c4e1d95cd7bf819818664aa41f662a412b316 100644 --- a/src/draw/coordinate.entity.ts +++ b/src/draw/coordinate.entity.ts @@ -17,7 +17,9 @@ export class MapDrawingEntity { @Column({ type: 'json', nullable: true }) data: JSON; - @ManyToOne(type => FactionEntity, faction => faction.mapDrawings) + @ManyToOne(type => FactionEntity, faction => faction.mapDrawings, { + onDelete: 'CASCADE', + }) faction: FactionEntity; @ManyToOne(type => GameEntity, gameEntity => gameEntity.id) gameId: GameEntity; diff --git a/src/draw/draw.controller.ts b/src/draw/draw.controller.ts index 21306d7ae91e565a5459138229a384e21fd68abd..10b836118855d17bee7856203b1258f4b572c8d5 100644 --- a/src/draw/draw.controller.ts +++ b/src/draw/draw.controller.ts @@ -11,7 +11,7 @@ import { import { AuthGuard } from '../shared/auth.guard'; import { DrawService } from './draw.service'; -import { Roles } from '../shared/roles.decorator'; +import { Roles, GameStates } from '../shared/guard.decorator'; /* DrawController @@ -27,6 +27,7 @@ export class DrawController { @Put('mapdrawing/:id') @UsePipes(new ValidationPipe()) @Roles('admin', 'factionleader') + @GameStates('CREATED', 'STARTED') async draw(@Param('id') gameId, @Body() data) { return this.drawService.draw(gameId, data); } diff --git a/src/draw/draw.service.ts b/src/draw/draw.service.ts index e3b62b5132223f466fa94661d4748b3f7cd497f5..ffa48d4b634a35c6b0dc4bb26391d269c9f9b76a 100644 --- a/src/draw/draw.service.ts +++ b/src/draw/draw.service.ts @@ -18,8 +18,8 @@ export class DrawService { if (data.mapDrawingId == null || data.mapDrawingId == '') { // luo uuden instanssin. - const mapdrawing = await this.mapDrawingRepository.insert(drawing); - return mapdrawing.identifiers; + const mapDrawing = await this.mapDrawingRepository.insert(drawing); + return mapDrawing.identifiers; } else { //päivittää mapDrawingin return await this.mapDrawingRepository.save(drawing); diff --git a/src/faction/faction.controller.ts b/src/faction/faction.controller.ts index dec9308d456e93f4c6f26fee5f5f802af4e5857e..98fcc856d6a5e1cb469e81249eea044df4011877 100644 --- a/src/faction/faction.controller.ts +++ b/src/faction/faction.controller.ts @@ -21,7 +21,7 @@ import { JoinGameGroupDTO, } from './faction.dto'; import { FactionService } from './faction.service'; -import { Roles } from '../shared/roles.decorator'; +import { Roles, GameStates } from '../shared/guard.decorator'; @Controller('faction') export class FactionController { @@ -30,6 +30,7 @@ export class FactionController { // takes gameId from the url to verify user role @Post('create-group/:id') @Roles('soldier') + @GameStates('CREATED') @UsePipes(new ValidationPipe()) async createGroup( @User('id') person, @@ -50,6 +51,7 @@ export class FactionController { // takes gameId from the url to verify user role @Put('join-group/:id') @Roles('soldier') + @GameStates('CREATED') async joinGroup( @User('id') person, @Param('id') id, @@ -66,17 +68,24 @@ export class FactionController { // param game ID is passed to @Roles @Put('promote/:id') - @UseGuards(new AuthGuard()) - @UsePipes(new ValidationPipe()) @Roles('admin') + @GameStates('CREATED') + @UsePipes(new ValidationPipe()) promotePlayer(@Param('id') game, @Body() body: PromotePlayerDTO) { return this.factionservice.promotePlayer(body); } - @Put('join-faction') + // used to join a faction + // :id is the id of the game, and is needed for GameStates to check the state of the game + @Put('join-faction/:id') @UseGuards(new AuthGuard()) + @GameStates('CREATED') @UsePipes(new ValidationPipe()) - joinFaction(@User('id') person, @Body() data: JoinFactionDTO) { + joinFaction( + @User('id') person, + @Param('id') game, + @Body() data: JoinFactionDTO, + ) { return this.factionservice.joinFaction(person, data); } } diff --git a/src/faction/faction.dto.ts b/src/faction/faction.dto.ts index a5b9f044bdf74edefa37ac077e09a3e384e5e5c3..0b65c47007d6424584d8fdb35ac9d9ee53eaa924 100644 --- a/src/faction/faction.dto.ts +++ b/src/faction/faction.dto.ts @@ -7,14 +7,18 @@ import { IsNumber, Min, Max, + IsOptional, } from 'class-validator'; import { GameEntity } from '../game/game.entity'; -import { RoleValidation } from '../shared/custom-validation'; +import { RoleValidation, Uuid } from '../shared/custom-validation'; import { GameDTO } from '../game/game.dto'; import { FactionEntity, GameGroupEntity } from './faction.entity'; export class FactionDTO { + @IsOptional() + @IsUUID('4') + factionId?: string; @IsString() @IsNotEmpty() @Length(2, 31) diff --git a/src/faction/faction.entity.ts b/src/faction/faction.entity.ts index 42f3aca413b36455786494818f2ca06f50f50f2e..8bc7548a4f37bf367a6cb0381bc2a131d8080da4 100644 --- a/src/faction/faction.entity.ts +++ b/src/faction/faction.entity.ts @@ -26,11 +26,15 @@ export class FactionEntity { @Column({ type: 'text' }) factionPassword: string; - @OneToMany(type => Game_PersonEntity, game_persons => game_persons.faction) + @OneToMany(type => Game_PersonEntity, game_persons => game_persons.faction, { + onDelete: 'SET NULL', + }) game_persons: Game_PersonEntity[]; @ManyToOne(type => GameEntity, game => game.factions) game: GameEntity; - @OneToMany(type => MapDrawingEntity, mapDrawings => mapDrawings.faction) + @OneToMany(type => MapDrawingEntity, mapDrawings => mapDrawings.faction, { + onDelete: 'CASCADE', + }) mapDrawings: MapDrawingEntity[]; factionObject() { @@ -102,6 +106,8 @@ export class GameGroupEntity { onDelete: 'CASCADE', }) players: Game_PersonEntity[]; - @ManyToOne(type => FactionEntity, faction => faction.factionId) + @ManyToOne(type => FactionEntity, faction => faction.factionId, { + onDelete: 'CASCADE', + }) faction: FactionEntity; } diff --git a/src/game/game.controller.ts b/src/game/game.controller.ts index 015baf9673563cd8732ce15493e80e23c19e0e6b..24d42bbae81d23bbba83af27d06ec58268e69127 100644 --- a/src/game/game.controller.ts +++ b/src/game/game.controller.ts @@ -14,9 +14,9 @@ import { import { GameService } from './game.service'; import { AuthGuard } from '../shared/auth.guard'; import { User } from '../user/user.decorator'; -import { GameDTO, FlagboxEventDTO } from './game.dto'; +import { GameDTO, FlagboxEventDTO, GameStateDTO } from './game.dto'; import { ValidationPipe } from '../shared/validation.pipe'; -import { Roles } from '../shared/roles.decorator'; +import { Roles, GameStates } from '../shared/guard.decorator'; import { GameEntity } from './game.entity'; @Controller('game') @@ -32,11 +32,20 @@ export class GameController { @Put('edit/:id') @Roles('admin') + @GameStates('CREATED') @UsePipes(new ValidationPipe()) async editGame(@Param('id') id: string, @Body() body: GameDTO) { + body.id = id; return this.gameservice.editGame(id, body); } + @Put('edit-state/:id') + @Roles('admin') + @UsePipes(new ValidationPipe()) + async updateGameState(@Param('id') id: string, @Body() body: GameStateDTO) { + return this.gameservice.updateGameStatus(body); + } + @Get('listgames') async listGames() { return this.gameservice.listGames(); @@ -61,6 +70,7 @@ export class GameController { } @Post('flag/:id') + @GameStates('STARTED') async flagboxEvent(@Param('id') id: string, @Body() data: FlagboxEventDTO) { return this.gameservice.flagboxEvent(id, data); } diff --git a/src/game/game.dto.ts b/src/game/game.dto.ts index 913b6c0f76637ecc94c82c8853cb5428e411f06d..7b912b49cbb3c5ab710063873d2a60e30db74c9e 100644 --- a/src/game/game.dto.ts +++ b/src/game/game.dto.ts @@ -9,6 +9,9 @@ import { Max, ValidateNested, Allow, + IsUUID, + IsIn, + IsOptional, } from 'class-validator'; import { ObjectivePointEntity } from './game.entity'; @@ -18,6 +21,8 @@ import { CenterDTO, NodeSettingsDTO } from './game.json.dto'; import { Type } from 'class-transformer'; export class GameDTO { + @IsOptional() + id: string; @IsString() @IsNotEmpty() @Length(3, 30) @@ -68,7 +73,17 @@ export class newGameDTO { enddate: string; } +export class GameStateDTO { + @IsUUID('4') + id: string; + @IsIn(['CREATED', 'STARTED', 'PAUSED', 'ENDED']) + state: string; +} + export class FlagboxDTO { + @IsOptional() + @IsUUID('4') + objectivePointId: string; @IsString() @IsNotEmpty() @Length(7) diff --git a/src/game/game.entity.ts b/src/game/game.entity.ts index 5f019e83c0b3af08bd423809b79ec5039850bbd2..c4f4e555ac2bb2aa45bfc0bf2eeac48349f25d86 100644 --- a/src/game/game.entity.ts +++ b/src/game/game.entity.ts @@ -25,6 +25,7 @@ export class GameEntity { @Column('json') center: CenterDTO; @Column({ type: 'json', nullable: true }) map: JSON; @Column({ type: 'json', nullable: true }) nodesettings?: NodeSettingsDTO; + @Column('text') state: string; @Column('timestamp') startdate: Timestamp; @Column('timestamp') enddate: Timestamp; @@ -55,7 +56,9 @@ export class GameEntity { export class Game_PersonEntity { @PrimaryGeneratedColumn('uuid') gamepersonId: string; @Column({ type: 'text', nullable: true }) role: string; - @ManyToOne(type => FactionEntity, faction => faction.game_persons) + @ManyToOne(type => FactionEntity, faction => faction.game_persons, { + onDelete: 'SET NULL', + }) faction: FactionEntity; @ManyToOne(type => GameEntity, game => game.id) game: GameEntity; @@ -89,9 +92,13 @@ export class ObjectivePoint_HistoryEntity { @PrimaryGeneratedColumn('uuid') oP_HistoryId: string; @Column({ type: 'timestamp' }) oP_HistoryTimestamp: Timestamp; @Column('float') action: number; - @ManyToOne(type => FactionEntity, factionEntity => factionEntity.factionId) + @ManyToOne(type => FactionEntity, factionEntity => factionEntity.factionId, { + onDelete: 'CASCADE', + }) capture: FactionEntity; - @ManyToOne(type => FactionEntity, factionentity => factionentity.factionId) + @ManyToOne(type => FactionEntity, factionentity => factionentity.factionId, { + onDelete: 'CASCADE', + }) owner: FactionEntity; @ManyToOne( type => ObjectivePointEntity, diff --git a/src/game/game.service.ts b/src/game/game.service.ts index 573bb2255ba2d65f828ad3900ff064e1fc471d13..a6c20c41d97c3b8b9c9848b5ceb377682c559a21 100644 --- a/src/game/game.service.ts +++ b/src/game/game.service.ts @@ -8,7 +8,7 @@ import { ObjectivePointEntity, ObjectivePoint_HistoryEntity, } from './game.entity'; -import { GameDTO, FlagboxEventDTO } from './game.dto'; +import { GameDTO, FlagboxEventDTO, GameStateDTO } from './game.dto'; import { PersonEntity } from '../user/user.entity'; import { FactionEntity } from '../faction/faction.entity'; import { NotificationGateway } from '../notifications/notifications.gateway'; @@ -39,6 +39,7 @@ export class GameService { } // else add the game to the database const game = await this.gameRepository.create(gameData); + game.state = 'CREATED'; await this.gameRepository.insert(game); // add gamePerson with role admin to the game const gamePerson = await this.game_PersonRepository.create({ @@ -54,54 +55,84 @@ export class GameService { } // edit already created game - async editGame(id: string, gameData: GameDTO) { + async editGame(id, gameData: GameDTO) { // checks if a game with the same name exists already if ( await this.gameRepository.findOne({ name: gameData.name, id: Not(id) }) ) { - throw new HttpException('Game already exists', HttpStatus.BAD_REQUEST); + throw new HttpException( + 'Game with the same name already exists', + HttpStatus.BAD_REQUEST, + ); + } + + // check for duplicate names in gameData + const factionNames = gameData.factions.map( + ({ factionName }) => factionName, + ); + const flagboxNodeIds = gameData.objective_points.map( + ({ objectivePointDescription }) => objectivePointDescription, + ); + if ( + new Set(factionNames).size !== factionNames.length || + new Set(flagboxNodeIds).size !== flagboxNodeIds.length + ) { + throw new HttpException( + 'No duplicate names allowed!', + HttpStatus.BAD_REQUEST, + ); } + + // get factions that have been added previously + let factions = await this.factionRepository.find({ game: id }); + // get flagboxes that have been added previously + let flagboxes = await this.objectivePointRepository.find({ + game: id, + }); // update game entry in db const updatedGame = await this.gameRepository.create(gameData); - updatedGame['id'] = id; const gameId = await this.gameRepository.save(updatedGame); - - // get all the factions that are associated with the game to deny duplicate entries - const factions = await this.factionRepository.find({ game: gameId }); - const factionNames = factions.map(({ factionName }) => factionName); - // add the factions to db + // iterate factions if any were added if (gameData.factions) { - gameData.factions.map(async faction => { - if (!Object.values(factionNames).includes(faction.factionName)) { - let name = await this.factionRepository.create({ - ...faction, - game: gameId, - }); - await this.factionRepository.insert(name); + const factionIds = gameData.factions.map(({ factionId }) => factionId); + // delete all existing factions that are not in submitted data + factions.map(async faction => { + if (!factionIds.includes(faction.factionId)) { + await this.factionRepository.delete(faction); } }); + // create / update factions present in the submitted data + gameData.factions.map(async faction => { + let name = await this.factionRepository.create({ + ...faction, + game: gameId, + }); + await this.factionRepository.save(name); + }); + } else { + // if no factions are present in data, delete all factions associated with the game + await this.factionRepository.delete({ game: id }); } - // get old flagboxes to deny duplicate entries - const flagboxes = await this.objectivePointRepository.find({ - game: gameId, - }); - const flagboxIds = flagboxes.map( - ({ objectivePointDescription }) => objectivePointDescription, - ); // insert the flagboxes to db if (gameData.objective_points) { - gameData.objective_points.map(async flagbox => { - if ( - !Object.values(flagboxIds).includes(flagbox.objectivePointDescription) - ) { - let newFlagbox = await this.objectivePointRepository.create({ - ...flagbox, - game: gameId, - }); - await this.objectivePointRepository.insert(newFlagbox); + const flagboxIds = gameData.objective_points.map( + ({ objectivePointId }) => objectivePointId, + ); + flagboxes.map(async flagbox => { + if (!flagboxIds.includes(flagbox.objectivePointDescription)) { + await this.objectivePointRepository.delete(flagbox); } }); + gameData.objective_points.map(async flagbox => { + let newFlagbox = await this.objectivePointRepository.create({ + ...flagbox, + game: gameId, + }); + await this.objectivePointRepository.save(newFlagbox); + }); + } else { + await this.objectivePointRepository.delete({ game: id }); } // TO DO: ADD FLAGBOX LOCATION TO MAPDRAWING ENTITY @@ -112,6 +143,21 @@ export class GameService { }; } + async updateGameStatus(game: GameStateDTO) { + const updatedGame = await this.gameRepository.findOne({ id: game.id }); + if (updatedGame) { + updatedGame.state = game.state; + await this.gameRepository.save(updatedGame); + // notify players about game state change + this.notificationGateway.server.emit(game.id, 'event update'); + return { + code: 200, + message: 'State was updated', + }; + } + throw new HttpException("Game doesn't exist", HttpStatus.BAD_REQUEST); + } + async listFactions(game: GameEntity) { return this.factionRepository.find({ game }); } diff --git a/src/shared/roles.decorator.ts b/src/shared/guard.decorator.ts similarity index 53% rename from src/shared/roles.decorator.ts rename to src/shared/guard.decorator.ts index 0d14223c3ce5ee1002cf245fa16fd1b57749bae1..b299d6de913f54dd496c344f6dcdafd2942b2c9c 100644 --- a/src/shared/roles.decorator.ts +++ b/src/shared/guard.decorator.ts @@ -1,3 +1,6 @@ import { SetMetadata } from '@nestjs/common'; -export const Roles = (...roles: string[]) => SetMetadata('roles', roles); \ No newline at end of file +export const Roles = (...roles: string[]) => SetMetadata('roles', roles); + +export const GameStates = (...states: string[]) => + SetMetadata('states', states); diff --git a/src/shared/states.guard.ts b/src/shared/states.guard.ts new file mode 100644 index 0000000000000000000000000000000000000000..4a4b5d423685e8ca5c2bd68cf715c50750981cd7 --- /dev/null +++ b/src/shared/states.guard.ts @@ -0,0 +1,45 @@ +import { + Injectable, + CanActivate, + ExecutionContext, + HttpException, + HttpStatus, +} from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { GameEntity } from '../game/game.entity'; + +@Injectable() +export class StatesGuard implements CanActivate { + constructor( + private readonly reflector: Reflector, + @InjectRepository(GameEntity) + private gameRepository: Repository<GameEntity>, + ) {} + + async canActivate(context: ExecutionContext): Promise<boolean> { + // get game states that are allowed access, identified by @GameStates('state') decorators in controllers + const states = this.reflector.get<string[]>('states', context.getHandler()); + if (!states) { + return true; + } + const request = context.switchToHttp().getRequest(); + const gameId = request.params.id; + const gameRef = await this.gameRepository.findOne({ + id: gameId, + }); + // check that the gameState matches the criteria + if (gameRef && states.includes(gameRef.state)) { + return true; + } else { + throw new HttpException( + `Game is set to ${ + gameRef.state + }, operation only valid in states ${states.join(', ')}`, + HttpStatus.BAD_REQUEST, + ); + } + } +} diff --git a/src/task/task.controller.ts b/src/task/task.controller.ts index 2cb030ff4332cc890e212f9749f1c84f0aa81248..acadbda71b34cb0ca591d974b383bca632cd22ce 100644 --- a/src/task/task.controller.ts +++ b/src/task/task.controller.ts @@ -2,7 +2,7 @@ import { Controller, Post, Body, UsePipes, Get, Param } from '@nestjs/common'; import { TaskService } from './task.service'; import { CreateTaskDTO, EditTaskDTO } from './task.dto'; -import { Roles } from '../shared/roles.decorator'; +import { Roles } from '../shared/guard.decorator'; import { ValidationPipe } from '../shared/validation.pipe'; import { User } from '../user/user.decorator'; diff --git a/src/task/task.entity.ts b/src/task/task.entity.ts index 85ee3c26d3830bdb81ebe89bccac93051e0562c7..c8f7d6218f373d8bd213e49dc7b9d6b2ab8ec68e 100644 --- a/src/task/task.entity.ts +++ b/src/task/task.entity.ts @@ -16,9 +16,13 @@ export class TaskEntity { @Column({ type: 'text' }) taskDescription: string; @Column({ type: 'bool' }) taskIsActive: boolean; - @ManyToOne(type => FactionEntity, faction => faction.factionId) + @ManyToOne(type => FactionEntity, faction => faction.factionId, { + onDelete: 'CASCADE', + }) faction: FactionEntity; - @ManyToOne(type => FactionEntity, faction => faction.factionId) + @ManyToOne(type => FactionEntity, faction => faction.factionId, { + onDelete: 'CASCADE', + }) taskWinner: FactionEntity; @ManyToOne(type => GameEntity, game => game.id) @JoinColumn({ name: 'taskGame' }) diff --git a/src/tracking/tracking.controller.ts b/src/tracking/tracking.controller.ts index 140b51e0ce816c2ac5c8b2b11ced8eee2b7d869f..9451cdc366f0847389067bd5b9093903a46a1633 100644 --- a/src/tracking/tracking.controller.ts +++ b/src/tracking/tracking.controller.ts @@ -1,27 +1,28 @@ import { Controller, - Get, Post, Param, UseGuards, UsePipes, Body, } from '@nestjs/common'; + import { TrackingService } from './tracking.service'; -import { Roles } from '../shared/roles.decorator'; -import { AuthGuard } from '../shared/auth.guard'; -import { ValidationPipe } from '../shared/validation.pipe'; import { TrackingDTO } from './tracking.dto'; -import { User } from 'src/user/user.decorator'; +import { User } from '../user/user.decorator'; +import { Roles, GameStates } from '../shared/guard.decorator'; +import { ValidationPipe } from '../shared/validation.pipe'; @Controller('tracking') export class TrackingController { constructor(private trackingservice: TrackingService) {} + // inserts tracking data to the database + // :id is the id of the game @Post('location/:id') - @UseGuards(new AuthGuard()) - @UsePipes(new ValidationPipe()) @Roles('soldier') + @GameStates('STARTED') + @UsePipes(new ValidationPipe()) async trackLocation( @User('id') userId, @Param('id') id, diff --git a/src/tracking/tracking.entity.ts b/src/tracking/tracking.entity.ts index c64f64f1bb90a8f7cbcb662956f883f895f17221..b31239101176a0316031cf6c1449205a26bcd150 100644 --- a/src/tracking/tracking.entity.ts +++ b/src/tracking/tracking.entity.ts @@ -1,12 +1,5 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - ManyToOne, - OneToOne, - JoinColumn, -} from 'typeorm'; -import { Game_PersonEntity, GameEntity } from '../game/game.entity'; +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm'; +import { Game_PersonEntity } from '../game/game.entity'; @Entity('Tracking') export class TrackingEntity { diff --git a/src/tracking/tracking.module.ts b/src/tracking/tracking.module.ts index a6f3ff1734adba3435360213c13cf024fe3cb288..45b167fc526ab41addbb87575518c9acae884482 100644 --- a/src/tracking/tracking.module.ts +++ b/src/tracking/tracking.module.ts @@ -1,9 +1,10 @@ import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + import { TrackingController } from './tracking.controller'; import { TrackingService } from './tracking.service'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { Game_PersonEntity } from 'src/game/game.entity'; import { TrackingEntity } from './tracking.entity'; +import { Game_PersonEntity } from '../game/game.entity'; @Module({ imports: [TypeOrmModule.forFeature([TrackingEntity, Game_PersonEntity])], diff --git a/src/tracking/tracking.service.ts b/src/tracking/tracking.service.ts index 06f665c4f8471c0863ab8c22da34c0fa9fb9d36b..6dc01c1d9c6dbfcf7336750602d2c90c5092f81b 100644 --- a/src/tracking/tracking.service.ts +++ b/src/tracking/tracking.service.ts @@ -1,7 +1,8 @@ import { Injectable, HttpStatus, HttpException } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; + import { Game_PersonEntity } from '../game/game.entity'; -import { InjectRepository } from '@nestjs/typeorm'; import { TrackingEntity } from './tracking.entity'; import { TrackingDTO } from './tracking.dto';