diff --git a/.dockerignore b/.dockerignore index 5c96d0d93b892d7824395eb3d21ce828e8104263..688ef231417dd6705b9d31c3c9076c6102786e54 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,2 @@ -npm-debug.log \ No newline at end of file +npm-debug.log +node_modules \ No newline at end of file diff --git a/.gitignore b/.gitignore index fe0fe574b8536598528a1f7d46becfb814892327..22239d80a5439734168dc992e57cc02191f8010c 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,6 @@ lerna-debug.log* !.vscode/extensions.json # db connection -ormconfig.json .env +ormconfig.json *.providers.ts diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..978f37bb87dd6b3cadd22d16dd95e0ad3c1908ec --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,42 @@ +stages: + - purge + - e2e-test + +purge-old-containers: + image: + name: docker/compose:1.24.0 + entrypoint: ['/bin/sh', '-c'] + services: + - docker:dind + stage: purge + tags: ['docker'] + only: + - testing + script: + - docker-compose down + allow_failure: true + +e2e-testing: + image: + name: docker/compose:1.24.0 + entrypoint: ['/bin/sh', '-c'] + services: + - docker:dind + stage: e2e-test + tags: ['compose'] + only: + - testing + script: + - printf "SECRET=%s\n" "$SECRET" > .env + - docker image prune -f + - docker-compose build --no-cache + - docker-compose up -d + after_script: + - sleep 8 + - echo "create database ehasa;" | docker exec -i postgis psql -U postgres + - echo "create user ehasa;" | docker exec -i postgis psql -U postgres + - echo "alter user ehasa with encrypted password 'salasana';" | docker exec -i postgis psql -U postgres + - echo "grant all privileges on database ehasa to ehasa;" | docker exec -i postgis psql -U postgres + - echo 'create extension "uuid-ossp";' | docker exec -i postgis psql -U postgres ehasa + allow_failure: true + diff --git a/Dockerfile b/Dockerfile index e3da96a638a495e9ea8c0b9f25845017e8955bbe..48021045491c2edcc6d0318e62c1de8ef4e03387 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,8 @@ FROM node:10.15.3 WORKDIR /usr/src/app COPY package*.json ./ RUN npm install -RUN npm ci --only=production +RUN npm install -g ts-node +RUN npm install -g typescript COPY . . EXPOSE 5000 -CMD [ "npm", "start", "--force" ] \ No newline at end of file +CMD [ "npm", "start" ] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 7ac4913cbab54c870edd0ab853747d763eb56edd..f64dd18c7358bda9ee5b8b5bf21c7a63138b07dd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,16 +1,12 @@ -version: "3" +version: '3' services: - ehasa-frontend: - image: "ehasa/frontend" - ports: - - 8080:80 ehasa-backend: - image: "ehasa/backend" + build: . + container_name: back ports: - - 5000:5000 + - 8080:5000 postgres: image: mdillon/postgis - volumes: - - /home/postgres:/var/lib/postgresql/data + container_name: postgis ports: - 5432:5432 \ No newline at end of file diff --git a/ormconfig.json b/ormconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..bb6b5fae1ce31ce82b0b7c85dda0cfc2a604bba9 --- /dev/null +++ b/ormconfig.json @@ -0,0 +1,12 @@ +{ + "type": "postgres", + "host": "localhost", + "port": 5432, + "username": "ehasa", + "password": "salasana", + "database": "ehasa", + "entities": ["src/**/*.entity{.ts,.js}"], + "synchronize": true, + "logging": true, + "dropSchema": false +} diff --git a/package-lock.json b/package-lock.json index d6db2b3d2b0a999da1d9d863faa81b0e0bedeac3..53a606c27128a913a5a331e85d089988f9f164f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7650,16 +7650,24 @@ } }, "ts-node": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.1.0.tgz", - "integrity": "sha512-34jpuOrxDuf+O6iW1JpgTRDFynUZ1iEqtYruBqh35gICNjN8x+LpVcPAcwzLPi9VU6mdA3ym+x233nZmZp445A==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", + "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", "dev": true, "requires": { "arg": "^4.1.0", - "diff": "^3.1.0", + "diff": "^4.0.1", "make-error": "^1.1.1", "source-map-support": "^0.5.6", "yn": "^3.0.0" + }, + "dependencies": { + "diff": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", + "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", + "dev": true + } } }, "tsconfig-paths": { @@ -7689,12 +7697,6 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true } } }, diff --git a/package.json b/package.json index eaf64ea6d6af75e1ef9ecb38237a0f2326895fc2..9e44f929c2267e5ed2e143931c9b6a6bf908ff0e 100644 --- a/package.json +++ b/package.json @@ -57,10 +57,10 @@ "prettier": "^1.15.3", "supertest": "^3.4.1", "ts-jest": "24.0.2", - "ts-node": "^8.1.0", - "tsconfig-paths": "3.8.0", + "ts-node": "^8.3.0", + "tsconfig-paths": "^3.8.0", "tslint": "5.16.0", - "typescript": "3.4.3", + "typescript": "^3.4.3", "wait-on": "^3.2.0" }, "jest": { diff --git a/src/game/game.service.ts b/src/game/game.service.ts index ea11a669de95f43f89f8b3a7f798358e63421bbe..83760b1f65ccc3a108a620a2539c0ccf137acde1 100644 --- a/src/game/game.service.ts +++ b/src/game/game.service.ts @@ -38,98 +38,88 @@ export class GameService { // create a new game async createNewGame(personId: PersonEntity, gameData: GameDTO) { - try { - // 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); - const gamePerson = await this.game_PersonRepository.create({ - faction: null, - game: game, - person: personId, - }); - gamePerson['role'] = 'admin'; - await this.game_PersonRepository.insert(gamePerson); - return { - message: 'New game added', - }; - } catch (error) { - return error.message; + // 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); + const gamePerson = await this.game_PersonRepository.create({ + faction: null, + game: game, + person: personId, + }); + gamePerson['role'] = 'admin'; + await this.game_PersonRepository.insert(gamePerson); + return { + message: 'New game added', + }; } // edit already created game async editGame(id: string, gameData: GameDTO) { - try { - // checks if a game with the same name exists already - const { name } = gameData; - if (await this.gameRepository.findOne({ name: name, id: Not(id) })) { - throw new HttpException('Game already exists', HttpStatus.BAD_REQUEST); - } - // update game entry in db - const updatedGame = await this.gameRepository.create({ - ...gameData, - factions: null, - objective_points: null, + // checks if a game with the same name exists already + const { name } = gameData; + if (await this.gameRepository.findOne({ name: name, id: Not(id) })) { + throw new HttpException('Game already exists', HttpStatus.BAD_REQUEST); + } + // update game entry in db + const updatedGame = await this.gameRepository.create({ + ...gameData, + factions: null, + objective_points: null, + }); + 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(gameId); + const factionNames = factions.map(({ factionName }) => factionName); + // add the factions to db + if (gameData.factions) { + gameData.factions.map(async faction => { + if (!Object.values(factionNames).includes(faction.factionName)) { + let name = await this.factionRepository.create({ + ...faction, + gameId: gameId, + }); + await this.factionRepository.insert(name); + } }); - 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(gameId); - const factionNames = factions.map(({ factionName }) => factionName); - // add the factions to db - if (gameData.factions) { - gameData.factions.map(async faction => { - if (!Object.values(factionNames).includes(faction.factionName)) { - let name = await this.factionRepository.create({ - ...faction, - gameId: gameId, - }); - await this.factionRepository.insert(name); - } - }); - } + } - // get old flagboxes to deny duplicate entries - const flagboxes = await this.objectivePointRepository.find({ - game: gameId, + // 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 = 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); - } - }); - } + } - // TO DO: ADD FLAGBOX LOCATION TO MAPDRAWING ENTITY + // TO DO: ADD FLAGBOX LOCATION TO MAPDRAWING ENTITY - return { - message: 'Game updated', - }; - } catch (error) { - return error; - } + return { + message: 'Game updated', + }; } async deleteGame(id) { @@ -142,87 +132,71 @@ export class GameService { // checks the password, creates an entry in GamePerson table with associated role&faction async createGroup(person, gameId, groupData) { - try { - // check if the person already is in a group in this game - const checkDuplicate = await this.game_PersonRepository.findOne({ - person: person, - }); - if (checkDuplicate) { - throw new HttpException( - 'You already belong to a group!', - HttpStatus.BAD_REQUEST, - ); - } - - // create a group entry and insert it to db - const group = await this.game_GroupRepository.create({ - ...groupData, - game: gameId, - }); - const gameGroup = await this.game_GroupRepository.insert(group); + // check if the person already is in a group in this game + const checkDuplicate = await this.game_PersonRepository.findOne({ + person: person, + }); + if (checkDuplicate) { + throw new HttpException( + 'You already belong to a group!', + HttpStatus.BAD_REQUEST, + ); + } - // create game_Person entry and insert it to db - const gamePerson = await this.game_PersonRepository.create({ - role: 'soldier', - faction: null, - game: gameId, - person: person, - leaderGroup: gameGroup.identifiers[0]['id'], - group: gameGroup.identifiers[0]['id'], - }); - await this.game_PersonRepository.insert(gamePerson); + // create a group entry and insert it to db + const group = await this.game_GroupRepository.create({ + ...groupData, + game: gameId, + }); + const gameGroup = await this.game_GroupRepository.insert(group); + + // create game_Person entry and insert it to db + const gamePerson = await this.game_PersonRepository.create({ + role: 'soldier', + faction: null, + game: gameId, + person: person, + leaderGroup: gameGroup.identifiers[0]['id'], + group: gameGroup.identifiers[0]['id'], + }); + await this.game_PersonRepository.insert(gamePerson); - return { - message: 'created new group', - }; - } catch (e) { - return e; - } + return { + message: 'created new group', + }; } async showGroups() { - try { - return await this.game_GroupRepository.find({ - relations: ['leader', 'players', 'game'], - }); - } catch (e) { - return e; - } + return await this.game_GroupRepository.find({ + relations: ['leader', 'players', 'game'], + }); } async joinGroup(person, groupId) { - try { - const gameData = await this.game_GroupRepository.findOne({ - where: { id: groupId }, - relations: ['players', 'game'], - }); - const gamePerson = await this.game_PersonRepository.create({ - role: 'soldier', - faction: null, - game: gameData.game, - person: person, - leaderGroup: null, - group: groupId, - }); - await this.game_PersonRepository.insert(gamePerson); - return { - message: 'Joined group', - }; - } catch (e) { - return e; - } + const gameData = await this.game_GroupRepository.findOne({ + where: { id: groupId }, + relations: ['players', 'game'], + }); + const gamePerson = await this.game_PersonRepository.create({ + role: 'soldier', + faction: null, + game: gameData.game, + person: person, + leaderGroup: null, + group: groupId, + }); + await this.game_PersonRepository.insert(gamePerson); + return { + message: 'Joined group', + }; } // returns name and id of each game async listGames() { - try { - const games = await this.gameRepository.find(); - return games.map(game => { - return game.gameObject(); - }); - } catch (error) { - return error; - } + const games = await this.gameRepository.find(); + return games.map(game => { + return game.gameObject(); + }); } // returns information about a game identified by id diff --git a/src/shared/http-error.filter.ts b/src/shared/http-error.filter.ts index cafc414be633ac315931559cad3f4015eef0bf08..8a65068c4275c4a8d576da54b0340c17b6ac8cc4 100644 --- a/src/shared/http-error.filter.ts +++ b/src/shared/http-error.filter.ts @@ -1,23 +1,47 @@ -import { Catch, ExceptionFilter, HttpException, ArgumentsHost, Logger } from "@nestjs/common"; +import { + ExceptionFilter, + Catch, + ArgumentsHost, + Logger, + HttpException, + HttpStatus, +} from '@nestjs/common'; @Catch() export class HttpErrorFilter implements ExceptionFilter { - catch(exception: HttpException, host: ArgumentsHost) { - const ctx = host.switchToHttp(); - const request = ctx.getRequest(); - const response = ctx.getResponse(); - const status = exception.getStatus(); + catch(exception: HttpException, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const request = ctx.getRequest(); + const status = exception.getStatus + ? exception.getStatus() + : HttpStatus.INTERNAL_SERVER_ERROR; - const errorResponse = { - code: status, - timestamp: new Date().toLocaleDateString(), - path: request.url, - method: request.method, - message: exception.message.error || exception.message || null, - }; + const errorResponse = { + code: status, + timestamp: new Date().toLocaleDateString(), + path: request.url, + method: request.method, + message: + status !== HttpStatus.INTERNAL_SERVER_ERROR + ? exception.message.error || exception.message || null + : 'Internal server error', + }; - Logger.error(`${request.method} ${request.url}`, JSON.stringify(errorResponse), "ExceptionFilter"); - - response.status(404).json({errorResponse}); + if (status === HttpStatus.INTERNAL_SERVER_ERROR) { + Logger.error( + `${request.method} ${request.url}`, + exception.stack, + 'ExceptionFilter', + ); + } else { + Logger.error( + `${request.method} ${request.url}`, + JSON.stringify(errorResponse), + 'ExceptionFilter', + ); } -} \ No newline at end of file + + response.status(status).json(errorResponse); + } +} diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 8d0a64d8ea0430d1633e67a17ee3fb782d21ce72..d491865f5177d2f6bbaab607da0c2a1a51711a48 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -1,6 +1,6 @@ import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; -import { Repository } from "typeorm"; -import { InjectRepository } from "@nestjs/typeorm"; +import { Repository } from 'typeorm'; +import { InjectRepository } from '@nestjs/typeorm'; import { PersonEntity } from './user.entity'; import { UserDTO } from './user.dto'; @@ -9,59 +9,51 @@ import { GameDTO } from '../game/game.dto'; @Injectable() export class UserService { - constructor( - @InjectRepository(PersonEntity) - private userRepository: Repository<PersonEntity>, - @InjectRepository(GameEntity) - private gameRepository: Repository<GameEntity> - ){} + constructor( + @InjectRepository(PersonEntity) + private userRepository: Repository<PersonEntity>, + @InjectRepository(GameEntity) + private gameRepository: Repository<GameEntity>, + ) {} - async register(data: UserDTO) { - const { name } = data; - let user = await this.userRepository.findOne({where: {name}}); - if (user) { - throw new HttpException('User already exists', HttpStatus.BAD_REQUEST); - } - user = await this.userRepository.create(data); - await this.userRepository.save(user); - return user.tokenObject(); + async register(data: UserDTO) { + const { name } = data; + let user = await this.userRepository.findOne({ where: { name } }); + if (user) { + throw new HttpException('User already exists', HttpStatus.BAD_REQUEST); } + user = await this.userRepository.create(data); + await this.userRepository.save(user); + return user.tokenObject(); + } - async login(data: UserDTO) { - try{ - const { name, password } = data; - const user = await this.userRepository.findOne({ where: { name }}); - if (!user || !(await user.comparePassword(password))) { - throw new HttpException( - 'Invalid username/password', - HttpStatus.BAD_REQUEST, - ); - } - return user.tokenObject(); - }catch(error){ - return error.message; - } + async login(data: UserDTO) { + const { name, password } = data; + const user = await this.userRepository.findOne({ where: { name } }); + if (!user || !(await user.comparePassword(password))) { + throw new HttpException('invalid password', HttpStatus.BAD_REQUEST); } + return user.tokenObject(); + } - // liitytään peliin - async joinGame(game: GameDTO, data: UserDTO){ - try { - // etsi peli valinnan mukaan ja otetaan käyttäjän rooli kyseisestä pelistä talteen - const role = await this.gameRepository.findOne(); - return role; - } catch (error) { - return error.message; - } + // liitytään peliin + async joinGame(game: GameDTO, data: UserDTO) { + try { + // etsi peli valinnan mukaan ja otetaan käyttäjän rooli kyseisestä pelistä talteen + const role = await this.gameRepository.findOne(); + return role; + } catch (error) { + return error.message; } + } - // liitytään factionii - async joinFaction(game: GameDTO, data: UserDTO): Promise<boolean>{ - try { - // tarkistetaan factionin salasana - return true; - } catch (error) { - return error; - } + // liitytään factionii + async joinFaction(game: GameDTO, data: UserDTO): Promise<boolean> { + try { + // tarkistetaan factionin salasana + return true; + } catch (error) { + return error; } - + } }