Compare commits
	
		
			61 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b3ce56c605 | |||
| 28cefa792c | |||
| 0803abc168 | |||
| 02ae883fa4 | |||
| be4050768e | |||
| dc6ec23cb9 | |||
| 1bb98c13d1 | |||
| bca979bab5 | |||
| e4fafd764c | |||
| 172159414b | |||
| 9355138a8c | |||
| 343cd8b772 | |||
| 01e0d5b94d | |||
| ac00667465 | |||
| 3deae2bfeb | |||
| 3f7b0f6563 | |||
| e6b9d4f273 | |||
| a00231dd3c | |||
| 3bc172e7e0 | |||
| ee9df21ae5 | |||
| f96b256ad3 | |||
| f2c50e929e | |||
| 02e3239848 | |||
| 8a54b027d0 | |||
| 3b11e896d4 | |||
| 89926b2c31 | |||
| 7b4e89555e | |||
| 1e37186247 | |||
| 154c763719 | |||
| 80197d5834 | |||
| 7e95103a2d | |||
| efe1a1f543 | |||
| 4fea690670 | |||
| f1dee1061d | |||
| 61cf0fc08d | |||
| 0c86e5dae1 | |||
| 638898fa28 | |||
| e7cd68e1c8 | |||
| e40e6faebd | |||
| 3d07aac944 | |||
| 1a5493facf | |||
| 9013b9492c | |||
| 188f26ad65 | |||
| 3ceb5a0c0f | |||
| e1ce052d3c | |||
| 70a379edef | |||
| 35ea3154d1 | |||
| ebf66821a2 | |||
| 8463bee253 | |||
| 860680d001 | |||
| df39166279 | |||
| 32fda46f0a | |||
| 36ecae7e6e | |||
| a5bfe4e3d5 | |||
| 4faeddc3f3 | |||
| 98f7bf366f | |||
| af3a9e5ce2 | |||
| 52eb7b1afe | |||
| 490fbd241d | |||
| f132131156 | |||
| c1e680a063 | 
							
								
								
									
										47
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "@odit/lfk-backend", | ||||
|   "version": "0.0.9", | ||||
|   "version": "0.0.11", | ||||
|   "main": "src/app.ts", | ||||
|   "repository": "https://git.odit.services/lfk/backend", | ||||
|   "author": { | ||||
| @@ -22,11 +22,11 @@ | ||||
|   ], | ||||
|   "license": "CC-BY-NC-SA-4.0", | ||||
|   "dependencies": { | ||||
|     "argon2": "^0.27.0", | ||||
|     "argon2": "^0.27.1", | ||||
|     "body-parser": "^1.19.0", | ||||
|     "class-transformer": "^0.3.1", | ||||
|     "class-validator": "^0.12.2", | ||||
|     "class-validator-jsonschema": "^2.0.3", | ||||
|     "class-validator-jsonschema": "^2.1.0", | ||||
|     "consola": "^2.15.0", | ||||
|     "cookie": "^0.4.1", | ||||
|     "cookie-parser": "^1.4.5", | ||||
| @@ -39,33 +39,34 @@ | ||||
|     "pg": "^8.5.1", | ||||
|     "reflect-metadata": "^0.1.13", | ||||
|     "routing-controllers": "^0.9.0-alpha.6", | ||||
|     "routing-controllers-openapi": "^2.1.0", | ||||
|     "routing-controllers-openapi": "^2.2.0", | ||||
|     "sqlite3": "5.0.0", | ||||
|     "typeorm": "^0.2.29", | ||||
|     "typeorm-routing-controllers-extensions": "^0.2.0", | ||||
|     "typeorm-seeding": "^1.6.1", | ||||
|     "uuid": "^8.3.1", | ||||
|     "uuid": "^8.3.2", | ||||
|     "validator": "^13.5.2" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@odit/license-exporter": "^0.0.8", | ||||
|     "@types/cors": "^2.8.8", | ||||
|     "@odit/license-exporter": "^0.0.9", | ||||
|     "@types/cors": "^2.8.9", | ||||
|     "@types/csvtojson": "^1.1.5", | ||||
|     "@types/express": "^4.17.9", | ||||
|     "@types/jest": "^26.0.16", | ||||
|     "@types/jsonwebtoken": "^8.5.0", | ||||
|     "@types/node": "^14.14.9", | ||||
|     "@types/node": "^14.14.20", | ||||
|     "@types/uuid": "^8.3.0", | ||||
|     "axios": "^0.21.0", | ||||
|     "axios": "^0.21.1", | ||||
|     "cp-cli": "^2.0.0", | ||||
|     "jest": "^26.6.3", | ||||
|     "nodemon": "^2.0.6", | ||||
|     "rimraf": "^2.7.1", | ||||
|     "start-server-and-test": "^1.11.6", | ||||
|     "nodemon": "^2.0.7", | ||||
|     "release-it": "^14.2.2", | ||||
|     "rimraf": "^3.0.2", | ||||
|     "start-server-and-test": "^1.11.7", | ||||
|     "ts-jest": "^26.4.4", | ||||
|     "ts-node": "^9.0.0", | ||||
|     "typedoc": "^0.19.2", | ||||
|     "typescript": "^4.1.2" | ||||
|     "ts-node": "^9.1.1", | ||||
|     "typedoc": "^0.20.14", | ||||
|     "typescript": "^4.1.3" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "dev": "nodemon src/app.ts", | ||||
| @@ -76,7 +77,21 @@ | ||||
|     "test:ci": "start-server-and-test dev http://localhost:4010/api/docs/openapi.json test", | ||||
|     "seed": "ts-node ./node_modules/typeorm/cli.js schema:sync && ts-node ./node_modules/typeorm-seeding/dist/cli.js seed", | ||||
|     "openapi:export": "ts-node scripts/openapi_export.ts", | ||||
|     "licenses:export": "license-exporter --md" | ||||
|     "licenses:export": "license-exporter --md", | ||||
|     "release": "release-it" | ||||
|   }, | ||||
|   "release-it": { | ||||
|     "git": { | ||||
|       "requireCleanWorkingDir": false, | ||||
|       "requireBranch": "main", | ||||
|       "push": false, | ||||
|       "tag": true, | ||||
|       "tagName": "v${version}", | ||||
|       "tagAnnotation": "v${version}" | ||||
|     }, | ||||
|     "npm": { | ||||
|       "publish": false | ||||
|     } | ||||
|   }, | ||||
|   "nodemonConfig": { | ||||
|     "ignore": [ | ||||
|   | ||||
| @@ -3,7 +3,7 @@ import consola from "consola"; | ||||
| import fs from "fs"; | ||||
| import "reflect-metadata"; | ||||
| import { createExpressServer, getMetadataArgsStorage } from "routing-controllers"; | ||||
| import { routingControllersToSpec } from 'routing-controllers-openapi'; | ||||
| import { generateSpec } from '../src/apispec'; | ||||
| import { config } from '../src/config'; | ||||
| import authchecker from "../src/middlewares/authchecker"; | ||||
| import { ErrorHandler } from '../src/middlewares/ErrorHandler'; | ||||
| @@ -24,46 +24,7 @@ const schemas = validationMetadatasToSchemas({ | ||||
| }); | ||||
|  | ||||
| //Spec creation based on the previously created schemas | ||||
| const spec = routingControllersToSpec( | ||||
|     storage, | ||||
|     { | ||||
|         routePrefix: "/api" | ||||
|     }, | ||||
|     { | ||||
|         components: { | ||||
|             schemas, | ||||
|             "securitySchemes": { | ||||
|                 "AuthToken": { | ||||
|                     "type": "http", | ||||
|                     "scheme": "bearer", | ||||
|                     "bearerFormat": "JWT", | ||||
|                     description: "A JWT based access token. Use /api/auth/login or /api/auth/refresh to get one." | ||||
|                 }, | ||||
|                 "RefreshTokenCookie": { | ||||
|                     "type": "apiKey", | ||||
|                     "in": "cookie", | ||||
|                     "name": "lfk_backend__refresh_token", | ||||
|                     description: "A cookie containing a JWT based refreh token. Attention: Doesn't work in swagger-ui. Use /api/auth/login or /api/auth/refresh to get one." | ||||
|                 }, | ||||
|                 "StatsApiToken": { | ||||
|                     "type": "http", | ||||
|                     "scheme": "bearer", | ||||
|                     description: "Api token that can be obtained by creating a new stats client (post to /api/statsclients). Only valid for obtaining stats." | ||||
|                 }, | ||||
|                 "StationApiToken": { | ||||
|                     "type": "http", | ||||
|                     "scheme": "bearer", | ||||
|                     description: "Api token that can be obtained by creating a new scan station (post to /api/stations). Only valid for creating scans." | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         info: { | ||||
|             description: "The the backend API for the LfK! runner system.", | ||||
|             title: "LfK! Backend API", | ||||
|             version: "0.0.8", | ||||
|         }, | ||||
|     } | ||||
| ); | ||||
| const spec = generateSpec(storage, schemas); | ||||
|  | ||||
| try { | ||||
|     fs.writeFileSync("./openapi.json", JSON.stringify(spec), { encoding: "utf-8" }); | ||||
|   | ||||
							
								
								
									
										50
									
								
								src/apispec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/apispec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| import { MetadataArgsStorage } from 'routing-controllers'; | ||||
| import { routingControllersToSpec } from 'routing-controllers-openapi'; | ||||
|  | ||||
| /** | ||||
|  * This function generates a the openapi spec from route metadata and type schemas. | ||||
|  * @param storage MetadataArgsStorage object generated by routing-controllers. | ||||
|  * @param schemas MetadataArgsStorage object generated by class-validator-jsonschema. | ||||
|  */ | ||||
| export function generateSpec(storage: MetadataArgsStorage, schemas) { | ||||
|     return routingControllersToSpec( | ||||
|         storage, | ||||
|         { | ||||
|             routePrefix: "/api" | ||||
|         }, | ||||
|         { | ||||
|             components: { | ||||
|                 schemas, | ||||
|                 "securitySchemes": { | ||||
|                     "AuthToken": { | ||||
|                         "type": "http", | ||||
|                         "scheme": "bearer", | ||||
|                         "bearerFormat": "JWT", | ||||
|                         description: "A JWT based access token. Use /api/auth/login or /api/auth/refresh to get one." | ||||
|                     }, | ||||
|                     "RefreshTokenCookie": { | ||||
|                         "type": "apiKey", | ||||
|                         "in": "cookie", | ||||
|                         "name": "lfk_backend__refresh_token", | ||||
|                         description: "A cookie containing a JWT based refreh token. Attention: Doesn't work in swagger-ui. Use /api/auth/login or /api/auth/refresh to get one." | ||||
|                     }, | ||||
|                     "StatsApiToken": { | ||||
|                         "type": "http", | ||||
|                         "scheme": "bearer", | ||||
|                         description: "Api token that can be obtained by creating a new stats client (post to /api/statsclients). Only valid for obtaining stats." | ||||
|                     }, | ||||
|                     "StationApiToken": { | ||||
|                         "type": "http", | ||||
|                         "scheme": "bearer", | ||||
|                         description: "Api token that can be obtained by creating a new scan station (post to /api/stations). Only valid for creating scans." | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             info: { | ||||
|                 description: "The the backend API for the LfK! runner system.", | ||||
|                 title: "LfK! Backend API", | ||||
|                 version: process.env.npm_package_version | ||||
|             }, | ||||
|         } | ||||
|     ); | ||||
| } | ||||
| @@ -2,12 +2,12 @@ import { Body, CookieParam, JsonController, Param, Post, Req, Res } from 'routin | ||||
| import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; | ||||
| import { IllegalJWTError, InvalidCredentialsError, JwtNotProvidedError, PasswordNeededError, RefreshTokenCountInvalidError, UsernameOrEmailNeededError } from '../errors/AuthError'; | ||||
| import { UserNotFoundError } from '../errors/UserErrors'; | ||||
| import { CreateAuth } from '../models/actions/CreateAuth'; | ||||
| import { CreateResetToken } from '../models/actions/CreateResetToken'; | ||||
| import { CreateAuth } from '../models/actions/create/CreateAuth'; | ||||
| import { CreateResetToken } from '../models/actions/create/CreateResetToken'; | ||||
| import { HandleLogout } from '../models/actions/HandleLogout'; | ||||
| import { RefreshAuth } from '../models/actions/RefreshAuth'; | ||||
| import { ResetPassword } from '../models/actions/ResetPassword'; | ||||
| import { Auth } from '../models/responses/ResponseAuth'; | ||||
| import { ResponseAuth } from '../models/responses/ResponseAuth'; | ||||
| import { Logout } from '../models/responses/ResponseLogout'; | ||||
|  | ||||
| @JsonController('/auth') | ||||
| @@ -16,7 +16,7 @@ export class AuthController { | ||||
| 	} | ||||
|  | ||||
| 	@Post("/login") | ||||
| 	@ResponseSchema(Auth) | ||||
| 	@ResponseSchema(ResponseAuth) | ||||
| 	@ResponseSchema(InvalidCredentialsError) | ||||
| 	@ResponseSchema(UserNotFoundError) | ||||
| 	@ResponseSchema(UsernameOrEmailNeededError) | ||||
| @@ -60,7 +60,7 @@ export class AuthController { | ||||
| 	} | ||||
|  | ||||
| 	@Post("/refresh") | ||||
| 	@ResponseSchema(Auth) | ||||
| 	@ResponseSchema(ResponseAuth) | ||||
| 	@ResponseSchema(JwtNotProvidedError) | ||||
| 	@ResponseSchema(IllegalJWTError) | ||||
| 	@ResponseSchema(UserNotFoundError) | ||||
| @@ -82,7 +82,7 @@ export class AuthController { | ||||
| 	} | ||||
|  | ||||
| 	@Post("/reset") | ||||
| 	@ResponseSchema(Auth) | ||||
| 	@ResponseSchema(ResponseAuth) | ||||
| 	@ResponseSchema(UserNotFoundError) | ||||
| 	@ResponseSchema(UsernameOrEmailNeededError) | ||||
| 	@OpenAPI({ description: "Request a password reset token. <br> This will provide you with a reset token that you can use by posting to /api/auth/reset/{token}." }) | ||||
| @@ -92,7 +92,7 @@ export class AuthController { | ||||
| 	} | ||||
|  | ||||
| 	@Post("/reset/:token") | ||||
| 	@ResponseSchema(Auth) | ||||
| 	@ResponseSchema(ResponseAuth) | ||||
| 	@ResponseSchema(UserNotFoundError) | ||||
| 	@ResponseSchema(UsernameOrEmailNeededError) | ||||
| 	@OpenAPI({ description: "Reset a user's utilising a valid password reset token. <br> This will set the user's password to the one you provided in the body. <br> To get a reset token post to /api/auth/reset with your username." }) | ||||
|   | ||||
| @@ -2,8 +2,8 @@ import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post | ||||
| import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; | ||||
| import { getConnectionManager, Repository } from 'typeorm'; | ||||
| import { DonorIdsNotMatchingError, DonorNotFoundError } from '../errors/DonorErrors'; | ||||
| import { CreateDonor } from '../models/actions/CreateDonor'; | ||||
| import { UpdateDonor } from '../models/actions/UpdateDonor'; | ||||
| import { CreateDonor } from '../models/actions/create/CreateDonor'; | ||||
| import { UpdateDonor } from '../models/actions/update/UpdateDonor'; | ||||
| import { Donor } from '../models/entities/Donor'; | ||||
| import { ResponseDonor } from '../models/responses/ResponseDonor'; | ||||
| import { ResponseEmpty } from '../models/responses/ResponseEmpty'; | ||||
| @@ -52,7 +52,7 @@ export class DonorController { | ||||
| 	async post(@Body({ validate: true }) createRunner: CreateDonor) { | ||||
| 		let donor; | ||||
| 		try { | ||||
| 			donor = await createRunner.toDonor(); | ||||
| 			donor = await createRunner.toEntity(); | ||||
| 		} catch (error) { | ||||
| 			throw error; | ||||
| 		} | ||||
| @@ -78,7 +78,7 @@ export class DonorController { | ||||
| 			throw new DonorIdsNotMatchingError(); | ||||
| 		} | ||||
|  | ||||
| 		await this.donorRepository.save(await donor.updateDonor(oldDonor)); | ||||
| 		await this.donorRepository.save(await donor.update(oldDonor)); | ||||
| 		return new ResponseDonor(await this.donorRepository.findOne({ id: id })); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -3,8 +3,8 @@ import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; | ||||
| import { getConnectionManager, Repository } from 'typeorm'; | ||||
| import { PermissionIdsNotMatchingError, PermissionNeedsPrincipalError, PermissionNotFoundError } from '../errors/PermissionErrors'; | ||||
| import { PrincipalNotFoundError } from '../errors/PrincipalErrors'; | ||||
| import { CreatePermission } from '../models/actions/CreatePermission'; | ||||
| import { UpdatePermission } from '../models/actions/UpdatePermission'; | ||||
| import { CreatePermission } from '../models/actions/create/CreatePermission'; | ||||
| import { UpdatePermission } from '../models/actions/update/UpdatePermission'; | ||||
| import { Permission } from '../models/entities/Permission'; | ||||
| import { ResponseEmpty } from '../models/responses/ResponseEmpty'; | ||||
| import { ResponsePermission } from '../models/responses/ResponsePermission'; | ||||
| @@ -58,7 +58,7 @@ export class PermissionController { | ||||
|     async post(@Body({ validate: true }) createPermission: CreatePermission) { | ||||
|         let permission; | ||||
|         try { | ||||
|             permission = await createPermission.toPermission(); | ||||
|             permission = await createPermission.toEntity(); | ||||
|         } catch (error) { | ||||
|             throw error; | ||||
|         } | ||||
| @@ -96,7 +96,7 @@ export class PermissionController { | ||||
|             return new ResponsePermission(existingPermission); | ||||
|         } | ||||
|  | ||||
|         await this.permissionRepository.save(await permission.updatePermission(oldPermission)); | ||||
|         await this.permissionRepository.save(await permission.update(oldPermission)); | ||||
|  | ||||
|         return new ResponsePermission(await this.permissionRepository.findOne({ id: permission.id }, { relations: ['principal'] })); | ||||
|     } | ||||
|   | ||||
							
								
								
									
										106
									
								
								src/controllers/RunnerCardController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								src/controllers/RunnerCardController.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers'; | ||||
| import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; | ||||
| import { getConnectionManager, Repository } from 'typeorm'; | ||||
| import { RunnerCardHasScansError, RunnerCardIdsNotMatchingError, RunnerCardNotFoundError } from '../errors/RunnerCardErrors'; | ||||
| import { RunnerNotFoundError } from '../errors/RunnerErrors'; | ||||
| import { CreateRunnerCard } from '../models/actions/create/CreateRunnerCard'; | ||||
| import { UpdateRunnerCard } from '../models/actions/update/UpdateRunnerCard'; | ||||
| import { RunnerCard } from '../models/entities/RunnerCard'; | ||||
| import { ResponseEmpty } from '../models/responses/ResponseEmpty'; | ||||
| import { ResponseRunnerCard } from '../models/responses/ResponseRunnerCard'; | ||||
| import { ScanController } from './ScanController'; | ||||
|  | ||||
| @JsonController('/cards') | ||||
| @OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) | ||||
| export class RunnerCardController { | ||||
| 	private cardRepository: Repository<RunnerCard>; | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets the repository of this controller's model/entity. | ||||
| 	 */ | ||||
| 	constructor() { | ||||
| 		this.cardRepository = getConnectionManager().get().getRepository(RunnerCard); | ||||
| 	} | ||||
|  | ||||
| 	@Get() | ||||
| 	@Authorized("CARD:GET") | ||||
| 	@ResponseSchema(ResponseRunnerCard, { isArray: true }) | ||||
| 	@OpenAPI({ description: 'Lists all card.' }) | ||||
| 	async getAll() { | ||||
| 		let responseCards: ResponseRunnerCard[] = new Array<ResponseRunnerCard>(); | ||||
| 		const cards = await this.cardRepository.find({ relations: ['runner'] }); | ||||
| 		cards.forEach(card => { | ||||
| 			responseCards.push(new ResponseRunnerCard(card)); | ||||
| 		}); | ||||
| 		return responseCards; | ||||
| 	} | ||||
|  | ||||
| 	@Get('/:id') | ||||
| 	@Authorized("CARD:GET") | ||||
| 	@ResponseSchema(ResponseRunnerCard) | ||||
| 	@ResponseSchema(RunnerCardNotFoundError, { statusCode: 404 }) | ||||
| 	@OnUndefined(RunnerCardNotFoundError) | ||||
| 	@OpenAPI({ description: "Lists all information about the card whose id got provided." }) | ||||
| 	async getOne(@Param('id') id: number) { | ||||
| 		let card = await this.cardRepository.findOne({ id: id }, { relations: ['runner'] }); | ||||
| 		if (!card) { throw new RunnerCardNotFoundError(); } | ||||
| 		return card.toResponse(); | ||||
| 	} | ||||
|  | ||||
| 	@Post() | ||||
| 	@Authorized("CARD:CREATE") | ||||
| 	@ResponseSchema(ResponseRunnerCard) | ||||
| 	@ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) | ||||
| 	@OpenAPI({ description: "Create a new card. <br> You can provide a associated runner by id but you don't have to." }) | ||||
| 	async post(@Body({ validate: true }) createCard: CreateRunnerCard) { | ||||
| 		let card = await createCard.toEntity(); | ||||
| 		card = await this.cardRepository.save(card); | ||||
| 		return (await this.cardRepository.findOne({ id: card.id }, { relations: ['runner'] })).toResponse(); | ||||
| 	} | ||||
|  | ||||
| 	@Put('/:id') | ||||
| 	@Authorized("CARD:UPDATE") | ||||
| 	@ResponseSchema(ResponseRunnerCard) | ||||
| 	@ResponseSchema(RunnerCardNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(RunnerCardIdsNotMatchingError, { statusCode: 406 }) | ||||
| 	@OpenAPI({ description: "Update the card whose id you provided. <br> Scans created via this card will still be associated with the old runner. <br> Please remember that ids can't be changed." }) | ||||
| 	async put(@Param('id') id: number, @Body({ validate: true }) card: UpdateRunnerCard) { | ||||
| 		let oldCard = await this.cardRepository.findOne({ id: id }); | ||||
|  | ||||
| 		if (!oldCard) { | ||||
| 			throw new RunnerCardNotFoundError(); | ||||
| 		} | ||||
|  | ||||
| 		if (oldCard.id != card.id) { | ||||
| 			throw new RunnerCardIdsNotMatchingError(); | ||||
| 		} | ||||
|  | ||||
| 		await this.cardRepository.save(await card.update(oldCard)); | ||||
| 		return (await this.cardRepository.findOne({ id: id }, { relations: ['runner'] })).toResponse(); | ||||
| 	} | ||||
|  | ||||
| 	@Delete('/:id') | ||||
| 	@Authorized("CARD:DELETE") | ||||
| 	@ResponseSchema(ResponseRunnerCard) | ||||
| 	@ResponseSchema(ResponseEmpty, { statusCode: 204 }) | ||||
| 	@ResponseSchema(RunnerCardHasScansError, { statusCode: 406 }) | ||||
| 	@OnUndefined(204) | ||||
| 	@OpenAPI({ description: "Delete the card whose id you provided. <br> If no card with this id exists it will just return 204(no content). <br> If the card still has scans associated you have to provide the force=true query param (warning: this deletes all scans associated with by this card - please disable it instead or just remove the runner association)." }) | ||||
| 	async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { | ||||
| 		let card = await this.cardRepository.findOne({ id: id }); | ||||
| 		if (!card) { return null; } | ||||
|  | ||||
| 		const cardScans = (await this.cardRepository.findOne({ id: id }, { relations: ["scans"] })).scans; | ||||
| 		if (cardScans.length != 0 && !force) { | ||||
| 			throw new RunnerCardHasScansError(); | ||||
| 		} | ||||
| 		const scanController = new ScanController; | ||||
| 		for (let scan of cardScans) { | ||||
| 			await scanController.remove(scan.id, force); | ||||
| 		} | ||||
|  | ||||
| 		await this.cardRepository.delete(card); | ||||
| 		return card.toResponse(); | ||||
| 	} | ||||
| } | ||||
| @@ -3,11 +3,13 @@ import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; | ||||
| import { getConnectionManager, Repository } from 'typeorm'; | ||||
| import { RunnerGroupNeededError, RunnerIdsNotMatchingError, RunnerNotFoundError } from '../errors/RunnerErrors'; | ||||
| import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors'; | ||||
| import { CreateRunner } from '../models/actions/CreateRunner'; | ||||
| import { UpdateRunner } from '../models/actions/UpdateRunner'; | ||||
| import { CreateRunner } from '../models/actions/create/CreateRunner'; | ||||
| import { UpdateRunner } from '../models/actions/update/UpdateRunner'; | ||||
| import { Runner } from '../models/entities/Runner'; | ||||
| import { ResponseEmpty } from '../models/responses/ResponseEmpty'; | ||||
| import { ResponseRunner } from '../models/responses/ResponseRunner'; | ||||
| import { RunnerCardController } from './RunnerCardController'; | ||||
| import { ScanController } from './ScanController'; | ||||
|  | ||||
| @JsonController('/runners') | ||||
| @OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) | ||||
| @@ -27,7 +29,7 @@ export class RunnerController { | ||||
| 	@OpenAPI({ description: 'Lists all runners from all teams/orgs. <br> This includes the runner\'s group and distance ran.' }) | ||||
| 	async getAll() { | ||||
| 		let responseRunners: ResponseRunner[] = new Array<ResponseRunner>(); | ||||
| 		const runners = await this.runnerRepository.find({ relations: ['scans', 'group'] }); | ||||
| 		const runners = await this.runnerRepository.find({ relations: ['scans', 'group', 'scans.track', 'cards'] }); | ||||
| 		runners.forEach(runner => { | ||||
| 			responseRunners.push(new ResponseRunner(runner)); | ||||
| 		}); | ||||
| @@ -41,7 +43,7 @@ export class RunnerController { | ||||
| 	@OnUndefined(RunnerNotFoundError) | ||||
| 	@OpenAPI({ description: 'Lists all information about the runner whose id got provided.' }) | ||||
| 	async getOne(@Param('id') id: number) { | ||||
| 		let runner = await this.runnerRepository.findOne({ id: id }, { relations: ['scans', 'group'] }) | ||||
| 		let runner = await this.runnerRepository.findOne({ id: id }, { relations: ['scans', 'group', 'scans.track', 'cards'] }) | ||||
| 		if (!runner) { throw new RunnerNotFoundError(); } | ||||
| 		return new ResponseRunner(runner); | ||||
| 	} | ||||
| @@ -55,13 +57,13 @@ export class RunnerController { | ||||
| 	async post(@Body({ validate: true }) createRunner: CreateRunner) { | ||||
| 		let runner; | ||||
| 		try { | ||||
| 			runner = await createRunner.toRunner(); | ||||
| 			runner = await createRunner.toEntity(); | ||||
| 		} catch (error) { | ||||
| 			throw error; | ||||
| 		} | ||||
|  | ||||
| 		runner = await this.runnerRepository.save(runner) | ||||
| 		return new ResponseRunner(await this.runnerRepository.findOne(runner, { relations: ['scans', 'group'] })); | ||||
| 		return new ResponseRunner(await this.runnerRepository.findOne(runner, { relations: ['scans', 'group', 'scans.track', 'cards'] })); | ||||
| 	} | ||||
|  | ||||
| 	@Put('/:id') | ||||
| @@ -81,8 +83,8 @@ export class RunnerController { | ||||
| 			throw new RunnerIdsNotMatchingError(); | ||||
| 		} | ||||
|  | ||||
| 		await this.runnerRepository.save(await runner.updateRunner(oldRunner)); | ||||
| 		return new ResponseRunner(await this.runnerRepository.findOne({ id: id }, { relations: ['scans', 'group'] })); | ||||
| 		await this.runnerRepository.save(await runner.update(oldRunner)); | ||||
| 		return new ResponseRunner(await this.runnerRepository.findOne({ id: id }, { relations: ['scans', 'group', 'scans.track', 'cards'] })); | ||||
| 	} | ||||
|  | ||||
| 	@Delete('/:id') | ||||
| @@ -90,16 +92,28 @@ export class RunnerController { | ||||
| 	@ResponseSchema(ResponseRunner) | ||||
| 	@ResponseSchema(ResponseEmpty, { statusCode: 204 }) | ||||
| 	@OnUndefined(204) | ||||
| 	@OpenAPI({ description: 'Delete the runner whose id you provided. <br> If no runner with this id exists it will just return 204(no content).' }) | ||||
| 	@OpenAPI({ description: 'Delete the runner whose id you provided. <br> This will also delete all scans and cards associated with the runner. <br> If no runner with this id exists it will just return 204(no content).' }) | ||||
| 	async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { | ||||
| 		let runner = await this.runnerRepository.findOne({ id: id }); | ||||
| 		if (!runner) { return null; } | ||||
| 		const responseRunner = await this.runnerRepository.findOne(runner, { relations: ['scans', 'group'] }); | ||||
| 		const responseRunner = await this.runnerRepository.findOne(runner, { relations: ['scans', 'group', 'scans.track', 'cards'] }); | ||||
|  | ||||
| 		if (!runner) { | ||||
| 			throw new RunnerNotFoundError(); | ||||
| 		} | ||||
|  | ||||
| 		const runnerCards = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["cards"] })).cards; | ||||
| 		const cardController = new RunnerCardController; | ||||
| 		for (let scan of runnerCards) { | ||||
| 			await cardController.remove(scan.id, force); | ||||
| 		} | ||||
|  | ||||
| 		const runnerScans = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["scans"] })).scans; | ||||
| 		const scanController = new ScanController; | ||||
| 		for (let scan of runnerScans) { | ||||
| 			await scanController.remove(scan.id, force); | ||||
| 		} | ||||
|  | ||||
| 		await this.runnerRepository.delete(runner); | ||||
| 		return new ResponseRunner(responseRunner); | ||||
| 	} | ||||
|   | ||||
| @@ -2,8 +2,8 @@ import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post | ||||
| import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; | ||||
| import { getConnectionManager, Repository } from 'typeorm'; | ||||
| import { RunnerOrganisationHasRunnersError, RunnerOrganisationHasTeamsError, RunnerOrganisationIdsNotMatchingError, RunnerOrganisationNotFoundError } from '../errors/RunnerOrganisationErrors'; | ||||
| import { CreateRunnerOrganisation } from '../models/actions/CreateRunnerOrganisation'; | ||||
| import { UpdateRunnerOrganisation } from '../models/actions/UpdateRunnerOrganisation'; | ||||
| import { CreateRunnerOrganisation } from '../models/actions/create/CreateRunnerOrganisation'; | ||||
| import { UpdateRunnerOrganisation } from '../models/actions/update/UpdateRunnerOrganisation'; | ||||
| import { RunnerOrganisation } from '../models/entities/RunnerOrganisation'; | ||||
| import { ResponseEmpty } from '../models/responses/ResponseEmpty'; | ||||
| import { ResponseRunnerOrganisation } from '../models/responses/ResponseRunnerOrganisation'; | ||||
| @@ -55,7 +55,7 @@ export class RunnerOrganisationController { | ||||
| 	async post(@Body({ validate: true }) createRunnerOrganisation: CreateRunnerOrganisation) { | ||||
| 		let runnerOrganisation; | ||||
| 		try { | ||||
| 			runnerOrganisation = await createRunnerOrganisation.toRunnerOrganisation(); | ||||
| 			runnerOrganisation = await createRunnerOrganisation.toEntity(); | ||||
| 		} catch (error) { | ||||
| 			throw error; | ||||
| 		} | ||||
| @@ -82,7 +82,7 @@ export class RunnerOrganisationController { | ||||
| 			throw new RunnerOrganisationIdsNotMatchingError(); | ||||
| 		} | ||||
|  | ||||
| 		await this.runnerOrganisationRepository.save(await updateOrganisation.updateRunnerOrganisation(oldRunnerOrganisation)); | ||||
| 		await this.runnerOrganisationRepository.save(await updateOrganisation.update(oldRunnerOrganisation)); | ||||
|  | ||||
| 		return new ResponseRunnerOrganisation(await this.runnerOrganisationRepository.findOne(id, { relations: ['address', 'contact', 'teams'] })); | ||||
| 	} | ||||
|   | ||||
| @@ -2,8 +2,8 @@ import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post | ||||
| import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; | ||||
| import { getConnectionManager, Repository } from 'typeorm'; | ||||
| import { RunnerTeamHasRunnersError, RunnerTeamIdsNotMatchingError, RunnerTeamNotFoundError } from '../errors/RunnerTeamErrors'; | ||||
| import { CreateRunnerTeam } from '../models/actions/CreateRunnerTeam'; | ||||
| import { UpdateRunnerTeam } from '../models/actions/UpdateRunnerTeam'; | ||||
| import { CreateRunnerTeam } from '../models/actions/create/CreateRunnerTeam'; | ||||
| import { UpdateRunnerTeam } from '../models/actions/update/UpdateRunnerTeam'; | ||||
| import { RunnerTeam } from '../models/entities/RunnerTeam'; | ||||
| import { ResponseEmpty } from '../models/responses/ResponseEmpty'; | ||||
| import { ResponseRunnerTeam } from '../models/responses/ResponseRunnerTeam'; | ||||
| @@ -54,7 +54,7 @@ export class RunnerTeamController { | ||||
| 	async post(@Body({ validate: true }) createRunnerTeam: CreateRunnerTeam) { | ||||
| 		let runnerTeam; | ||||
| 		try { | ||||
| 			runnerTeam = await createRunnerTeam.toRunnerTeam(); | ||||
| 			runnerTeam = await createRunnerTeam.toEntity(); | ||||
| 		} catch (error) { | ||||
| 			throw error; | ||||
| 		} | ||||
| @@ -82,7 +82,7 @@ export class RunnerTeamController { | ||||
| 			throw new RunnerTeamIdsNotMatchingError(); | ||||
| 		} | ||||
|  | ||||
| 		await this.runnerTeamRepository.save(await runnerTeam.updateRunnerTeam(oldRunnerTeam)); | ||||
| 		await this.runnerTeamRepository.save(await runnerTeam.update(oldRunnerTeam)); | ||||
|  | ||||
| 		return new ResponseRunnerTeam(await this.runnerTeamRepository.findOne({ id: runnerTeam.id }, { relations: ['parentGroup', 'contact'] })); | ||||
| 	} | ||||
|   | ||||
| @@ -3,11 +3,14 @@ import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; | ||||
| import { getConnectionManager, Repository } from 'typeorm'; | ||||
| import { RunnerNotFoundError } from '../errors/RunnerErrors'; | ||||
| import { ScanIdsNotMatchingError, ScanNotFoundError } from '../errors/ScanErrors'; | ||||
| import { ScanStationNotFoundError } from '../errors/ScanStationErrors'; | ||||
| import ScanAuth from '../middlewares/ScanAuth'; | ||||
| import { CreateScan } from '../models/actions/CreateScan'; | ||||
| import { CreateTrackScan } from '../models/actions/CreateTrackScan'; | ||||
| import { UpdateScan } from '../models/actions/UpdateScan'; | ||||
| import { CreateScan } from '../models/actions/create/CreateScan'; | ||||
| import { CreateTrackScan } from '../models/actions/create/CreateTrackScan'; | ||||
| import { UpdateScan } from '../models/actions/update/UpdateScan'; | ||||
| import { UpdateTrackScan } from '../models/actions/update/UpdateTrackScan'; | ||||
| import { Scan } from '../models/entities/Scan'; | ||||
| import { TrackScan } from '../models/entities/TrackScan'; | ||||
| import { ResponseEmpty } from '../models/responses/ResponseEmpty'; | ||||
| import { ResponseScan } from '../models/responses/ResponseScan'; | ||||
| import { ResponseTrackScan } from '../models/responses/ResponseTrackScan'; | ||||
| @@ -16,12 +19,14 @@ import { ResponseTrackScan } from '../models/responses/ResponseTrackScan'; | ||||
| @OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) | ||||
| export class ScanController { | ||||
| 	private scanRepository: Repository<Scan>; | ||||
| 	private trackScanRepository: Repository<TrackScan>; | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets the repository of this controller's model/entity. | ||||
| 	 */ | ||||
| 	constructor() { | ||||
| 		this.scanRepository = getConnectionManager().get().getRepository(Scan); | ||||
| 		this.trackScanRepository = getConnectionManager().get().getRepository(TrackScan); | ||||
| 	} | ||||
|  | ||||
| 	@Get() | ||||
| @@ -31,7 +36,7 @@ export class ScanController { | ||||
| 	@OpenAPI({ description: 'Lists all scans (normal or track) from all runners. <br> This includes the scan\'s runner\'s distance ran.' }) | ||||
| 	async getAll() { | ||||
| 		let responseScans: ResponseScan[] = new Array<ResponseScan>(); | ||||
| 		const scans = await this.scanRepository.find({ relations: ['runner', 'runner.scans', 'runner.scans.track'] }); | ||||
| 		const scans = await this.scanRepository.find({ relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] }); | ||||
| 		scans.forEach(scan => { | ||||
| 			responseScans.push(scan.toResponse()); | ||||
| 		}); | ||||
| @@ -46,7 +51,7 @@ export class ScanController { | ||||
| 	@OnUndefined(ScanNotFoundError) | ||||
| 	@OpenAPI({ description: 'Lists all information about the scan whose id got provided. This includes the scan\'s runner\'s distance ran.' }) | ||||
| 	async getOne(@Param('id') id: number) { | ||||
| 		let scan = await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'runner.scans', 'runner.scans.track'] }) | ||||
| 		let scan = await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] }) | ||||
| 		if (!scan) { throw new ScanNotFoundError(); } | ||||
| 		return scan.toResponse(); | ||||
| 	} | ||||
| @@ -55,20 +60,22 @@ export class ScanController { | ||||
| 	@UseBefore(ScanAuth) | ||||
| 	@ResponseSchema(ResponseScan) | ||||
| 	@ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) | ||||
| 	@OpenAPI({ description: 'Create a new scan. <br> Please remeber to provide the scan\'s runner\'s id and distance for normal scans.', security: [{ "ScanApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) | ||||
| 	@OpenAPI({ description: 'Create a new scan (not track scan - use /scans/trackscans instead). <br> Please rmemember to provide the scan\'s runner\'s id and distance.', security: [{ "ScanApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) | ||||
| 	async post(@Body({ validate: true }) createScan: CreateScan) { | ||||
| 		let scan = await createScan.toScan(); | ||||
| 		let scan = await createScan.toEntity(); | ||||
| 		scan = await this.scanRepository.save(scan); | ||||
| 		return (await this.scanRepository.findOne({ id: scan.id }, { relations: ['runner'] })).toResponse(); | ||||
| 		return (await this.scanRepository.findOne({ id: scan.id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] })).toResponse(); | ||||
| 	} | ||||
|  | ||||
| 	@Post("/trackscans") | ||||
| 	@UseBefore(ScanAuth) | ||||
| 	@ResponseSchema(ResponseScan) | ||||
| 	@ResponseSchema(ResponseTrackScan) | ||||
| 	@ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) | ||||
| 	@OpenAPI({ description: 'Create a new track scan. <br> This is just a alias for posting /scans', security: [{ "ScanApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) | ||||
| 	@OpenAPI({ description: 'Create a new track scan (for "normal" scans use /scans instead). <br> Please remember that to provide the scan\'s card\'s station\'s id.', security: [{ "ScanApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) | ||||
| 	async postTrackScans(@Body({ validate: true }) createScan: CreateTrackScan) { | ||||
| 		return this.post(createScan); | ||||
| 		let scan = await createScan.toEntity(); | ||||
| 		scan = await this.trackScanRepository.save(scan); | ||||
| 		return (await this.scanRepository.findOne({ id: scan.id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] })).toResponse(); | ||||
| 	} | ||||
|  | ||||
| 	@Put('/:id') | ||||
| @@ -77,7 +84,7 @@ export class ScanController { | ||||
| 	@ResponseSchema(ScanNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(ScanIdsNotMatchingError, { statusCode: 406 }) | ||||
| 	@OpenAPI({ description: "Update the scan whose id you provided. <br> Please remember that ids can't be changed and distances must be positive." }) | ||||
| 	@OpenAPI({ description: "Update the scan (not track scan use /scans/trackscans/:id instead) whose id you provided. <br> Please remember that ids can't be changed and distances must be positive." }) | ||||
| 	async put(@Param('id') id: number, @Body({ validate: true }) scan: UpdateScan) { | ||||
| 		let oldScan = await this.scanRepository.findOne({ id: id }); | ||||
|  | ||||
| @@ -89,8 +96,31 @@ export class ScanController { | ||||
| 			throw new ScanIdsNotMatchingError(); | ||||
| 		} | ||||
|  | ||||
| 		await this.scanRepository.save(await scan.updateScan(oldScan)); | ||||
| 		return (await this.scanRepository.findOne({ id: id }, { relations: ['runner'] })).toResponse(); | ||||
| 		await this.scanRepository.save(await scan.update(oldScan)); | ||||
| 		return (await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] })).toResponse(); | ||||
| 	} | ||||
|  | ||||
| 	@Put('/trackscans/:id') | ||||
| 	@Authorized("SCAN:UPDATE") | ||||
| 	@ResponseSchema(ResponseTrackScan) | ||||
| 	@ResponseSchema(ScanNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(ScanStationNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(ScanIdsNotMatchingError, { statusCode: 406 }) | ||||
| 	@OpenAPI({ description: 'Update the track scan (not "normal" scan use /scans/trackscans/:id instead) whose id you provided. <br> Please remember that only the validity, runner and track can be changed.' }) | ||||
| 	async putTrackScan(@Param('id') id: number, @Body({ validate: true }) scan: UpdateTrackScan) { | ||||
| 		let oldScan = await this.trackScanRepository.findOne({ id: id }); | ||||
|  | ||||
| 		if (!oldScan) { | ||||
| 			throw new ScanNotFoundError(); | ||||
| 		} | ||||
|  | ||||
| 		if (oldScan.id != scan.id) { | ||||
| 			throw new ScanIdsNotMatchingError(); | ||||
| 		} | ||||
|  | ||||
| 		await this.trackScanRepository.save(await scan.update(oldScan)); | ||||
| 		return (await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] })).toResponse(); | ||||
| 	} | ||||
|  | ||||
| 	@Delete('/:id') | ||||
| @@ -102,7 +132,7 @@ export class ScanController { | ||||
| 	async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { | ||||
| 		let scan = await this.scanRepository.findOne({ id: id }); | ||||
| 		if (!scan) { return null; } | ||||
| 		const responseScan = await this.scanRepository.findOne({ id: scan.id }, { relations: ["runner"] }); | ||||
| 		const responseScan = await this.scanRepository.findOne({ id: scan.id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] }); | ||||
|  | ||||
| 		await this.scanRepository.delete(scan); | ||||
| 		return responseScan.toResponse(); | ||||
|   | ||||
| @@ -3,8 +3,8 @@ import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; | ||||
| import { getConnectionManager, Repository } from 'typeorm'; | ||||
| import { ScanStationHasScansError, ScanStationIdsNotMatchingError, ScanStationNotFoundError } from '../errors/ScanStationErrors'; | ||||
| import { TrackNotFoundError } from '../errors/TrackErrors'; | ||||
| import { CreateScanStation } from '../models/actions/CreateScanStation'; | ||||
| import { UpdateScanStation } from '../models/actions/UpdateScanStation'; | ||||
| import { CreateScanStation } from '../models/actions/create/CreateScanStation'; | ||||
| import { UpdateScanStation } from '../models/actions/update/UpdateScanStation'; | ||||
| import { ScanStation } from '../models/entities/ScanStation'; | ||||
| import { ResponseEmpty } from '../models/responses/ResponseEmpty'; | ||||
| import { ResponseScanStation } from '../models/responses/ResponseScanStation'; | ||||
| @@ -77,7 +77,7 @@ export class ScanStationController { | ||||
| 			throw new ScanStationIdsNotMatchingError(); | ||||
| 		} | ||||
|  | ||||
| 		await this.stationRepository.save(await station.updateStation(oldStation)); | ||||
| 		await this.stationRepository.save(await station.update(oldStation)); | ||||
| 		return (await this.stationRepository.findOne({ id: id }, { relations: ['track'] })).toResponse(); | ||||
| 	} | ||||
|  | ||||
| @@ -98,7 +98,7 @@ export class ScanStationController { | ||||
| 		} | ||||
| 		const scanController = new ScanController; | ||||
| 		for (let scan of stationScans) { | ||||
| 			scanController.remove(scan.id, force); | ||||
| 			await scanController.remove(scan.id, force); | ||||
| 		} | ||||
|  | ||||
| 		const responseStation = await this.stationRepository.findOne({ id: station.id }, { relations: ["track"] }); | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post } from 'routing-controllers'; | ||||
| import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, QueryParam } from 'routing-controllers'; | ||||
| import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; | ||||
| import { getConnectionManager, Repository } from 'typeorm'; | ||||
| import { StatsClientNotFoundError } from '../errors/StatsClientErrors'; | ||||
| import { TrackNotFoundError } from "../errors/TrackErrors"; | ||||
| import { CreateStatsClient } from '../models/actions/CreateStatsClient'; | ||||
| import { CreateStatsClient } from '../models/actions/create/CreateStatsClient'; | ||||
| import { StatsClient } from '../models/entities/StatsClient'; | ||||
| import { ResponseEmpty } from '../models/responses/ResponseEmpty'; | ||||
| import { ResponseStatsClient } from '../models/responses/ResponseStatsClient'; | ||||
| @@ -53,7 +53,7 @@ export class StatsClientController { | ||||
| 		@Body({ validate: true }) | ||||
| 		client: CreateStatsClient | ||||
| 	) { | ||||
| 		let newClient = await this.clientRepository.save(await client.toStatsClient()); | ||||
| 		let newClient = await this.clientRepository.save(await client.toEntity()); | ||||
| 		let responseClient = new ResponseStatsClient(newClient); | ||||
| 		responseClient.key = newClient.cleartextkey; | ||||
| 		return responseClient; | ||||
| @@ -65,7 +65,7 @@ export class StatsClientController { | ||||
| 	@ResponseSchema(ResponseEmpty, { statusCode: 204 }) | ||||
| 	@OnUndefined(204) | ||||
| 	@OpenAPI({ description: "Delete the stats client whose id you provided. <br> If no client with this id exists it will just return 204(no content)." }) | ||||
| 	async remove(@Param("id") id: number) { | ||||
| 	async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { | ||||
| 		let client = await this.clientRepository.findOne({ id: id }); | ||||
| 		if (!client) { return null; } | ||||
|  | ||||
|   | ||||
| @@ -2,8 +2,8 @@ import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post | ||||
| import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; | ||||
| import { getConnectionManager, Repository } from 'typeorm'; | ||||
| import { TrackHasScanStationsError, TrackIdsNotMatchingError, TrackLapTimeCantBeNegativeError, TrackNotFoundError } from "../errors/TrackErrors"; | ||||
| import { CreateTrack } from '../models/actions/CreateTrack'; | ||||
| import { UpdateTrack } from '../models/actions/UpdateTrack'; | ||||
| import { CreateTrack } from '../models/actions/create/CreateTrack'; | ||||
| import { UpdateTrack } from '../models/actions/update/UpdateTrack'; | ||||
| import { Track } from '../models/entities/Track'; | ||||
| import { ResponseEmpty } from '../models/responses/ResponseEmpty'; | ||||
| import { ResponseTrack } from '../models/responses/ResponseTrack'; | ||||
| @@ -55,7 +55,7 @@ export class TrackController { | ||||
| 		@Body({ validate: true }) | ||||
| 		track: CreateTrack | ||||
| 	) { | ||||
| 		return new ResponseTrack(await this.trackRepository.save(track.toTrack())); | ||||
| 		return new ResponseTrack(await this.trackRepository.save(await track.toEntity())); | ||||
| 	} | ||||
|  | ||||
| 	@Put('/:id') | ||||
| @@ -75,7 +75,7 @@ export class TrackController { | ||||
| 		if (oldTrack.id != updateTrack.id) { | ||||
| 			throw new TrackIdsNotMatchingError(); | ||||
| 		} | ||||
| 		await this.trackRepository.save(await updateTrack.updateTrack(oldTrack)); | ||||
| 		await this.trackRepository.save(await updateTrack.update(oldTrack)); | ||||
|  | ||||
| 		return new ResponseTrack(await this.trackRepository.findOne({ id: id })); | ||||
| 	} | ||||
| @@ -94,9 +94,9 @@ export class TrackController { | ||||
| 		if (trackStations.length != 0 && !force) { | ||||
| 			throw new TrackHasScanStationsError(); | ||||
| 		} | ||||
| 		const scanController = new ScanStationController; | ||||
| 		const stationController = new ScanStationController; | ||||
| 		for (let station of trackStations) { | ||||
| 			scanController.remove(station.id, force); | ||||
| 			await stationController.remove(station.id, force); | ||||
| 		} | ||||
|  | ||||
| 		await this.trackRepository.delete(track); | ||||
|   | ||||
| @@ -3,8 +3,8 @@ import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; | ||||
| import { getConnectionManager, Repository } from 'typeorm'; | ||||
| import { UserIdsNotMatchingError, UserNotFoundError } from '../errors/UserErrors'; | ||||
| import { UserGroupNotFoundError } from '../errors/UserGroupErrors'; | ||||
| import { CreateUser } from '../models/actions/CreateUser'; | ||||
| import { UpdateUser } from '../models/actions/UpdateUser'; | ||||
| import { CreateUser } from '../models/actions/create/CreateUser'; | ||||
| import { UpdateUser } from '../models/actions/update/UpdateUser'; | ||||
| import { User } from '../models/entities/User'; | ||||
| import { ResponseEmpty } from '../models/responses/ResponseEmpty'; | ||||
| import { ResponseUser } from '../models/responses/ResponseUser'; | ||||
| @@ -25,7 +25,7 @@ export class UserController { | ||||
|  | ||||
| 	@Get() | ||||
| 	@Authorized("USER:GET") | ||||
| 	@ResponseSchema(User, { isArray: true }) | ||||
| 	@ResponseSchema(ResponseUser, { isArray: true }) | ||||
| 	@OpenAPI({ description: 'Lists all users. <br> This includes their groups and permissions directly granted to them (if existing/associated).' }) | ||||
| 	async getAll() { | ||||
| 		let responseUsers: ResponseUser[] = new Array<ResponseUser>(); | ||||
| @@ -38,7 +38,7 @@ export class UserController { | ||||
|  | ||||
| 	@Get('/:id') | ||||
| 	@Authorized("USER:GET") | ||||
| 	@ResponseSchema(User) | ||||
| 	@ResponseSchema(ResponseUser) | ||||
| 	@ResponseSchema(UserNotFoundError, { statusCode: 404 }) | ||||
| 	@OnUndefined(UserNotFoundError) | ||||
| 	@OpenAPI({ description: 'Lists all information about the user whose id got provided. <br> Please remember that only permissions granted directly to the user will show up here, not permissions inherited from groups.' }) | ||||
| @@ -50,13 +50,13 @@ export class UserController { | ||||
|  | ||||
| 	@Post() | ||||
| 	@Authorized("USER:CREATE") | ||||
| 	@ResponseSchema(User) | ||||
| 	@ResponseSchema(ResponseUser) | ||||
| 	@ResponseSchema(UserGroupNotFoundError) | ||||
| 	@OpenAPI({ description: 'Create a new user. <br> If you want to grant permissions to the user you have to create them seperately by posting to /api/permissions after creating the user.' }) | ||||
| 	async post(@Body({ validate: true }) createUser: CreateUser) { | ||||
| 		let user; | ||||
| 		try { | ||||
| 			user = await createUser.toUser(); | ||||
| 			user = await createUser.toEntity(); | ||||
| 		} catch (error) { | ||||
| 			throw error; | ||||
| 		} | ||||
| @@ -67,7 +67,7 @@ export class UserController { | ||||
|  | ||||
| 	@Put('/:id') | ||||
| 	@Authorized("USER:UPDATE") | ||||
| 	@ResponseSchema(User) | ||||
| 	@ResponseSchema(ResponseUser) | ||||
| 	@ResponseSchema(UserNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(UserIdsNotMatchingError, { statusCode: 406 }) | ||||
| 	@OpenAPI({ description: "Update the user whose id you provided. <br> To change the permissions directly granted to the user please use /api/permissions instead. <br> Please remember that ids can't be changed." }) | ||||
| @@ -81,14 +81,14 @@ export class UserController { | ||||
| 		if (oldUser.id != updateUser.id) { | ||||
| 			throw new UserIdsNotMatchingError(); | ||||
| 		} | ||||
| 		await this.userRepository.save(await updateUser.updateUser(oldUser)); | ||||
| 		await this.userRepository.save(await updateUser.update(oldUser)); | ||||
|  | ||||
| 		return new ResponseUser(await this.userRepository.findOne({ id: id }, { relations: ['permissions', 'groups'] })); | ||||
| 	} | ||||
|  | ||||
| 	@Delete('/:id') | ||||
| 	@Authorized("USER:DELETE") | ||||
| 	@ResponseSchema(User) | ||||
| 	@ResponseSchema(ResponseUser) | ||||
| 	@ResponseSchema(ResponseEmpty, { statusCode: 204 }) | ||||
| 	@OnUndefined(204) | ||||
| 	@OpenAPI({ description: 'Delete the user whose id you provided. <br> If there are any permissions directly granted to the user they will get deleted as well. <br> If no user with this id exists it will just return 204(no content).' }) | ||||
|   | ||||
| @@ -3,7 +3,8 @@ import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; | ||||
| import { getConnectionManager, Repository } from 'typeorm'; | ||||
| import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; | ||||
| import { UserGroupIdsNotMatchingError, UserGroupNotFoundError } from '../errors/UserGroupErrors'; | ||||
| import { CreateUserGroup } from '../models/actions/CreateUserGroup'; | ||||
| import { CreateUserGroup } from '../models/actions/create/CreateUserGroup'; | ||||
| import { UpdateUserGroup } from '../models/actions/update/UpdateUserGroup'; | ||||
| import { UserGroup } from '../models/entities/UserGroup'; | ||||
| import { ResponseEmpty } from '../models/responses/ResponseEmpty'; | ||||
| import { ResponseUserGroup } from '../models/responses/ResponseUserGroup'; | ||||
| @@ -48,7 +49,7 @@ export class UserGroupController { | ||||
| 	async post(@Body({ validate: true }) createUserGroup: CreateUserGroup) { | ||||
| 		let userGroup; | ||||
| 		try { | ||||
| 			userGroup = await createUserGroup.toUserGroup(); | ||||
| 			userGroup = await createUserGroup.toEntity(); | ||||
| 		} catch (error) { | ||||
| 			throw error; | ||||
| 		} | ||||
| @@ -62,19 +63,19 @@ export class UserGroupController { | ||||
| 	@ResponseSchema(UserGroupNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(UserGroupIdsNotMatchingError, { statusCode: 406 }) | ||||
| 	@OpenAPI({ description: "Update the group whose id you provided. <br> To change the permissions granted to the group please use /api/permissions instead. <br> Please remember that ids can't be changed." }) | ||||
| 	async put(@Param('id') id: number, @EntityFromBody() userGroup: UserGroup) { | ||||
| 		let oldUserGroup = await this.userGroupsRepository.findOne({ id: id }, { relations: ["permissions"] }); | ||||
| 	async put(@Param('id') id: number, @EntityFromBody() updateGroup: UpdateUserGroup) { | ||||
| 		let oldGroup = await this.userGroupsRepository.findOne({ id: id }); | ||||
|  | ||||
| 		if (!oldUserGroup) { | ||||
| 			throw new UserGroupNotFoundError() | ||||
| 		if (!oldGroup) { | ||||
| 			throw new UserGroupNotFoundError(); | ||||
| 		} | ||||
|  | ||||
| 		if (oldUserGroup.id != userGroup.id) { | ||||
| 		if (oldGroup.id != updateGroup.id) { | ||||
| 			throw new UserGroupIdsNotMatchingError(); | ||||
| 		} | ||||
| 		await this.userGroupsRepository.save(await updateGroup.update(oldGroup)); | ||||
|  | ||||
| 		await this.userGroupsRepository.save(userGroup); | ||||
| 		return userGroup; | ||||
| 		return (await this.userGroupsRepository.findOne({ id: id }, { relations: ['permissions', 'groups'] })).toResponse(); | ||||
| 	} | ||||
|  | ||||
| 	@Delete('/:id') | ||||
| @@ -88,9 +89,9 @@ export class UserGroupController { | ||||
| 		if (!group) { return null; } | ||||
| 		const responseGroup = await this.userGroupsRepository.findOne({ id: id }, { relations: ['permissions'] }); | ||||
|  | ||||
| 		const permissionControler = new PermissionController(); | ||||
| 		const permissionController = new PermissionController(); | ||||
| 		for (let permission of responseGroup.permissions) { | ||||
| 			await permissionControler.remove(permission.id, true); | ||||
| 			await permissionController.remove(permission.id, true); | ||||
| 		} | ||||
|  | ||||
| 		await this.userGroupsRepository.delete(group); | ||||
|   | ||||
| @@ -9,11 +9,11 @@ export class AddressWrongTypeError extends NotAcceptableError { | ||||
| 	name = "AddressWrongTypeError" | ||||
|  | ||||
| 	@IsString() | ||||
| 	message = "The address must be an existing adress's id. \n You provided a object of another type." | ||||
| 	message = "The address must be an existing address's id. \n You provided a object of another type." | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Error to throw, when a non-existant address get's loaded. | ||||
|  * Error to throw, when a non-existent address get's loaded. | ||||
|  */ | ||||
| export class AddressNotFoundError extends NotFoundError { | ||||
| 	@IsString() | ||||
|   | ||||
| @@ -118,7 +118,7 @@ export class RefreshTokenCountInvalidError extends NotAcceptableError { | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Error to throw when someone tryes to reset a user's password more than once in 15 minutes. | ||||
|  * Error to throw when someone tries to reset a user's password more than once in 15 minutes. | ||||
|  */ | ||||
| export class ResetAlreadyRequestedError extends NotAcceptableError { | ||||
| 	@IsString() | ||||
|   | ||||
| @@ -13,7 +13,7 @@ export class GroupContactWrongTypeError extends NotAcceptableError { | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Error to throw, when a non-existant groupContact get's loaded. | ||||
|  * Error to throw, when a non-existent groupContact get's loaded. | ||||
|  */ | ||||
| export class GroupContactNotFoundError extends NotFoundError { | ||||
| 	@IsString() | ||||
|   | ||||
| @@ -13,12 +13,12 @@ export class PrincipalNotFoundError extends NotFoundError { | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Error to throw, when a provided runnerOrganisation doesn't belong to the accepted types. | ||||
|  * Error to throw, when a provided runner organization doesn't belong to the accepted types. | ||||
|  */ | ||||
| export class PrincipalWrongTypeError extends NotAcceptableError { | ||||
| 	@IsString() | ||||
| 	name = "PrincipalWrongTypeError" | ||||
|  | ||||
| 	@IsString() | ||||
| 	message = "The princial must have an existing principal's id. \n You provided a object of another type." | ||||
| 	message = "The principal must have an existing principal's id. \n You provided a object of another type." | ||||
| } | ||||
|   | ||||
							
								
								
									
										48
									
								
								src/errors/RunnerCardErrors.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/errors/RunnerCardErrors.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| import { IsString } from 'class-validator'; | ||||
| import { NotAcceptableError, NotFoundError } from 'routing-controllers'; | ||||
|  | ||||
| /** | ||||
|  * Error to throw when a card couldn't be found. | ||||
|  */ | ||||
| export class RunnerCardNotFoundError extends NotFoundError { | ||||
| 	@IsString() | ||||
| 	name = "RunnerCardNotFoundError" | ||||
|  | ||||
| 	@IsString() | ||||
| 	message = "Card not found!" | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Error to throw when two cards' ids don't match. | ||||
|  * Usually occurs when a user tries to change a card's id. | ||||
|  */ | ||||
| export class RunnerCardIdsNotMatchingError extends NotAcceptableError { | ||||
| 	@IsString() | ||||
| 	name = "RunnerCardIdsNotMatchingError" | ||||
|  | ||||
| 	@IsString() | ||||
| 	message = "The ids don't match! \n And if you wanted to change a cards's id: This isn't allowed" | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Error to throw when a card still has scans associated. | ||||
|  */ | ||||
| export class RunnerCardHasScansError extends NotAcceptableError { | ||||
| 	@IsString() | ||||
| 	name = "RunnerCardHasScansError" | ||||
|  | ||||
| 	@IsString() | ||||
| 	message = "This card still has scans associated with it. \n If you want to delete this card with all it's scans add `?force` to your query. \n Otherwise please consider just disabling it." | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Error to throw when a card's id is too big to generate a ean-13 barcode for it. | ||||
|  * This error should never reach a end user. | ||||
|  */ | ||||
| export class RunnerCardIdOutOfRangeError extends Error { | ||||
| 	@IsString() | ||||
| 	name = "RunnerCardIdOutOfRangeError" | ||||
|  | ||||
| 	@IsString() | ||||
| 	message = "The card's id is too big to fit into a ean-13 barcode. \n This has a very low probability of happening but means that you might want to switch your barcode format for something that can accept numbers over 9999999999." | ||||
| } | ||||
| @@ -32,5 +32,5 @@ export class RunnerGroupNeededError extends NotAcceptableError { | ||||
| 	name = "RunnerGroupNeededError" | ||||
|  | ||||
| 	@IsString() | ||||
| 	message = "Runner's need to be part of one group (team or organisiation)! \n You provided neither." | ||||
| 	message = "Runner's need to be part of one group (team or organisation)! \n You provided neither." | ||||
| } | ||||
| @@ -13,7 +13,7 @@ export class RunnerOrganisationNotFoundError extends NotFoundError { | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Error to throw when two runner organisations' ids don't match. | ||||
|  * Error to throw when two runner organisation's ids don't match. | ||||
|  * Usually occurs when a user tries to change a runner organisation's id. | ||||
|  */ | ||||
| export class RunnerOrganisationIdsNotMatchingError extends NotAcceptableError { | ||||
|   | ||||
| @@ -2,7 +2,7 @@ import { IsString } from 'class-validator'; | ||||
| import { NotAcceptableError, NotFoundError } from 'routing-controllers'; | ||||
|  | ||||
| /** | ||||
|  * Error to throw, when a non-existant scan station get's loaded. | ||||
|  * Error to throw, when a non-existent scan station get's loaded. | ||||
|  */ | ||||
| export class ScanStationNotFoundError extends NotFoundError { | ||||
| 	@IsString() | ||||
|   | ||||
| @@ -2,7 +2,7 @@ import { IsString } from 'class-validator'; | ||||
| import { NotAcceptableError, NotFoundError } from 'routing-controllers'; | ||||
|  | ||||
| /** | ||||
|  * Error to throw, when a non-existant stats client get's loaded. | ||||
|  * Error to throw, when a non-existent stats client get's loaded. | ||||
|  */ | ||||
| export class StatsClientNotFoundError extends NotFoundError { | ||||
| 	@IsString() | ||||
|   | ||||
| @@ -2,7 +2,7 @@ import { validationMetadatasToSchemas } from "class-validator-jsonschema"; | ||||
| import express, { Application } from "express"; | ||||
| import path from 'path'; | ||||
| import { getMetadataArgsStorage } from "routing-controllers"; | ||||
| import { routingControllersToSpec } from "routing-controllers-openapi"; | ||||
| import { generateSpec } from '../apispec'; | ||||
|  | ||||
| /** | ||||
|  * Loader for everything openapi related - from creating the schema to serving it via a static route and swaggerUiExpress. | ||||
| @@ -15,46 +15,7 @@ export default async (app: Application) => { | ||||
|   }); | ||||
|  | ||||
|   //Spec creation based on the previously created schemas | ||||
|   const spec = routingControllersToSpec( | ||||
|     storage, | ||||
|     { | ||||
|       routePrefix: "/api" | ||||
|     }, | ||||
|     { | ||||
|       components: { | ||||
|         schemas, | ||||
|         "securitySchemes": { | ||||
|           "AuthToken": { | ||||
|             "type": "http", | ||||
|             "scheme": "bearer", | ||||
|             "bearerFormat": "JWT", | ||||
|             description: "A JWT based access token. Use /api/auth/login or /api/auth/refresh to get one." | ||||
|           }, | ||||
|           "RefreshTokenCookie": { | ||||
|             "type": "apiKey", | ||||
|             "in": "cookie", | ||||
|             "name": "lfk_backend__refresh_token", | ||||
|             description: "A cookie containing a JWT based refreh token. Attention: Doesn't work in swagger-ui. Use /api/auth/login or /api/auth/refresh to get one." | ||||
|           }, | ||||
|           "StatsApiToken": { | ||||
|             "type": "http", | ||||
|             "scheme": "bearer", | ||||
|             description: "Api token that can be obtained by creating a new stats client (post to /api/statsclients). Only valid for obtaining stats." | ||||
|           }, | ||||
|           "StationApiToken": { | ||||
|             "type": "http", | ||||
|             "scheme": "bearer", | ||||
|             description: "Api token that can be obtained by creating a new scan station (post to /api/stations). Only valid for creating scans." | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       info: { | ||||
|         description: "The the backend API for the LfK! runner system.", | ||||
|         title: "LfK! Backend API", | ||||
|         version: "0.0.8", | ||||
|       }, | ||||
|     } | ||||
|   ); | ||||
|   const spec = generateSpec(storage, schemas); | ||||
|   app.get(["/api/docs/openapi.json", "/api/docs/swagger.json"], (req, res) => { | ||||
|     res.json(spec); | ||||
|   }); | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { Request, Response } from 'express'; | ||||
|  | ||||
| /** | ||||
|  * Custom express middleware that appends the raw body to the request obeject. | ||||
|  * Mainly used for parsing csvs from boddies. | ||||
|  * Custom express middleware that appends the raw body to the request object. | ||||
|  * Mainly used for parsing csvs from bodies. | ||||
|  */ | ||||
|  | ||||
| const RawBodyMiddleware = (req: Request, res: Response, next: () => void) => { | ||||
|   | ||||
| @@ -5,8 +5,9 @@ import { ScanStation } from '../models/entities/ScanStation'; | ||||
| import authchecker from './authchecker'; | ||||
|  | ||||
| /** | ||||
|  * This middleware handels the authentification of scan station api tokens. | ||||
|  * The tokens have to be provided via Bearer auth header. | ||||
|  * This middleware handles the authentication of scan station api tokens. | ||||
|  * The tokens have to be provided via Bearer authorization header. | ||||
|  * You have to manually use this middleware via @UseBefore(ScanAuth) instead of using @Authorized(). | ||||
|  * @param req Express request object. | ||||
|  * @param res Express response object. | ||||
|  * @param next Next function to call on success. | ||||
| @@ -31,7 +32,7 @@ const ScanAuth = async (req: Request, res: Response, next: () => void) => { | ||||
|     } | ||||
|     finally { | ||||
|         if (prefix == "" || prefix == undefined || prefix == null) { | ||||
|             res.status(401).send("Api token non-existant or invalid syntax."); | ||||
|             res.status(401).send("Api token non-existent or invalid syntax."); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| @@ -45,7 +46,7 @@ const ScanAuth = async (req: Request, res: Response, next: () => void) => { | ||||
|         } | ||||
|         finally { | ||||
|             if (user_authorized == false) { | ||||
|                 res.status(401).send("Api token non-existant or invalid syntax."); | ||||
|                 res.status(401).send("Api token non-existent or invalid syntax."); | ||||
|                 return; | ||||
|             } | ||||
|             else { | ||||
|   | ||||
| @@ -5,8 +5,9 @@ import { StatsClient } from '../models/entities/StatsClient'; | ||||
| import authchecker from './authchecker'; | ||||
|  | ||||
| /** | ||||
|  * This middleware handels the authentification of stats client api tokens. | ||||
|  * The tokens have to be provided via Bearer auth header. | ||||
|  * This middleware handles the authentication of stats client api tokens. | ||||
|  * The tokens have to be provided via Bearer authorization header. | ||||
|  * You have to manually use this middleware via @UseBefore(StatsAuth) instead of using @Authorized(). | ||||
|  * @param req Express request object. | ||||
|  * @param res Express response object. | ||||
|  * @param next Next function to call on success. | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import { JwtCreator, JwtUser } from '../jwtcreator'; | ||||
| import { User } from '../models/entities/User'; | ||||
|  | ||||
| /** | ||||
|  * Handels authorisation verification via jwt's for all api endpoints using the @Authorized decorator. | ||||
|  * Handles authentication via jwt's (Bearer authorization header) for all api endpoints using the @Authorized decorator. | ||||
|  * @param action Routing-Controllers action object that provides request and response objects among other stuff. | ||||
|  * @param permissions The permissions that the endpoint using @Authorized requires. | ||||
|  */ | ||||
| @@ -43,7 +43,7 @@ const authchecker = async (action: Action, permissions: string[] | string) => { | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Handels soft-refreshing of access-tokens. | ||||
|  * Handles soft-refreshing of access-tokens. | ||||
|  * @param action Routing-Controllers action object that provides request and response objects among other stuff. | ||||
|  */ | ||||
| const refresh = async (action: Action) => { | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import { RunnerOrganisationNotFoundError } from '../../errors/RunnerOrganisation | ||||
| import { RunnerGroup } from '../entities/RunnerGroup'; | ||||
| import { RunnerOrganisation } from '../entities/RunnerOrganisation'; | ||||
| import { RunnerTeam } from '../entities/RunnerTeam'; | ||||
| import { CreateRunner } from './CreateRunner'; | ||||
| import { CreateRunner } from './create/CreateRunner'; | ||||
|  | ||||
| /** | ||||
|  * Special class used to import runners from csv files - or json arrays created from csv to be exact. | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import { config } from '../../config'; | ||||
| import { IllegalJWTError, JwtNotProvidedError, RefreshTokenCountInvalidError, UserDisabledError, UserNotFoundError } from '../../errors/AuthError'; | ||||
| import { JwtCreator } from "../../jwtcreator"; | ||||
| import { User } from '../entities/User'; | ||||
| import { Auth } from '../responses/ResponseAuth'; | ||||
| import { ResponseAuth } from '../responses/ResponseAuth'; | ||||
|  | ||||
| /** | ||||
|  * This class is used to create refreshed auth credentials. | ||||
| @@ -24,8 +24,8 @@ export class RefreshAuth { | ||||
|     /** | ||||
|      * Creates a new auth object based on this. | ||||
|      */ | ||||
|     public async toAuth(): Promise<Auth> { | ||||
|         let newAuth: Auth = new Auth(); | ||||
|     public async toAuth(): Promise<ResponseAuth> { | ||||
|         let newAuth: ResponseAuth = new ResponseAuth(); | ||||
|         if (!this.token || this.token === undefined) { | ||||
|             throw new JwtNotProvidedError() | ||||
|         } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { IsNotEmpty, IsOptional, IsPostalCode, IsString } from 'class-validator'; | ||||
| import { config } from '../../config'; | ||||
| import { Address } from '../entities/Address'; | ||||
| import { config } from '../../../config'; | ||||
| import { Address } from '../../entities/Address'; | ||||
| 
 | ||||
| /** | ||||
|  * This classed is used to create a new Address entity from a json body (post request). | ||||
| @@ -56,7 +56,7 @@ export class CreateAddress { | ||||
|     /** | ||||
|      * Creates a new Address entity from this. | ||||
|      */ | ||||
|     public toAddress(): Address { | ||||
|     public async toEntity(): Promise<Address> { | ||||
|         let newAddress: Address = new Address(); | ||||
| 
 | ||||
|         newAddress.address1 = this.address1; | ||||
| @@ -1,11 +1,11 @@ | ||||
| import * as argon2 from "argon2"; | ||||
| import { IsEmail, IsNotEmpty, IsOptional, IsString } from 'class-validator'; | ||||
| import { getConnectionManager } from 'typeorm'; | ||||
| import { InvalidCredentialsError, PasswordNeededError, UserDisabledError, UserNotFoundError } from '../../errors/AuthError'; | ||||
| import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; | ||||
| import { JwtCreator } from '../../jwtcreator'; | ||||
| import { User } from '../entities/User'; | ||||
| import { Auth } from '../responses/ResponseAuth'; | ||||
| import { InvalidCredentialsError, PasswordNeededError, UserDisabledError, UserNotFoundError } from '../../../errors/AuthError'; | ||||
| import { UsernameOrEmailNeededError } from '../../../errors/UserErrors'; | ||||
| import { JwtCreator } from '../../../jwtcreator'; | ||||
| import { User } from '../../entities/User'; | ||||
| import { ResponseAuth } from '../../responses/ResponseAuth'; | ||||
| 
 | ||||
| /** | ||||
|  * This class is used to create auth credentials based on user credentials provided in a json body (post request). | ||||
| @@ -42,8 +42,8 @@ export class CreateAuth { | ||||
|     /** | ||||
|      * Creates a new auth object based on this. | ||||
|      */ | ||||
|     public async toAuth(): Promise<Auth> { | ||||
|         let newAuth: Auth = new Auth(); | ||||
|     public async toAuth(): Promise<ResponseAuth> { | ||||
|         let newAuth: ResponseAuth = new ResponseAuth(); | ||||
| 
 | ||||
|         if (this.email === undefined && this.username === undefined) { | ||||
|             throw new UsernameOrEmailNeededError(); | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { IsBoolean, IsOptional } from 'class-validator'; | ||||
| import { DonorReceiptAddressNeededError } from '../../errors/DonorErrors'; | ||||
| import { Donor } from '../entities/Donor'; | ||||
| import { DonorReceiptAddressNeededError } from '../../../errors/DonorErrors'; | ||||
| import { Donor } from '../../entities/Donor'; | ||||
| import { CreateParticipant } from './CreateParticipant'; | ||||
| 
 | ||||
| /** | ||||
| @@ -18,7 +18,7 @@ export class CreateDonor extends CreateParticipant { | ||||
|     /** | ||||
|      * Creates a new Donor entity from this. | ||||
|      */ | ||||
|     public async toDonor(): Promise<Donor> { | ||||
|     public async toEntity(): Promise<Donor> { | ||||
|         let newDonor: Donor = new Donor(); | ||||
| 
 | ||||
|         newDonor.firstname = this.firstname; | ||||
| @@ -1,9 +1,9 @@ | ||||
| import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; | ||||
| import { getConnectionManager } from 'typeorm'; | ||||
| import { config } from '../../config'; | ||||
| import { AddressNotFoundError, AddressWrongTypeError } from '../../errors/AddressErrors'; | ||||
| import { Address } from '../entities/Address'; | ||||
| import { GroupContact } from '../entities/GroupContact'; | ||||
| import { config } from '../../../config'; | ||||
| import { AddressNotFoundError, AddressWrongTypeError } from '../../../errors/AddressErrors'; | ||||
| import { Address } from '../../entities/Address'; | ||||
| import { GroupContact } from '../../entities/GroupContact'; | ||||
| 
 | ||||
| /** | ||||
|  * This classed is used to create a new Group entity from a json body (post request). | ||||
| @@ -72,7 +72,7 @@ export class CreateGroupContact { | ||||
|     /** | ||||
|      * Creates a new Address entity from this. | ||||
|      */ | ||||
|     public async toGroupContact(): Promise<GroupContact> { | ||||
|     public async toEntity(): Promise<GroupContact> { | ||||
|         let contact: GroupContact = new GroupContact(); | ||||
|         contact.firstname = this.firstname; | ||||
|         contact.middlename = this.middlename; | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; | ||||
| import { getConnectionManager } from 'typeorm'; | ||||
| import { config } from '../../config'; | ||||
| import { AddressNotFoundError, AddressWrongTypeError } from '../../errors/AddressErrors'; | ||||
| import { Address } from '../entities/Address'; | ||||
| import { config } from '../../../config'; | ||||
| import { AddressNotFoundError, AddressWrongTypeError } from '../../../errors/AddressErrors'; | ||||
| import { Address } from '../../entities/Address'; | ||||
| 
 | ||||
| /** | ||||
|  * This classed is used to create a new Participant entity from a json body (post request). | ||||
| @@ -4,11 +4,11 @@ import { | ||||
|     IsNotEmpty | ||||
| } from "class-validator"; | ||||
| import { getConnectionManager } from 'typeorm'; | ||||
| import { PrincipalNotFoundError } from '../../errors/PrincipalErrors'; | ||||
| import { Permission } from '../entities/Permission'; | ||||
| import { Principal } from '../entities/Principal'; | ||||
| import { PermissionAction } from '../enums/PermissionAction'; | ||||
| import { PermissionTarget } from '../enums/PermissionTargets'; | ||||
| import { PrincipalNotFoundError } from '../../../errors/PrincipalErrors'; | ||||
| import { Permission } from '../../entities/Permission'; | ||||
| import { Principal } from '../../entities/Principal'; | ||||
| import { PermissionAction } from '../../enums/PermissionAction'; | ||||
| import { PermissionTarget } from '../../enums/PermissionTargets'; | ||||
| 
 | ||||
| /** | ||||
|  * This classed is used to create a new Permission entity from a json body (post request). | ||||
| @@ -39,7 +39,7 @@ export class CreatePermission { | ||||
|     /** | ||||
|      * Creates a new Permission entity from this. | ||||
|      */ | ||||
|     public async toPermission(): Promise<Permission> { | ||||
|     public async toEntity(): Promise<Permission> { | ||||
|         let newPermission: Permission = new Permission(); | ||||
| 
 | ||||
|         newPermission.principal = await this.getPrincipal(); | ||||
| @@ -1,9 +1,9 @@ | ||||
| import { IsEmail, IsOptional, IsString } from 'class-validator'; | ||||
| import { getConnectionManager } from 'typeorm'; | ||||
| import { ResetAlreadyRequestedError, UserDisabledError, UserNotFoundError } from '../../errors/AuthError'; | ||||
| import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; | ||||
| import { JwtCreator } from '../../jwtcreator'; | ||||
| import { User } from '../entities/User'; | ||||
| import { ResetAlreadyRequestedError, UserDisabledError, UserNotFoundError } from '../../../errors/AuthError'; | ||||
| import { UsernameOrEmailNeededError } from '../../../errors/UserErrors'; | ||||
| import { JwtCreator } from '../../../jwtcreator'; | ||||
| import { User } from '../../entities/User'; | ||||
| 
 | ||||
| /** | ||||
|  * This calss is used to create password reset tokens for users. | ||||
| @@ -1,10 +1,10 @@ | ||||
| import { IsInt } from 'class-validator'; | ||||
| import { getConnectionManager } from 'typeorm'; | ||||
| import { RunnerGroupNotFoundError } from '../../errors/RunnerGroupErrors'; | ||||
| import { RunnerOrganisationWrongTypeError } from '../../errors/RunnerOrganisationErrors'; | ||||
| import { RunnerTeamNeedsParentError } from '../../errors/RunnerTeamErrors'; | ||||
| import { Runner } from '../entities/Runner'; | ||||
| import { RunnerGroup } from '../entities/RunnerGroup'; | ||||
| import { RunnerGroupNotFoundError } from '../../../errors/RunnerGroupErrors'; | ||||
| import { RunnerOrganisationWrongTypeError } from '../../../errors/RunnerOrganisationErrors'; | ||||
| import { RunnerTeamNeedsParentError } from '../../../errors/RunnerTeamErrors'; | ||||
| import { Runner } from '../../entities/Runner'; | ||||
| import { RunnerGroup } from '../../entities/RunnerGroup'; | ||||
| import { CreateParticipant } from './CreateParticipant'; | ||||
| 
 | ||||
| /** | ||||
| @@ -21,7 +21,7 @@ export class CreateRunner extends CreateParticipant { | ||||
|     /** | ||||
|      * Creates a new Runner entity from this. | ||||
|      */ | ||||
|     public async toRunner(): Promise<Runner> { | ||||
|     public async toEntity(): Promise<Runner> { | ||||
|         let newRunner: Runner = new Runner(); | ||||
| 
 | ||||
|         newRunner.firstname = this.firstname; | ||||
							
								
								
									
										45
									
								
								src/models/actions/create/CreateRunnerCard.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/models/actions/create/CreateRunnerCard.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| import { IsBoolean, IsInt, IsOptional } from 'class-validator'; | ||||
| import { getConnection } from 'typeorm'; | ||||
| import { RunnerNotFoundError } from '../../../errors/RunnerErrors'; | ||||
| import { Runner } from '../../entities/Runner'; | ||||
| import { RunnerCard } from '../../entities/RunnerCard'; | ||||
|  | ||||
| /** | ||||
|  * This classed is used to create a new RunnerCard entity from a json body (post request). | ||||
|  */ | ||||
| export class CreateRunnerCard { | ||||
|     /** | ||||
|      * The card's associated runner. | ||||
|      */ | ||||
|     @IsInt() | ||||
|     @IsOptional() | ||||
|     runner?: number; | ||||
|  | ||||
|     /** | ||||
|      * Is the new card enabled (for fraud reasons)? | ||||
|      * Default: true | ||||
|      */ | ||||
|     @IsBoolean() | ||||
|     enabled: boolean = true; | ||||
|  | ||||
|     /** | ||||
|      * Creates a new RunnerCard entity from this. | ||||
|      */ | ||||
|     public async toEntity(): Promise<RunnerCard> { | ||||
|         let newCard: RunnerCard = new RunnerCard(); | ||||
|  | ||||
|         newCard.enabled = this.enabled; | ||||
|         newCard.runner = await this.getRunner(); | ||||
|  | ||||
|         return newCard; | ||||
|     } | ||||
|  | ||||
|     public async getRunner(): Promise<Runner> { | ||||
|         if (!this.runner) { return null; } | ||||
|         const runner = await getConnection().getRepository(Runner).findOne({ id: this.runner }); | ||||
|         if (!runner) { | ||||
|             throw new RunnerNotFoundError(); | ||||
|         } | ||||
|         return runner; | ||||
|     } | ||||
| } | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { IsInt, IsNotEmpty, IsOptional, IsString } from 'class-validator'; | ||||
| import { getConnectionManager } from 'typeorm'; | ||||
| import { GroupContactNotFoundError, GroupContactWrongTypeError } from '../../errors/GroupContactErrors'; | ||||
| import { GroupContact } from '../entities/GroupContact'; | ||||
| import { GroupContactNotFoundError, GroupContactWrongTypeError } from '../../../errors/GroupContactErrors'; | ||||
| import { GroupContact } from '../../entities/GroupContact'; | ||||
| 
 | ||||
| /** | ||||
|  * This classed is used to create a new RunnerGroup entity from a json body (post request). | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { IsInt, IsOptional } from 'class-validator'; | ||||
| import { getConnectionManager } from 'typeorm'; | ||||
| import { AddressNotFoundError, AddressWrongTypeError } from '../../errors/AddressErrors'; | ||||
| import { Address } from '../entities/Address'; | ||||
| import { RunnerOrganisation } from '../entities/RunnerOrganisation'; | ||||
| import { AddressNotFoundError, AddressWrongTypeError } from '../../../errors/AddressErrors'; | ||||
| import { Address } from '../../entities/Address'; | ||||
| import { RunnerOrganisation } from '../../entities/RunnerOrganisation'; | ||||
| import { CreateRunnerGroup } from './CreateRunnerGroup'; | ||||
| 
 | ||||
| /** | ||||
| @@ -36,7 +36,7 @@ export class CreateRunnerOrganisation extends CreateRunnerGroup { | ||||
|     /** | ||||
|      * Creates a new RunnerOrganisation entity from this. | ||||
|      */ | ||||
|     public async toRunnerOrganisation(): Promise<RunnerOrganisation> { | ||||
|     public async toEntity(): Promise<RunnerOrganisation> { | ||||
|         let newRunnerOrganisation: RunnerOrganisation = new RunnerOrganisation(); | ||||
| 
 | ||||
|         newRunnerOrganisation.name = this.name; | ||||
| @@ -1,9 +1,9 @@ | ||||
| import { IsInt, IsNotEmpty } from 'class-validator'; | ||||
| import { getConnectionManager } from 'typeorm'; | ||||
| import { RunnerOrganisationNotFoundError, RunnerOrganisationWrongTypeError } from '../../errors/RunnerOrganisationErrors'; | ||||
| import { RunnerTeamNeedsParentError } from '../../errors/RunnerTeamErrors'; | ||||
| import { RunnerOrganisation } from '../entities/RunnerOrganisation'; | ||||
| import { RunnerTeam } from '../entities/RunnerTeam'; | ||||
| import { RunnerOrganisationNotFoundError, RunnerOrganisationWrongTypeError } from '../../../errors/RunnerOrganisationErrors'; | ||||
| import { RunnerTeamNeedsParentError } from '../../../errors/RunnerTeamErrors'; | ||||
| import { RunnerOrganisation } from '../../entities/RunnerOrganisation'; | ||||
| import { RunnerTeam } from '../../entities/RunnerTeam'; | ||||
| import { CreateRunnerGroup } from './CreateRunnerGroup'; | ||||
| 
 | ||||
| /** | ||||
| @@ -37,7 +37,7 @@ export class CreateRunnerTeam extends CreateRunnerGroup { | ||||
|     /** | ||||
|      * Creates a new RunnerTeam entity from this. | ||||
|      */ | ||||
|     public async toRunnerTeam(): Promise<RunnerTeam> { | ||||
|     public async toEntity(): Promise<RunnerTeam> { | ||||
|         let newRunnerTeam: RunnerTeam = new RunnerTeam(); | ||||
| 
 | ||||
|         newRunnerTeam.name = this.name; | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { IsBoolean, IsInt, IsOptional, IsPositive } from 'class-validator'; | ||||
| import { getConnection } from 'typeorm'; | ||||
| import { RunnerNotFoundError } from '../../errors/RunnerErrors'; | ||||
| import { Runner } from '../entities/Runner'; | ||||
| import { Scan } from '../entities/Scan'; | ||||
| import { RunnerNotFoundError } from '../../../errors/RunnerErrors'; | ||||
| import { Runner } from '../../entities/Runner'; | ||||
| import { Scan } from '../../entities/Scan'; | ||||
| 
 | ||||
| /** | ||||
|  * This class is used to create a new Scan entity from a json body (post request). | ||||
| @@ -36,7 +36,7 @@ export abstract class CreateScan { | ||||
|     /** | ||||
|      * Creates a new Scan entity from this. | ||||
|      */ | ||||
|     public async toScan(): Promise<Scan> { | ||||
|     public async toEntity(): Promise<Scan> { | ||||
|         let newScan = new Scan(); | ||||
| 
 | ||||
|         newScan.distance = this.distance; | ||||
| @@ -3,9 +3,9 @@ import { IsBoolean, IsInt, IsOptional, IsPositive, IsString } from 'class-valida | ||||
| import crypto from 'crypto'; | ||||
| import { getConnection } from 'typeorm'; | ||||
| import * as uuid from 'uuid'; | ||||
| import { TrackNotFoundError } from '../../errors/TrackErrors'; | ||||
| import { ScanStation } from '../entities/ScanStation'; | ||||
| import { Track } from '../entities/Track'; | ||||
| import { TrackNotFoundError } from '../../../errors/TrackErrors'; | ||||
| import { ScanStation } from '../../entities/ScanStation'; | ||||
| import { Track } from '../../entities/Track'; | ||||
| 
 | ||||
| /** | ||||
|  * This class is used to create a new StatsClient entity from a json body (post request). | ||||
| @@ -2,7 +2,7 @@ import * as argon2 from "argon2"; | ||||
| import { IsOptional, IsString } from 'class-validator'; | ||||
| import crypto from 'crypto'; | ||||
| import * as uuid from 'uuid'; | ||||
| import { StatsClient } from '../entities/StatsClient'; | ||||
| import { StatsClient } from '../../entities/StatsClient'; | ||||
| 
 | ||||
| /** | ||||
|  * This classed is used to create a new StatsClient entity from a json body (post request). | ||||
| @@ -18,7 +18,7 @@ export class CreateStatsClient { | ||||
|     /** | ||||
|      * Converts this to a StatsClient entity. | ||||
|      */ | ||||
|     public async toStatsClient(): Promise<StatsClient> { | ||||
|     public async toEntity(): Promise<StatsClient> { | ||||
|         let newClient: StatsClient = new StatsClient(); | ||||
| 
 | ||||
|         newClient.description = this.description; | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { IsInt, IsNotEmpty, IsOptional, IsPositive, IsString } from 'class-validator'; | ||||
| import { TrackLapTimeCantBeNegativeError } from '../../errors/TrackErrors'; | ||||
| import { Track } from '../entities/Track'; | ||||
| import { TrackLapTimeCantBeNegativeError } from '../../../errors/TrackErrors'; | ||||
| import { Track } from '../../entities/Track'; | ||||
| 
 | ||||
| /** | ||||
|  * This classed is used to create a new Track entity from a json body (post request). | ||||
| @@ -31,7 +31,7 @@ export class CreateTrack { | ||||
|     /** | ||||
|      * Creates a new Track entity from this. | ||||
|      */ | ||||
|     public toTrack(): Track { | ||||
|     public toEntity(): Track { | ||||
|         let newTrack: Track = new Track(); | ||||
| 
 | ||||
|         newTrack.name = this.name; | ||||
| @@ -1,41 +1,36 @@ | ||||
| import { IsNotEmpty } from 'class-validator'; | ||||
| import { IsInt, IsPositive } from 'class-validator'; | ||||
| import { getConnection } from 'typeorm'; | ||||
| import { RunnerNotFoundError } from '../../errors/RunnerErrors'; | ||||
| import { RunnerCard } from '../entities/RunnerCard'; | ||||
| import { ScanStation } from '../entities/ScanStation'; | ||||
| import { TrackScan } from '../entities/TrackScan'; | ||||
| import { CreateScan } from './CreateScan'; | ||||
| import { RunnerCardNotFoundError } from '../../../errors/RunnerCardErrors'; | ||||
| import { RunnerNotFoundError } from '../../../errors/RunnerErrors'; | ||||
| import { ScanStationNotFoundError } from '../../../errors/ScanStationErrors'; | ||||
| import { RunnerCard } from '../../entities/RunnerCard'; | ||||
| import { ScanStation } from '../../entities/ScanStation'; | ||||
| import { TrackScan } from '../../entities/TrackScan'; | ||||
| 
 | ||||
| /** | ||||
|  * This classed is used to create a new Scan entity from a json body (post request). | ||||
|  */ | ||||
| export class CreateTrackScan extends CreateScan { | ||||
| 
 | ||||
|     /** | ||||
|      * The scan's associated track. | ||||
|      * This is used to determine the scan's distance. | ||||
|      */ | ||||
|     @IsNotEmpty() | ||||
|     track: number; | ||||
| 
 | ||||
| export class CreateTrackScan { | ||||
|     /** | ||||
|      * The runnerCard associated with the scan. | ||||
|      * This get's saved for documentation and management purposes. | ||||
|      */ | ||||
|     @IsNotEmpty() | ||||
|     @IsInt() | ||||
|     @IsPositive() | ||||
|     card: number; | ||||
| 
 | ||||
|     /** | ||||
|      * The scanning station that created the scan. | ||||
|      * Mainly used for logging and traceing back scans (or errors) | ||||
|      */ | ||||
|     @IsNotEmpty() | ||||
|     @IsInt() | ||||
|     @IsPositive() | ||||
|     station: number; | ||||
| 
 | ||||
|     /** | ||||
|      * Creates a new Track entity from this. | ||||
|      */ | ||||
|     public async toScan(): Promise<TrackScan> { | ||||
|     public async toEntity(): Promise<TrackScan> { | ||||
|         let newScan: TrackScan = new TrackScan(); | ||||
| 
 | ||||
|         newScan.station = await this.getStation(); | ||||
| @@ -48,7 +43,7 @@ export class CreateTrackScan extends CreateScan { | ||||
|             throw new RunnerNotFoundError(); | ||||
|         } | ||||
| 
 | ||||
|         newScan.timestamp = new Date(Date.now()).toString(); | ||||
|         newScan.timestamp = Math.round(new Date().getTime() / 1000); | ||||
|         newScan.valid = await this.validateScan(newScan); | ||||
| 
 | ||||
|         return newScan; | ||||
| @@ -57,25 +52,25 @@ export class CreateTrackScan extends CreateScan { | ||||
|     public async getCard(): Promise<RunnerCard> { | ||||
|         const track = await getConnection().getRepository(RunnerCard).findOne({ id: this.card }, { relations: ["runner"] }); | ||||
|         if (!track) { | ||||
|             throw new Error(); | ||||
|             throw new RunnerCardNotFoundError(); | ||||
|         } | ||||
|         return track; | ||||
|     } | ||||
| 
 | ||||
|     public async getStation(): Promise<ScanStation> { | ||||
|         const track = await getConnection().getRepository(ScanStation).findOne({ id: this.card }, { relations: ["track"] }); | ||||
|         if (!track) { | ||||
|             throw new Error(); | ||||
|         const station = await getConnection().getRepository(ScanStation).findOne({ id: this.station }, { relations: ["track"] }); | ||||
|         if (!station) { | ||||
|             throw new ScanStationNotFoundError(); | ||||
|         } | ||||
|         return track; | ||||
|         return station; | ||||
|     } | ||||
| 
 | ||||
|     public async validateScan(scan: TrackScan): Promise<boolean> { | ||||
|         const scans = await getConnection().getRepository(TrackScan).find({ where: { runner: scan.runner }, relations: ["track"] }); | ||||
|         const scans = await getConnection().getRepository(TrackScan).find({ where: { runner: scan.runner, valid: true }, relations: ["track"] }); | ||||
|         if (scans.length == 0) { return true; } | ||||
| 
 | ||||
|         const newestScan = scans[0]; | ||||
|         if ((new Date(scan.timestamp).getTime() - new Date(newestScan.timestamp).getTime()) > scan.track.minimumLapTime) { | ||||
|         const newestScan = scans[scans.length - 1]; | ||||
|         if ((scan.timestamp - newestScan.timestamp) > scan.track.minimumLapTime) { | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
| @@ -2,11 +2,11 @@ import * as argon2 from "argon2"; | ||||
| import { IsBoolean, IsEmail, IsOptional, IsPhoneNumber, IsString, IsUrl } from 'class-validator'; | ||||
| import { getConnectionManager } from 'typeorm'; | ||||
| import * as uuid from 'uuid'; | ||||
| import { config } from '../../config'; | ||||
| import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; | ||||
| import { UserGroupNotFoundError } from '../../errors/UserGroupErrors'; | ||||
| import { User } from '../entities/User'; | ||||
| import { UserGroup } from '../entities/UserGroup'; | ||||
| import { config } from '../../../config'; | ||||
| import { UsernameOrEmailNeededError } from '../../../errors/UserErrors'; | ||||
| import { UserGroupNotFoundError } from '../../../errors/UserGroupErrors'; | ||||
| import { User } from '../../entities/User'; | ||||
| import { UserGroup } from '../../entities/UserGroup'; | ||||
| 
 | ||||
| /** | ||||
|  * This classed is used to create a new User entity from a json body (post request). | ||||
| @@ -89,7 +89,7 @@ export class CreateUser { | ||||
|     /** | ||||
|      * Converts this to a User entity. | ||||
|      */ | ||||
|     public async toUser(): Promise<User> { | ||||
|     public async toEntity(): Promise<User> { | ||||
|         let newUser: User = new User(); | ||||
| 
 | ||||
|         if (this.email === undefined && this.username === undefined) { | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { IsOptional, IsString } from 'class-validator'; | ||||
| import { UserGroup } from '../entities/UserGroup'; | ||||
| import { UserGroup } from '../../entities/UserGroup'; | ||||
| 
 | ||||
| /** | ||||
|  * This classed is used to create a new UserGroup entity from a json body (post request). | ||||
| @@ -22,7 +22,7 @@ export class CreateUserGroup { | ||||
|     /** | ||||
|      * Creates a new UserGroup entity from this. | ||||
|      */ | ||||
|     public async toUserGroup(): Promise<UserGroup> { | ||||
|     public async toEntity(): Promise<UserGroup> { | ||||
|         let newUserGroup: UserGroup = new UserGroup(); | ||||
| 
 | ||||
|         newUserGroup.name = this.name; | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { IsBoolean, IsInt, IsOptional } from 'class-validator'; | ||||
| import { DonorReceiptAddressNeededError } from '../../errors/DonorErrors'; | ||||
| import { Donor } from '../entities/Donor'; | ||||
| import { CreateParticipant } from './CreateParticipant'; | ||||
| import { DonorReceiptAddressNeededError } from '../../../errors/DonorErrors'; | ||||
| import { Donor } from '../../entities/Donor'; | ||||
| import { CreateParticipant } from '../create/CreateParticipant'; | ||||
| 
 | ||||
| /** | ||||
|  * This class is used to update a Donor entity (via put request). | ||||
| @@ -26,7 +26,7 @@ export class UpdateDonor extends CreateParticipant { | ||||
|     /** | ||||
|      * Updates a provided Donor entity based on this. | ||||
|      */ | ||||
|     public async updateDonor(donor: Donor): Promise<Donor> { | ||||
|     public async update(donor: Donor): Promise<Donor> { | ||||
|         donor.firstname = this.firstname; | ||||
|         donor.middlename = this.middlename; | ||||
|         donor.lastname = this.lastname; | ||||
| @@ -1,11 +1,11 @@ | ||||
| import { IsInt, IsNotEmpty, IsObject } from 'class-validator'; | ||||
| import { getConnectionManager } from 'typeorm'; | ||||
| import { PermissionNeedsPrincipalError } from '../../errors/PermissionErrors'; | ||||
| import { PrincipalNotFoundError, PrincipalWrongTypeError } from '../../errors/PrincipalErrors'; | ||||
| import { Permission } from '../entities/Permission'; | ||||
| import { Principal } from '../entities/Principal'; | ||||
| import { PermissionAction } from '../enums/PermissionAction'; | ||||
| import { PermissionTarget } from '../enums/PermissionTargets'; | ||||
| import { PermissionNeedsPrincipalError } from '../../../errors/PermissionErrors'; | ||||
| import { PrincipalNotFoundError, PrincipalWrongTypeError } from '../../../errors/PrincipalErrors'; | ||||
| import { Permission } from '../../entities/Permission'; | ||||
| import { Principal } from '../../entities/Principal'; | ||||
| import { PermissionAction } from '../../enums/PermissionAction'; | ||||
| import { PermissionTarget } from '../../enums/PermissionTargets'; | ||||
| 
 | ||||
| /** | ||||
|  * This class is used to update a Permission entity (via put request). | ||||
| @@ -42,7 +42,7 @@ export class UpdatePermission { | ||||
|     /** | ||||
|      * Updates a provided Permission entity based on this. | ||||
|      */ | ||||
|     public async updatePermission(permission: Permission): Promise<Permission> { | ||||
|     public async update(permission: Permission): Promise<Permission> { | ||||
|         permission.principal = await this.getPrincipal(); | ||||
|         permission.target = this.target; | ||||
|         permission.action = this.action; | ||||
| @@ -1,11 +1,11 @@ | ||||
| import { IsInt, IsObject } from 'class-validator'; | ||||
| import { getConnectionManager } from 'typeorm'; | ||||
| import { RunnerGroupNotFoundError } from '../../errors/RunnerGroupErrors'; | ||||
| import { RunnerOrganisationWrongTypeError } from '../../errors/RunnerOrganisationErrors'; | ||||
| import { RunnerTeamNeedsParentError } from '../../errors/RunnerTeamErrors'; | ||||
| import { Runner } from '../entities/Runner'; | ||||
| import { RunnerGroup } from '../entities/RunnerGroup'; | ||||
| import { CreateParticipant } from './CreateParticipant'; | ||||
| import { RunnerGroupNotFoundError } from '../../../errors/RunnerGroupErrors'; | ||||
| import { RunnerOrganisationWrongTypeError } from '../../../errors/RunnerOrganisationErrors'; | ||||
| import { RunnerTeamNeedsParentError } from '../../../errors/RunnerTeamErrors'; | ||||
| import { Runner } from '../../entities/Runner'; | ||||
| import { RunnerGroup } from '../../entities/RunnerGroup'; | ||||
| import { CreateParticipant } from '../create/CreateParticipant'; | ||||
| 
 | ||||
| /** | ||||
|  * This class is used to update a Runner entity (via put request). | ||||
| @@ -29,7 +29,7 @@ export class UpdateRunner extends CreateParticipant { | ||||
|     /** | ||||
|      * Updates a provided Runner entity based on this. | ||||
|      */ | ||||
|     public async updateRunner(runner: Runner): Promise<Runner> { | ||||
|     public async update(runner: Runner): Promise<Runner> { | ||||
|         runner.firstname = this.firstname; | ||||
|         runner.middlename = this.middlename; | ||||
|         runner.lastname = this.lastname; | ||||
							
								
								
									
										51
									
								
								src/models/actions/update/UpdateRunnerCard.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/models/actions/update/UpdateRunnerCard.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| import { IsBoolean, IsInt, IsOptional, IsPositive } from 'class-validator'; | ||||
| import { getConnection } from 'typeorm'; | ||||
| import { RunnerNotFoundError } from '../../../errors/RunnerErrors'; | ||||
| import { Runner } from '../../entities/Runner'; | ||||
| import { RunnerCard } from '../../entities/RunnerCard'; | ||||
|  | ||||
| /** | ||||
|  * This class is used to update a RunnerCard entity (via put request). | ||||
|  */ | ||||
| export class UpdateRunnerCard { | ||||
|     /** | ||||
|      * The updated card's id. | ||||
|      * This shouldn't have changed but it is here in case anyone ever wants to enable id changes (whyever they would want to). | ||||
|      */ | ||||
|     @IsInt() | ||||
|     @IsPositive() | ||||
|     id?: number; | ||||
|  | ||||
|     /** | ||||
|      * The updated card's associated runner. | ||||
|      */ | ||||
|     @IsInt() | ||||
|     @IsOptional() | ||||
|     runner?: number; | ||||
|  | ||||
|     /** | ||||
|      * Is the updated card enabled (for fraud reasons)? | ||||
|      * Default: true | ||||
|      */ | ||||
|     @IsBoolean() | ||||
|     enabled: boolean = true; | ||||
|  | ||||
|     /** | ||||
|      * Creates a new RunnerCard entity from this. | ||||
|      */ | ||||
|     public async update(card: RunnerCard): Promise<RunnerCard> { | ||||
|         card.enabled = this.enabled; | ||||
|         card.runner = await this.getRunner(); | ||||
|  | ||||
|         return card; | ||||
|     } | ||||
|  | ||||
|     public async getRunner(): Promise<Runner> { | ||||
|         if (!this.runner) { return null; } | ||||
|         const runner = await getConnection().getRepository(Runner).findOne({ id: this.runner }); | ||||
|         if (!runner) { | ||||
|             throw new RunnerNotFoundError(); | ||||
|         } | ||||
|         return runner; | ||||
|     } | ||||
| } | ||||
| @@ -1,9 +1,9 @@ | ||||
| import { IsInt, IsOptional } from 'class-validator'; | ||||
| import { getConnectionManager } from 'typeorm'; | ||||
| import { AddressNotFoundError } from '../../errors/AddressErrors'; | ||||
| import { Address } from '../entities/Address'; | ||||
| import { RunnerOrganisation } from '../entities/RunnerOrganisation'; | ||||
| import { CreateRunnerGroup } from './CreateRunnerGroup'; | ||||
| import { AddressNotFoundError } from '../../../errors/AddressErrors'; | ||||
| import { Address } from '../../entities/Address'; | ||||
| import { RunnerOrganisation } from '../../entities/RunnerOrganisation'; | ||||
| import { CreateRunnerGroup } from '../create/CreateRunnerGroup'; | ||||
| 
 | ||||
| /** | ||||
|  * This class is used to update a RunnerOrganisation entity (via put request). | ||||
| @@ -41,7 +41,7 @@ export class UpdateRunnerOrganisation extends CreateRunnerGroup { | ||||
|     /** | ||||
|      * Updates a provided RunnerOrganisation entity based on this. | ||||
|      */ | ||||
|     public async updateRunnerOrganisation(organisation: RunnerOrganisation): Promise<RunnerOrganisation> { | ||||
|     public async update(organisation: RunnerOrganisation): Promise<RunnerOrganisation> { | ||||
| 
 | ||||
|         organisation.name = this.name; | ||||
|         organisation.contact = await this.getContact(); | ||||
| @@ -1,10 +1,10 @@ | ||||
| import { IsInt, IsNotEmpty, IsObject } from 'class-validator'; | ||||
| import { getConnectionManager } from 'typeorm'; | ||||
| import { RunnerOrganisationNotFoundError, RunnerOrganisationWrongTypeError } from '../../errors/RunnerOrganisationErrors'; | ||||
| import { RunnerTeamNeedsParentError } from '../../errors/RunnerTeamErrors'; | ||||
| import { RunnerOrganisation } from '../entities/RunnerOrganisation'; | ||||
| import { RunnerTeam } from '../entities/RunnerTeam'; | ||||
| import { CreateRunnerGroup } from './CreateRunnerGroup'; | ||||
| import { RunnerOrganisationNotFoundError, RunnerOrganisationWrongTypeError } from '../../../errors/RunnerOrganisationErrors'; | ||||
| import { RunnerTeamNeedsParentError } from '../../../errors/RunnerTeamErrors'; | ||||
| import { RunnerOrganisation } from '../../entities/RunnerOrganisation'; | ||||
| import { RunnerTeam } from '../../entities/RunnerTeam'; | ||||
| import { CreateRunnerGroup } from '../create/CreateRunnerGroup'; | ||||
| 
 | ||||
| /** | ||||
|  * This class is used to update a RunnerTeam entity (via put request). | ||||
| @@ -45,7 +45,7 @@ export class UpdateRunnerTeam extends CreateRunnerGroup { | ||||
|     /** | ||||
|      * Updates a provided RunnerTeam entity based on this. | ||||
|      */ | ||||
|     public async updateRunnerTeam(team: RunnerTeam): Promise<RunnerTeam> { | ||||
|     public async update(team: RunnerTeam): Promise<RunnerTeam> { | ||||
| 
 | ||||
|         team.name = this.name; | ||||
|         team.parentGroup = await this.getParent(); | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { IsBoolean, IsInt, IsOptional, IsPositive } from 'class-validator'; | ||||
| import { getConnection } from 'typeorm'; | ||||
| import { RunnerNotFoundError } from '../../errors/RunnerErrors'; | ||||
| import { Runner } from '../entities/Runner'; | ||||
| import { Scan } from '../entities/Scan'; | ||||
| import { RunnerNotFoundError } from '../../../errors/RunnerErrors'; | ||||
| import { Runner } from '../../entities/Runner'; | ||||
| import { Scan } from '../../entities/Scan'; | ||||
| 
 | ||||
| /** | ||||
|  * This class is used to update a Scan entity (via put request) | ||||
| @@ -41,7 +41,7 @@ export abstract class UpdateScan { | ||||
|      * Update a Scan entity based on this. | ||||
|      * @param scan The scan that shall be updated. | ||||
|      */ | ||||
|     public async updateScan(scan: Scan): Promise<Scan> { | ||||
|     public async update(scan: Scan): Promise<Scan> { | ||||
|         scan.distance = this.distance; | ||||
|         scan.valid = this.valid; | ||||
|         scan.runner = await this.getRunner(); | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { IsBoolean, IsInt, IsOptional, IsString } from 'class-validator'; | ||||
| import { ScanStation } from '../entities/ScanStation'; | ||||
| import { ScanStation } from '../../entities/ScanStation'; | ||||
| 
 | ||||
| /** | ||||
|  * This class is used to update a ScanStation entity (via put request) | ||||
| @@ -30,7 +30,7 @@ export class UpdateScanStation { | ||||
|      * Update a ScanStation entity based on this. | ||||
|      * @param station The station that shall be updated. | ||||
|      */ | ||||
|     public async updateStation(station: ScanStation): Promise<ScanStation> { | ||||
|     public async update(station: ScanStation): Promise<ScanStation> { | ||||
|         station.description = this.description; | ||||
|         station.enabled = this.enabled; | ||||
| 
 | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { IsInt, IsNotEmpty, IsOptional, IsPositive, IsString } from 'class-validator'; | ||||
| import { TrackLapTimeCantBeNegativeError } from '../../errors/TrackErrors'; | ||||
| import { Track } from '../entities/Track'; | ||||
| import { TrackLapTimeCantBeNegativeError } from '../../../errors/TrackErrors'; | ||||
| import { Track } from '../../entities/Track'; | ||||
| 
 | ||||
| /** | ||||
|  * This class is used to update a Track entity (via put request). | ||||
| @@ -37,7 +37,7 @@ export class UpdateTrack { | ||||
|      * Update a Track entity based on this. | ||||
|      * @param track The track that shall be updated. | ||||
|      */ | ||||
|     public updateTrack(track: Track): Track { | ||||
|     public async update(track: Track): Promise<Track> { | ||||
|         track.name = this.name; | ||||
|         track.distance = this.distance; | ||||
|         track.minimumLapTime = this.minimumLapTime; | ||||
							
								
								
									
										81
									
								
								src/models/actions/update/UpdateTrackScan.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/models/actions/update/UpdateTrackScan.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| import { IsBoolean, IsInt, IsOptional } from 'class-validator'; | ||||
| import { getConnection } from 'typeorm'; | ||||
| import { RunnerNotFoundError } from '../../../errors/RunnerErrors'; | ||||
| import { ScanStationNotFoundError } from '../../../errors/ScanStationErrors'; | ||||
| import { Runner } from '../../entities/Runner'; | ||||
| import { ScanStation } from '../../entities/ScanStation'; | ||||
| import { TrackScan } from '../../entities/TrackScan'; | ||||
|  | ||||
| /** | ||||
|  * This class is used to update a TrackScan entity (via put request) | ||||
|  */ | ||||
| export abstract class UpdateTrackScan { | ||||
|     /** | ||||
|      * The updated scan's id. | ||||
|      * This shouldn't have changed but it is here in case anyone ever wants to enable id changes (whyever they would want to). | ||||
|      */ | ||||
|     @IsInt() | ||||
|     id: number; | ||||
|  | ||||
|     /** | ||||
|      * The updated scan's associated runner. | ||||
|      * This is important to link ran distances to runners. | ||||
|      */ | ||||
|     @IsInt() | ||||
|     @IsOptional() | ||||
|     runner?: number; | ||||
|  | ||||
|     /** | ||||
|      * Is the updated scan valid (for fraud reasons). | ||||
|      */ | ||||
|     @IsBoolean() | ||||
|     @IsOptional() | ||||
|     valid?: boolean = true; | ||||
|  | ||||
|     /** | ||||
|      * The updated scan's associated station. | ||||
|      * This is important to link ran distances to runners. | ||||
|      */ | ||||
|     @IsInt() | ||||
|     @IsOptional() | ||||
|     public station?: number; | ||||
|  | ||||
|     /** | ||||
|      * Update a TrackScan entity based on this. | ||||
|      * @param scan The scan that shall be updated. | ||||
|      */ | ||||
|     public async update(scan: TrackScan): Promise<TrackScan> { | ||||
|         scan.valid = this.valid; | ||||
|         if (this.runner) { | ||||
|             scan.runner = await this.getRunner(); | ||||
|         } | ||||
|         if (this.station) { | ||||
|             scan.station = await this.getStation(); | ||||
|         } | ||||
|         scan.track = scan.station.track; | ||||
|  | ||||
|         return scan; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets a runner based on the runner id provided via this.runner. | ||||
|      */ | ||||
|     public async getRunner(): Promise<Runner> { | ||||
|         const runner = await getConnection().getRepository(Runner).findOne({ id: this.runner }); | ||||
|         if (!runner) { | ||||
|             throw new RunnerNotFoundError(); | ||||
|         } | ||||
|         return runner; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets a runner based on the runner id provided via this.runner. | ||||
|      */ | ||||
|     public async getStation(): Promise<ScanStation> { | ||||
|         const station = await getConnection().getRepository(ScanStation).findOne({ id: this.station }); | ||||
|         if (!station) { | ||||
|             throw new ScanStationNotFoundError(); | ||||
|         } | ||||
|         return station; | ||||
|     } | ||||
| } | ||||
| @@ -1,11 +1,11 @@ | ||||
| import * as argon2 from "argon2"; | ||||
| import { IsBoolean, IsEmail, IsInt, IsOptional, IsPhoneNumber, IsString, IsUrl } from 'class-validator'; | ||||
| import { getConnectionManager } from 'typeorm'; | ||||
| import { config } from '../../config'; | ||||
| import { UsernameOrEmailNeededError } from '../../errors/AuthError'; | ||||
| import { UserGroupNotFoundError } from '../../errors/UserGroupErrors'; | ||||
| import { User } from '../entities/User'; | ||||
| import { UserGroup } from '../entities/UserGroup'; | ||||
| import { config } from '../../../config'; | ||||
| import { UsernameOrEmailNeededError } from '../../../errors/AuthError'; | ||||
| import { UserGroupNotFoundError } from '../../../errors/UserGroupErrors'; | ||||
| import { User } from '../../entities/User'; | ||||
| import { UserGroup } from '../../entities/UserGroup'; | ||||
| 
 | ||||
| /** | ||||
|  * This class is used to update a User entity (via put request). | ||||
| @@ -98,7 +98,7 @@ export class UpdateUser { | ||||
|      * Updates a user entity based on this. | ||||
|      * @param user The user that shall be updated. | ||||
|      */ | ||||
|     public async updateUser(user: User): Promise<User> { | ||||
|     public async update(user: User): Promise<User> { | ||||
|         user.email = this.email; | ||||
|         user.username = this.username; | ||||
|         if ((user.email === undefined || user.email === null) && (user.username === undefined || user.username === null)) { | ||||
							
								
								
									
										39
									
								
								src/models/actions/update/UpdateUserGroup.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/models/actions/update/UpdateUserGroup.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| import { IsInt, IsOptional, IsString } from 'class-validator'; | ||||
| import { UserGroup } from '../../entities/UserGroup'; | ||||
|  | ||||
| /** | ||||
|  * This class is used to update a UserGroup entity (via put request). | ||||
|  */ | ||||
| export class UpdateUserGroup { | ||||
|  | ||||
|     /** | ||||
|      * The updated group's id. | ||||
|      * This shouldn't have changed but it is here in case anyone ever wants to enable id changes (whyever they would want to). | ||||
|      */ | ||||
|     @IsInt() | ||||
|     id: number; | ||||
|  | ||||
|     /** | ||||
|      * The updated group's name. | ||||
|      */ | ||||
|     @IsString() | ||||
|     name: string; | ||||
|  | ||||
|     /** | ||||
|      * The updated groups's description. | ||||
|      */ | ||||
|     @IsString() | ||||
|     @IsOptional() | ||||
|     description?: string; | ||||
|  | ||||
|     /** | ||||
|      * Updates a group entity based on this. | ||||
|      * @param group The group that shall be updated. | ||||
|      */ | ||||
|     public async update(group: UserGroup): Promise<UserGroup> { | ||||
|         group.name = this.name; | ||||
|         group.description = this.description; | ||||
|  | ||||
|         return group; | ||||
|     } | ||||
| } | ||||
| @@ -1,12 +1,13 @@ | ||||
| import { | ||||
|   IsBoolean, | ||||
|   IsEAN, | ||||
|  | ||||
|   IsInt, | ||||
|   IsNotEmpty, | ||||
|   IsOptional, | ||||
|   IsString | ||||
|  | ||||
|   IsOptional | ||||
| } from "class-validator"; | ||||
| import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; | ||||
| import { RunnerCardIdOutOfRangeError } from '../../errors/RunnerCardErrors'; | ||||
| import { ResponseRunnerCard } from '../responses/ResponseRunnerCard'; | ||||
| import { Runner } from "./Runner"; | ||||
| import { TrackScan } from "./TrackScan"; | ||||
|  | ||||
| @@ -32,17 +33,6 @@ export class RunnerCard { | ||||
|   @ManyToOne(() => Runner, runner => runner.cards, { nullable: true }) | ||||
|   runner: Runner; | ||||
|  | ||||
|   /** | ||||
|    * The card's code. | ||||
|    * This has to be able to being converted to something barcode compatible. | ||||
|    * Will get automaticlly generated (not implemented yet). | ||||
|    */ | ||||
|   @Column() | ||||
|   @IsEAN() | ||||
|   @IsString() | ||||
|   @IsNotEmpty() | ||||
|   code: string; | ||||
|  | ||||
|   /** | ||||
|    * Is the card enabled (for fraud reasons)? | ||||
|    * Default: true | ||||
| @@ -58,10 +48,37 @@ export class RunnerCard { | ||||
|   @OneToMany(() => TrackScan, scan => scan.track, { nullable: true }) | ||||
|   scans: TrackScan[]; | ||||
|  | ||||
|   /** | ||||
|    * Generates a ean-13 compliant string for barcode generation. | ||||
|    */ | ||||
|   public get code(): string { | ||||
|     const multiply = [1, 3]; | ||||
|     let total = 0; | ||||
|     this.paddedId.split('').forEach((letter, index) => { | ||||
|       total += parseInt(letter, 10) * multiply[index % 2]; | ||||
|     }); | ||||
|     const checkSum = (Math.ceil(total / 10) * 10) - total; | ||||
|     return this.paddedId + checkSum.toString(); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Returns this card's id as a string padded to the length of 12 characters with leading zeros. | ||||
|    */ | ||||
|   private get paddedId(): string { | ||||
|     let id: string = this.id.toString(); | ||||
|  | ||||
|     if (id.length > 12) { | ||||
|       throw new RunnerCardIdOutOfRangeError(); | ||||
|     } | ||||
|     while (id.length < 12) { id = '0' + id; } | ||||
|  | ||||
|     return id; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Turns this entity into it's response class. | ||||
|    */ | ||||
|   public toResponse() { | ||||
|     return new Error("NotImplemented"); | ||||
|     return new ResponseRunnerCard(this); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| import { | ||||
|   IsDateString, | ||||
|   IsInt, | ||||
|   IsNotEmpty, | ||||
|  | ||||
| @@ -57,9 +56,8 @@ export class TrackScan extends Scan { | ||||
|    * Will be used to implement fraud detection. | ||||
|    */ | ||||
|   @Column() | ||||
|   @IsDateString() | ||||
|   @IsNotEmpty() | ||||
|   timestamp: string; | ||||
|   @IsInt() | ||||
|   timestamp: number; | ||||
|  | ||||
|   /** | ||||
|    * Turns this entity into it's response class. | ||||
|   | ||||
| @@ -12,5 +12,6 @@ export enum PermissionTarget { | ||||
|     STATSCLIENT = 'STATSCLIENT', | ||||
|     DONOR = 'DONOR', | ||||
|     SCAN = 'SCAN', | ||||
|     STATION = 'STATION' | ||||
|     STATION = 'STATION', | ||||
|     CARD = 'CARD' | ||||
| } | ||||
| @@ -3,7 +3,7 @@ import { IsInt, IsString } from 'class-validator'; | ||||
| /** | ||||
|  * Defines the repsonse auth. | ||||
| */ | ||||
| export class Auth { | ||||
| export class ResponseAuth { | ||||
|   /** | ||||
|   * The access_token - JWT shortterm access token. | ||||
|   */ | ||||
|   | ||||
							
								
								
									
										53
									
								
								src/models/responses/ResponseRunnerCard.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/models/responses/ResponseRunnerCard.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| import { IsBoolean, IsEAN, IsInt, IsNotEmpty, IsObject, IsString } from "class-validator"; | ||||
| import { RunnerCard } from '../entities/RunnerCard'; | ||||
| import { ResponseRunner } from './ResponseRunner'; | ||||
|  | ||||
| /** | ||||
|  * Defines the runner card response. | ||||
| */ | ||||
| export class ResponseRunnerCard { | ||||
|     /** | ||||
|      * The card's id. | ||||
|      */ | ||||
|     @IsInt() | ||||
|     id: number;; | ||||
|  | ||||
|     /** | ||||
|      * The card's associated runner. | ||||
|      * This is important to link scans to runners. | ||||
|      */ | ||||
|     @IsObject() | ||||
|     runner: ResponseRunner | null; | ||||
|  | ||||
|     /** | ||||
|      * The card's code. | ||||
|      */ | ||||
|     @IsEAN() | ||||
|     @IsString() | ||||
|     @IsNotEmpty() | ||||
|     code: string; | ||||
|  | ||||
|     /** | ||||
|      * Is the enabled valid (for fraud reasons). | ||||
|      * The determination of validity will work differently for every child class. | ||||
|      */ | ||||
|     @IsBoolean() | ||||
|     enabled: boolean = true; | ||||
|  | ||||
|     /** | ||||
|      * Creates a ResponseRunnerCard object from a runner card. | ||||
|      * @param card The card the response shall be build for. | ||||
|      */ | ||||
|     public constructor(card: RunnerCard) { | ||||
|         this.id = card.id; | ||||
|         if (!card.runner) { this.runner = null } | ||||
|         else { this.runner = card.runner.toResponse(); } | ||||
|         try { | ||||
|             this.code = card.code; | ||||
|         } catch (error) { | ||||
|             this.code = "0000000000000" | ||||
|         } | ||||
|  | ||||
|         this.enabled = card.enabled; | ||||
|     } | ||||
| } | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { IsDateString, IsNotEmpty } from "class-validator"; | ||||
| import { RunnerCard } from '../entities/RunnerCard'; | ||||
| import { ScanStation } from '../entities/ScanStation'; | ||||
| import { TrackScan } from '../entities/TrackScan'; | ||||
| import { ResponseRunnerCard } from './ResponseRunnerCard'; | ||||
| import { ResponseScan } from './ResponseScan'; | ||||
| import { ResponseScanStation } from './ResponseScanStation'; | ||||
| import { ResponseTrack } from './ResponseTrack'; | ||||
|  | ||||
| /** | ||||
| @@ -19,20 +19,20 @@ export class ResponseTrackScan extends ResponseScan { | ||||
|      * The runnerCard associated with the scan. | ||||
|      */ | ||||
|     @IsNotEmpty() | ||||
|     card: RunnerCard; | ||||
|     card: ResponseRunnerCard; | ||||
|  | ||||
|     /** | ||||
|      * The scanning station that created the scan. | ||||
|      */ | ||||
|     @IsNotEmpty() | ||||
|     station: ScanStation; | ||||
|     station: ResponseScanStation; | ||||
|  | ||||
|     /** | ||||
|      * The scan's creation timestamp. | ||||
|      */ | ||||
|     @IsDateString() | ||||
|     @IsNotEmpty() | ||||
|     timestamp: string; | ||||
|     timestamp: number; | ||||
|  | ||||
|     /** | ||||
|      * Creates a ResponseTrackScan object from a scan. | ||||
| @@ -41,8 +41,9 @@ export class ResponseTrackScan extends ResponseScan { | ||||
|     public constructor(scan: TrackScan) { | ||||
|         super(scan); | ||||
|         this.track = new ResponseTrack(scan.track); | ||||
|         this.card = scan.card; | ||||
|         this.station = scan.station; | ||||
|         this.card = scan.card.toResponse(); | ||||
|         this.station = scan.station.toResponse(); | ||||
|         this.timestamp = scan.timestamp; | ||||
|         this.distance = scan.distance; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { Connection } from 'typeorm'; | ||||
| import { Factory, Seeder } from 'typeorm-seeding'; | ||||
| import { CreatePermission } from '../models/actions/CreatePermission'; | ||||
| import { CreateUser } from '../models/actions/CreateUser'; | ||||
| import { CreateUserGroup } from '../models/actions/CreateUserGroup'; | ||||
| import { CreatePermission } from '../models/actions/create/CreatePermission'; | ||||
| import { CreateUser } from '../models/actions/create/CreateUser'; | ||||
| import { CreateUserGroup } from '../models/actions/create/CreateUserGroup'; | ||||
| import { Permission } from '../models/entities/Permission'; | ||||
| import { User } from '../models/entities/User'; | ||||
| import { UserGroup } from '../models/entities/UserGroup'; | ||||
| @@ -24,7 +24,7 @@ export default class SeedUsers implements Seeder { | ||||
|         let adminGroup = new CreateUserGroup(); | ||||
|         adminGroup.name = "ADMINS"; | ||||
|         adminGroup.description = "Have all possible permissions"; | ||||
|         return await connection.getRepository(UserGroup).save(await adminGroup.toUserGroup()); | ||||
|         return await connection.getRepository(UserGroup).save(await adminGroup.toEntity()); | ||||
|     } | ||||
|  | ||||
|     public async createUser(connection: Connection, group: number) { | ||||
| @@ -34,7 +34,7 @@ export default class SeedUsers implements Seeder { | ||||
|         initialUser.username = "demo"; | ||||
|         initialUser.password = "demo"; | ||||
|         initialUser.groups = group; | ||||
|         return await connection.getRepository(User).save(await initialUser.toUser()); | ||||
|         return await connection.getRepository(User).save(await initialUser.toEntity()); | ||||
|     } | ||||
|  | ||||
|     public async createPermissions(connection: Connection, principal: number) { | ||||
| @@ -45,7 +45,7 @@ export default class SeedUsers implements Seeder { | ||||
|                 permission.target = <PermissionTarget>target; | ||||
|                 permission.action = <PermissionAction>action; | ||||
|                 permission.principal = principal; | ||||
|                 await repo.save(await permission.toPermission()); | ||||
|                 await repo.save(await permission.toEntity()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
							
								
								
									
										144
									
								
								src/tests/cards/cards_add.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								src/tests/cards/cards_add.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | ||||
| import axios from 'axios'; | ||||
| import { config } from '../../config'; | ||||
| const base = "http://localhost:" + config.internal_port | ||||
|  | ||||
| let access_token; | ||||
| let axios_config; | ||||
|  | ||||
| beforeAll(async () => { | ||||
| 	const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); | ||||
| 	access_token = res.data["access_token"]; | ||||
| 	axios_config = { | ||||
| 		headers: { "authorization": "Bearer " + access_token }, | ||||
| 		validateStatus: undefined | ||||
| 	}; | ||||
| }); | ||||
|  | ||||
|  | ||||
| describe('POST /api/cards illegally', () => { | ||||
| 	it('non-existant runner input should return 404', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', { | ||||
| 			"runner": 999999999999999999999999 | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(404); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('POST /api/cards successfully (without runner)', () => { | ||||
| 	it('creating a card with the minimum amount of parameters should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', null, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		delete res.data.id; | ||||
| 		delete res.data.code; | ||||
| 		expect(res.data).toEqual({ | ||||
| 			"runner": null, | ||||
| 			"enabled": true | ||||
| 		}); | ||||
| 	}); | ||||
| 	it('creating a disabled card should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', { | ||||
| 			"enabled": false | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		delete res.data.id; | ||||
| 		delete res.data.code; | ||||
| 		expect(res.data).toEqual({ | ||||
| 			"runner": null, | ||||
| 			"enabled": false | ||||
| 		}); | ||||
| 	}); | ||||
| 	it('creating a enabled card should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', { | ||||
| 			"enabled": true | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		delete res.data.id; | ||||
| 		delete res.data.code; | ||||
| 		expect(res.data).toEqual({ | ||||
| 			"runner": null, | ||||
| 			"enabled": true | ||||
| 		}); | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('POST /api/cards successfully (with runner)', () => { | ||||
| 	let added_org; | ||||
| 	let added_runner; | ||||
| 	it('creating a new org with just a name should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/organisations', { | ||||
| 			"name": "test123" | ||||
| 		}, axios_config); | ||||
| 		added_org = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res2 = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		delete res2.data.group; | ||||
| 		added_runner = res2.data; | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a card with the minimum amount of parameters should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', { | ||||
| 			"runner": added_runner.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		delete res.data.id; | ||||
| 		delete res.data.code; | ||||
| 		expect(res.data).toEqual({ | ||||
| 			"runner": added_runner, | ||||
| 			"enabled": true | ||||
| 		}); | ||||
| 	}); | ||||
| 	it('creating a card with runner (no optional params) should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', { | ||||
| 			"runner": added_runner.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		delete res.data.id; | ||||
| 		delete res.data.code; | ||||
| 		expect(res.data).toEqual({ | ||||
| 			"runner": added_runner, | ||||
| 			"enabled": true | ||||
| 		}); | ||||
| 	}); | ||||
| 	it('creating a enabled card with runner should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', { | ||||
| 			"runner": added_runner.id, | ||||
| 			"enabled": true | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		delete res.data.id; | ||||
| 		delete res.data.code; | ||||
| 		expect(res.data).toEqual({ | ||||
| 			"runner": added_runner, | ||||
| 			"enabled": true | ||||
| 		}); | ||||
| 	}); | ||||
| 	it('creating a disabled card with runner should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', { | ||||
| 			"runner": added_runner.id, | ||||
| 			"enabled": false | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		delete res.data.id; | ||||
| 		delete res.data.code; | ||||
| 		expect(res.data).toEqual({ | ||||
| 			"runner": added_runner, | ||||
| 			"enabled": false | ||||
| 		}); | ||||
| 	}); | ||||
| }); | ||||
							
								
								
									
										45
									
								
								src/tests/cards/cards_delete.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/tests/cards/cards_delete.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| import axios from 'axios'; | ||||
| import { config } from '../../config'; | ||||
| const base = "http://localhost:" + config.internal_port | ||||
|  | ||||
| let access_token; | ||||
| let axios_config; | ||||
|  | ||||
| beforeAll(async () => { | ||||
| 	const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); | ||||
| 	access_token = res.data["access_token"]; | ||||
| 	axios_config = { | ||||
| 		headers: { "authorization": "Bearer " + access_token }, | ||||
| 		validateStatus: undefined | ||||
| 	}; | ||||
| }); | ||||
|  | ||||
| // --------------- | ||||
|  | ||||
| describe('DELETE card', () => { | ||||
| 	let added_card; | ||||
| 	it('creating card without runner should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', null, axios_config); | ||||
| 		added_card = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('delete card', async () => { | ||||
| 		const res2 = await axios.delete(base + '/api/cards/' + added_card.id, axios_config); | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 		expect(res2.data).toEqual(added_card); | ||||
| 	}); | ||||
| 	it('check if card really was deleted', async () => { | ||||
| 		const res3 = await axios.get(base + '/api/cards/' + added_card.id, axios_config); | ||||
| 		expect(res3.status).toEqual(404); | ||||
| 		expect(res3.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('DELETE card (non-existant)', () => { | ||||
| 	it('delete', async () => { | ||||
| 		const res2 = await axios.delete(base + '/api/cards/0', axios_config); | ||||
| 		expect(res2.status).toEqual(204); | ||||
| 	}); | ||||
| }); | ||||
							
								
								
									
										46
									
								
								src/tests/cards/cards_get.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/tests/cards/cards_get.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| import axios from 'axios'; | ||||
| import { config } from '../../config'; | ||||
| const base = "http://localhost:" + config.internal_port | ||||
|  | ||||
| let access_token; | ||||
| let axios_config; | ||||
|  | ||||
| beforeAll(async () => { | ||||
| 	const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); | ||||
| 	access_token = res.data["access_token"]; | ||||
| 	axios_config = { | ||||
| 		headers: { "authorization": "Bearer " + access_token }, | ||||
| 		validateStatus: undefined | ||||
| 	}; | ||||
| }); | ||||
|  | ||||
| describe('GET /api/cards sucessfully', () => { | ||||
| 	it('basic get should return 200', async () => { | ||||
| 		const res = await axios.get(base + '/api/cards', axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('GET /api/cards illegally', () => { | ||||
| 	it('get for non-existant track should return 404', async () => { | ||||
| 		const res = await axios.get(base + '/api/cards/-1', axios_config); | ||||
| 		expect(res.status).toEqual(404); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('adding + getting cards (no runner)', () => { | ||||
| 	let added_card; | ||||
| 	it('correct distance and runner input should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', null, axios_config); | ||||
| 		added_card = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('check if scans was added (no parameter validation)', async () => { | ||||
| 		const res = await axios.get(base + '/api/cards/' + added_card.id, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 	}); | ||||
| }); | ||||
							
								
								
									
										163
									
								
								src/tests/cards/cards_update.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								src/tests/cards/cards_update.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,163 @@ | ||||
| import axios from 'axios'; | ||||
| import { config } from '../../config'; | ||||
| const base = "http://localhost:" + config.internal_port | ||||
|  | ||||
| let access_token; | ||||
| let axios_config; | ||||
|  | ||||
| beforeAll(async () => { | ||||
| 	const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); | ||||
| 	access_token = res.data["access_token"]; | ||||
| 	axios_config = { | ||||
| 		headers: { "authorization": "Bearer " + access_token }, | ||||
| 		validateStatus: undefined | ||||
| 	}; | ||||
| }); | ||||
|  | ||||
| describe('adding + updating illegally', () => { | ||||
| 	let added_card; | ||||
| 	it('creating card without runner should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', null, axios_config); | ||||
| 		added_card = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating empty should return 400', async () => { | ||||
| 		const res2 = await axios.put(base + '/api/cards/' + added_card.id, null, axios_config); | ||||
| 		expect(res2.status).toEqual(400); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating with wrong id should return 406', async () => { | ||||
| 		const res2 = await axios.put(base + '/api/cards/' + added_card.id, { | ||||
| 			"id": added_card.id + 1 | ||||
| 		}, axios_config); | ||||
| 		expect(res2.status).toEqual(406); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('update with invalid runner id should return 404', async () => { | ||||
| 		const res2 = await axios.put(base + '/api/cards/' + added_card.id, { | ||||
| 			"id": added_card.id, | ||||
| 			"runner": 9999999999999999999999999 | ||||
| 		}, axios_config); | ||||
| 		expect(res2.status).toEqual(404); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('adding + updating card.runner successfully', () => { | ||||
| 	let added_org; | ||||
| 	let added_runner; | ||||
| 	let added_runner2; | ||||
| 	let added_card; | ||||
| 	it('creating a new org with just a name should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/organisations', { | ||||
| 			"name": "test123" | ||||
| 		}, axios_config); | ||||
| 		added_org = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res2 = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		delete res2.data.group; | ||||
| 		added_runner = res2.data; | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res2 = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		delete res2.data.group; | ||||
| 		added_runner2 = res2.data; | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating card without runner should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', null, axios_config); | ||||
| 		added_card = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('valid runner update (add runner) should return 200', async () => { | ||||
| 		const res2 = await axios.put(base + '/api/cards/' + added_card.id, { | ||||
| 			"id": added_card.id, | ||||
| 			"runner": added_runner.id | ||||
| 		}, axios_config); | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json"); | ||||
| 		expect(res2.data).toEqual({ | ||||
| 			"id": added_card.id, | ||||
| 			"runner": added_runner, | ||||
| 			"enabled": true, | ||||
| 			"code": added_card.code | ||||
| 		}); | ||||
| 	}); | ||||
| 	it('valid runner update (change runner) should return 200', async () => { | ||||
| 		const res2 = await axios.put(base + '/api/cards/' + added_card.id, { | ||||
| 			"id": added_card.id, | ||||
| 			"runner": added_runner2.id | ||||
| 		}, axios_config); | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json"); | ||||
| 		expect(res2.data).toEqual({ | ||||
| 			"id": added_card.id, | ||||
| 			"runner": added_runner2, | ||||
| 			"enabled": true, | ||||
| 			"code": added_card.code | ||||
| 		}); | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('adding + updating other values successfully', () => { | ||||
| 	let added_card; | ||||
| 	it('creating card without runner should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', null, axios_config); | ||||
| 		added_card = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('valid update changeing nothing should return 200', async () => { | ||||
| 		const res2 = await axios.put(base + '/api/cards/' + added_card.id, { | ||||
| 			"id": added_card.id | ||||
| 		}, axios_config); | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json"); | ||||
| 		expect(res2.data).toEqual(added_card); | ||||
| 	}); | ||||
| 	it('valid disable update should return 200', async () => { | ||||
| 		const res2 = await axios.put(base + '/api/cards/' + added_card.id, { | ||||
| 			"id": added_card.id, | ||||
| 			"enabled": false | ||||
| 		}, axios_config); | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json"); | ||||
| 		expect(res2.data).toEqual({ | ||||
| 			"id": added_card.id, | ||||
| 			"runner": null, | ||||
| 			"enabled": false, | ||||
| 			"code": added_card.code | ||||
| 		}); | ||||
| 	}); | ||||
| 	it('valid enable update should return 200', async () => { | ||||
| 		const res2 = await axios.put(base + '/api/cards/' + added_card.id, { | ||||
| 			"id": added_card.id, | ||||
| 			"enabled": true | ||||
| 		}, axios_config); | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json"); | ||||
| 		expect(res2.data).toEqual({ | ||||
| 			"id": added_card.id, | ||||
| 			"runner": null, | ||||
| 			"enabled": true, | ||||
| 			"code": added_card.code | ||||
| 		}); | ||||
| 	}); | ||||
| }); | ||||
|  | ||||
| @@ -84,6 +84,7 @@ describe('POST /api/scans successfully', () => { | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		delete res2.data.group; | ||||
| 		delete res2.data.distance; | ||||
| 		added_runner = res2.data; | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| @@ -96,6 +97,7 @@ describe('POST /api/scans successfully', () => { | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		delete res.data.id; | ||||
| 		delete res.data.runner.distance; | ||||
| 		expect(res.data).toEqual({ | ||||
| 			"runner": added_runner, | ||||
| 			"distance": 200, | ||||
| @@ -111,6 +113,7 @@ describe('POST /api/scans successfully', () => { | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		delete res.data.id; | ||||
| 		delete res.data.runner.distance; | ||||
| 		expect(res.data).toEqual({ | ||||
| 			"runner": added_runner, | ||||
| 			"distance": 200, | ||||
| @@ -126,6 +129,7 @@ describe('POST /api/scans successfully', () => { | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		delete res.data.id; | ||||
| 		delete res.data.runner.distance; | ||||
| 		expect(res.data).toEqual({ | ||||
| 			"runner": added_runner, | ||||
| 			"distance": 200, | ||||
| @@ -154,6 +158,7 @@ describe('POST /api/scans successfully via scan station', () => { | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		delete res2.data.group; | ||||
| 		delete res2.data.distance; | ||||
| 		added_runner = res2.data; | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| @@ -186,6 +191,7 @@ describe('POST /api/scans successfully via scan station', () => { | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		delete res.data.id; | ||||
| 		delete res.data.runner.distance; | ||||
| 		expect(res.data).toEqual({ | ||||
| 			"runner": added_runner, | ||||
| 			"distance": 200, | ||||
| @@ -204,6 +210,7 @@ describe('POST /api/scans successfully via scan station', () => { | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		delete res.data.id; | ||||
| 		delete res.data.runner.distance; | ||||
| 		expect(res.data).toEqual({ | ||||
| 			"runner": added_runner, | ||||
| 			"distance": 200, | ||||
| @@ -222,6 +229,7 @@ describe('POST /api/scans successfully via scan station', () => { | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		delete res.data.id; | ||||
| 		delete res.data.runner.distance; | ||||
| 		expect(res.data).toEqual({ | ||||
| 			"runner": added_runner, | ||||
| 			"distance": 200, | ||||
|   | ||||
| @@ -44,6 +44,7 @@ describe('DELETE scan', () => { | ||||
| 			"distance": 1000 | ||||
| 		}, axios_config); | ||||
| 		added_scan = res.data; | ||||
| 		delete res.data.runner.distance; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| @@ -51,6 +52,7 @@ describe('DELETE scan', () => { | ||||
| 		const res2 = await axios.delete(base + '/api/scans/' + added_scan.id, axios_config); | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 		delete res2.data.runner.distance; | ||||
| 		expect(res2.data).toEqual(added_scan); | ||||
| 	}); | ||||
| 	it('check if scan really was deleted', async () => { | ||||
|   | ||||
| @@ -100,6 +100,7 @@ describe('adding + updating successfilly', () => { | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		delete res2.data.group; | ||||
| 		delete res2.data.distance; | ||||
| 		added_runner = res2.data; | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| @@ -121,6 +122,7 @@ describe('adding + updating successfilly', () => { | ||||
| 		}, axios_config); | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 		delete res2.data.runner.distance; | ||||
| 		expect(res2.data).toEqual({ | ||||
| 			"id": added_scan.id, | ||||
| 			"runner": added_runner, | ||||
| @@ -137,7 +139,8 @@ describe('adding + updating successfilly', () => { | ||||
| 			"valid": false | ||||
| 		}, axios_config); | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 		expect(res2.headers['content-type']).toContain("application/json"); | ||||
| 		delete res2.data.runner.distance; | ||||
| 		expect(res2.data).toEqual({ | ||||
| 			"id": added_scan.id, | ||||
| 			"runner": added_runner, | ||||
| @@ -152,6 +155,7 @@ describe('adding + updating successfilly', () => { | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		delete res2.data.group; | ||||
| 		delete res2.data.distance; | ||||
| 		added_runner2 = res2.data; | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| @@ -164,6 +168,7 @@ describe('adding + updating successfilly', () => { | ||||
| 		}, axios_config); | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json"); | ||||
| 		delete res2.data.runner.distance; | ||||
| 		expect(res2.data).toEqual({ | ||||
| 			"id": added_scan.id, | ||||
| 			"runner": added_runner2, | ||||
|   | ||||
							
								
								
									
										249
									
								
								src/tests/trackscans/trackscans_add.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								src/tests/trackscans/trackscans_add.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,249 @@ | ||||
| import axios from 'axios'; | ||||
| import { config } from '../../config'; | ||||
| const base = "http://localhost:" + config.internal_port | ||||
|  | ||||
| let access_token; | ||||
| let axios_config; | ||||
|  | ||||
| beforeAll(async () => { | ||||
| 	const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); | ||||
| 	access_token = res.data["access_token"]; | ||||
| 	axios_config = { | ||||
| 		headers: { "authorization": "Bearer " + access_token }, | ||||
| 		validateStatus: undefined | ||||
| 	}; | ||||
| }); | ||||
|  | ||||
|  | ||||
| describe('POST /api/scans illegally', () => { | ||||
| 	let added_org; | ||||
| 	let added_runner; | ||||
| 	let added_card; | ||||
| 	let added_track; | ||||
| 	let added_station; | ||||
| 	it('creating a new org with just a name should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/organisations', { | ||||
| 			"name": "test123" | ||||
| 		}, axios_config); | ||||
| 		added_org = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res2 = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		added_runner = res2.data; | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a card with the minimum amount of parameters should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', { | ||||
| 			"runner": added_runner.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		added_card = res.data; | ||||
| 	}); | ||||
| 	it('creating a track should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/tracks', { | ||||
| 			"name": "test123", | ||||
| 			"distance": 123 | ||||
| 		}, axios_config); | ||||
| 		added_track = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('correct description and track input for station creation return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/stations', { | ||||
| 			"track": added_track.id, | ||||
| 			"description": "I am but a simple test." | ||||
| 		}, axios_config); | ||||
| 		added_station = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('no input should return 400', async () => { | ||||
| 		const res = await axios.post(base + '/api/scans/trackscans', null, axios_config); | ||||
| 		expect(res.status).toEqual(400); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('no station should return 400', async () => { | ||||
| 		const res = await axios.post(base + '/api/scans/trackscans', { | ||||
| 			"card": added_card.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(400); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('no card should return 400', async () => { | ||||
| 		const res = await axios.post(base + '/api/scans/trackscans', { | ||||
| 			"station": added_station.id, | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(400); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('invalid card input should return 404', async () => { | ||||
| 		const res = await axios.post(base + '/api/scans/trackscans', { | ||||
| 			"card": 999999999999999999999999, | ||||
| 			"station": added_station.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(404); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('invalid station input should return 404', async () => { | ||||
| 		const res = await axios.post(base + '/api/scans/trackscans', { | ||||
| 			"card": added_card.id, | ||||
| 			"station": 999999999999999999999999 | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(404); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('POST /api/scans successfully', () => { | ||||
| 	let added_org; | ||||
| 	let added_runner; | ||||
| 	let added_card; | ||||
| 	let added_track; | ||||
| 	let added_station; | ||||
| 	it('creating a new org with just a name should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/organisations', { | ||||
| 			"name": "test123" | ||||
| 		}, axios_config); | ||||
| 		added_org = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res2 = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		added_runner = res2.data; | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a card with the minimum amount of parameters should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', { | ||||
| 			"runner": added_runner.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		added_card = res.data; | ||||
| 	}); | ||||
| 	it('creating a track should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/tracks', { | ||||
| 			"name": "test123", | ||||
| 			"distance": 123, | ||||
| 			"minimumLapTime": 30 | ||||
| 		}, axios_config); | ||||
| 		added_track = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('correct description and track input for station creation return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/stations', { | ||||
| 			"track": added_track.id, | ||||
| 			"description": "I am but a simple test." | ||||
| 		}, axios_config); | ||||
| 		added_station = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a scan should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/scans/trackscans', { | ||||
| 			"card": added_card.id, | ||||
| 			"station": added_station.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 	}); | ||||
| 	it('creating a invalid scan should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/scans/trackscans', { | ||||
| 			"card": added_card.id, | ||||
| 			"station": added_station.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		expect(res.data.valid).toEqual(false); | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('POST /api/scans successfully via scan station', () => { | ||||
| 	let added_org; | ||||
| 	let added_runner; | ||||
| 	let added_card; | ||||
| 	let added_track; | ||||
| 	let added_station; | ||||
| 	it('creating a new org with just a name should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/organisations', { | ||||
| 			"name": "test123" | ||||
| 		}, axios_config); | ||||
| 		added_org = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res2 = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		added_runner = res2.data; | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a card with the minimum amount of parameters should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', { | ||||
| 			"runner": added_runner.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		added_card = res.data; | ||||
| 	}); | ||||
| 	it('creating a track should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/tracks', { | ||||
| 			"name": "test123", | ||||
| 			"distance": 123, | ||||
| 			"minimumLapTime": 30 | ||||
| 		}, axios_config); | ||||
| 		added_track = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('correct description and track input for station creation return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/stations', { | ||||
| 			"track": added_track.id, | ||||
| 			"description": "I am but a simple test." | ||||
| 		}, axios_config); | ||||
| 		added_station = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a scan should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/scans/trackscans', { | ||||
| 			"card": added_card.id, | ||||
| 			"station": added_station.id | ||||
| 		}, { | ||||
| 			headers: { "authorization": "Bearer " + added_station.key }, | ||||
| 			validateStatus: undefined | ||||
| 		}); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 	}); | ||||
| 	it('creating a invalid scan should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/scans/trackscans', { | ||||
| 			"card": added_card.id, | ||||
| 			"station": added_station.id | ||||
| 		}, { | ||||
| 			headers: { "authorization": "Bearer " + added_station.key }, | ||||
| 			validateStatus: undefined | ||||
| 		}); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		expect(res.data.valid).toEqual(false); | ||||
| 	}); | ||||
| }); | ||||
							
								
								
									
										101
									
								
								src/tests/trackscans/trackscans_delete.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/tests/trackscans/trackscans_delete.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| import axios from 'axios'; | ||||
| import { config } from '../../config'; | ||||
| const base = "http://localhost:" + config.internal_port | ||||
|  | ||||
| let access_token; | ||||
| let axios_config; | ||||
|  | ||||
| beforeAll(async () => { | ||||
| 	const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); | ||||
| 	access_token = res.data["access_token"]; | ||||
| 	axios_config = { | ||||
| 		headers: { "authorization": "Bearer " + access_token }, | ||||
| 		validateStatus: undefined | ||||
| 	}; | ||||
| }); | ||||
|  | ||||
| // --------------- | ||||
|  | ||||
| describe('DELETE scan', () => { | ||||
| 	let added_org; | ||||
| 	let added_runner; | ||||
| 	let added_card; | ||||
| 	let added_track; | ||||
| 	let added_station; | ||||
| 	let added_scan; | ||||
| 	it('creating a new org with just a name should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/organisations', { | ||||
| 			"name": "test123" | ||||
| 		}, axios_config); | ||||
| 		added_org = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res2 = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		added_runner = res2.data; | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a card with the minimum amount of parameters should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', { | ||||
| 			"runner": added_runner.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		added_card = res.data; | ||||
| 	}); | ||||
| 	it('creating a track should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/tracks', { | ||||
| 			"name": "test123", | ||||
| 			"distance": 123, | ||||
| 			"minimumLapTime": 30 | ||||
| 		}, axios_config); | ||||
| 		added_track = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('correct description and track input for station creation return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/stations', { | ||||
| 			"track": added_track.id, | ||||
| 			"description": "I am but a simple test." | ||||
| 		}, axios_config); | ||||
| 		added_station = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a scan should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/scans/trackscans', { | ||||
| 			"card": added_card.id, | ||||
| 			"station": added_station.id | ||||
| 		}, { | ||||
| 			headers: { "authorization": "Bearer " + added_station.key }, | ||||
| 			validateStatus: undefined | ||||
| 		}); | ||||
| 		added_scan = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 	}); | ||||
| 	it('delete scan', async () => { | ||||
| 		const res2 = await axios.delete(base + '/api/scans/' + added_scan.id, axios_config); | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 		expect(res2.data).toEqual(added_scan); | ||||
| 	}); | ||||
| 	it('check if scan really was deleted', async () => { | ||||
| 		const res3 = await axios.get(base + '/api/scans/' + added_scan.id, axios_config); | ||||
| 		expect(res3.status).toEqual(404); | ||||
| 		expect(res3.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('DELETE scan (non-existant)', () => { | ||||
| 	it('delete', async () => { | ||||
| 		const res2 = await axios.delete(base + '/api/scans/0', axios_config); | ||||
| 		expect(res2.status).toEqual(204); | ||||
| 	}); | ||||
| }); | ||||
							
								
								
									
										102
									
								
								src/tests/trackscans/trackscans_get.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/tests/trackscans/trackscans_get.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | ||||
| import axios from 'axios'; | ||||
| import { config } from '../../config'; | ||||
| const base = "http://localhost:" + config.internal_port | ||||
|  | ||||
| let access_token; | ||||
| let axios_config; | ||||
|  | ||||
| beforeAll(async () => { | ||||
| 	const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); | ||||
| 	access_token = res.data["access_token"]; | ||||
| 	axios_config = { | ||||
| 		headers: { "authorization": "Bearer " + access_token }, | ||||
| 		validateStatus: undefined | ||||
| 	}; | ||||
| }); | ||||
|  | ||||
| describe('GET /api/scans sucessfully', () => { | ||||
| 	it('basic get should return 200', async () => { | ||||
| 		const res = await axios.get(base + '/api/scans', axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('GET /api/scans illegally', () => { | ||||
| 	it('get for non-existant track should return 404', async () => { | ||||
| 		const res = await axios.get(base + '/api/scans/-1', axios_config); | ||||
| 		expect(res.status).toEqual(404); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('adding + getting scans', () => { | ||||
| 	let added_org; | ||||
| 	let added_runner; | ||||
| 	let added_card; | ||||
| 	let added_track; | ||||
| 	let added_station; | ||||
| 	let added_scan; | ||||
| 	it('creating a new org with just a name should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/organisations', { | ||||
| 			"name": "test123" | ||||
| 		}, axios_config); | ||||
| 		added_org = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res2 = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		added_runner = res2.data; | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a card with the minimum amount of parameters should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', { | ||||
| 			"runner": added_runner.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		added_card = res.data; | ||||
| 	}); | ||||
| 	it('creating a track should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/tracks', { | ||||
| 			"name": "test123", | ||||
| 			"distance": 123, | ||||
| 			"minimumLapTime": 30 | ||||
| 		}, axios_config); | ||||
| 		added_track = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('correct description and track input for station creation return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/stations', { | ||||
| 			"track": added_track.id, | ||||
| 			"description": "I am but a simple test." | ||||
| 		}, axios_config); | ||||
| 		added_station = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a scan should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/scans/trackscans', { | ||||
| 			"card": added_card.id, | ||||
| 			"station": added_station.id | ||||
| 		}, { | ||||
| 			headers: { "authorization": "Bearer " + added_station.key }, | ||||
| 			validateStatus: undefined | ||||
| 		}); | ||||
| 		added_scan = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 	}); | ||||
| 	it('check if scans was added (no parameter validation)', async () => { | ||||
| 		const res = await axios.get(base + '/api/scans/' + added_scan.id, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 	}); | ||||
| }); | ||||
							
								
								
									
										239
									
								
								src/tests/trackscans/trackscans_update.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								src/tests/trackscans/trackscans_update.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,239 @@ | ||||
| import axios from 'axios'; | ||||
| import { config } from '../../config'; | ||||
| const base = "http://localhost:" + config.internal_port | ||||
|  | ||||
| let access_token; | ||||
| let axios_config; | ||||
|  | ||||
| beforeAll(async () => { | ||||
| 	const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); | ||||
| 	access_token = res.data["access_token"]; | ||||
| 	axios_config = { | ||||
| 		headers: { "authorization": "Bearer " + access_token }, | ||||
| 		validateStatus: undefined | ||||
| 	}; | ||||
| }); | ||||
|  | ||||
| describe('adding + updating illegally', () => { | ||||
| 	let added_org; | ||||
| 	let added_runner; | ||||
| 	let added_card; | ||||
| 	let added_track; | ||||
| 	let added_station; | ||||
| 	let added_scan; | ||||
| 	it('creating a new org with just a name should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/organisations', { | ||||
| 			"name": "test123" | ||||
| 		}, axios_config); | ||||
| 		added_org = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res2 = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		added_runner = res2.data; | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a card with the minimum amount of parameters should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', { | ||||
| 			"runner": added_runner.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		added_card = res.data; | ||||
| 	}); | ||||
| 	it('creating a track should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/tracks', { | ||||
| 			"name": "test123", | ||||
| 			"distance": 123, | ||||
| 			"minimumLapTime": 30 | ||||
| 		}, axios_config); | ||||
| 		added_track = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('correct description and track input for station creation return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/stations', { | ||||
| 			"track": added_track.id, | ||||
| 			"description": "I am but a simple test." | ||||
| 		}, axios_config); | ||||
| 		added_station = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a scan should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/scans/trackscans', { | ||||
| 			"card": added_card.id, | ||||
| 			"station": added_station.id | ||||
| 		}, { | ||||
| 			headers: { "authorization": "Bearer " + added_station.key }, | ||||
| 			validateStatus: undefined | ||||
| 		}); | ||||
| 		added_scan = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 	}); | ||||
| 	it('updating empty should return 400', async () => { | ||||
| 		const res2 = await axios.put(base + '/api/scans/trackscans/' + added_scan.id, null, axios_config); | ||||
| 		expect(res2.status).toEqual(400); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating with wrong id should return 406', async () => { | ||||
| 		const res2 = await axios.put(base + '/api/scans/trackscans/' + added_scan.id, { | ||||
| 			"id": added_scan.id + 1, | ||||
| 			"station": added_station.id, | ||||
| 			"runner": added_runner.id | ||||
| 		}, axios_config); | ||||
| 		expect(res2.status).toEqual(406); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating with invalid station should return 404', async () => { | ||||
| 		const res2 = await axios.put(base + '/api/scans/trackscans/' + added_scan.id, { | ||||
| 			"id": added_scan.id, | ||||
| 			"station": 9999999999999999, | ||||
| 			"runner": added_runner.id | ||||
| 		}, axios_config); | ||||
| 		expect(res2.status).toEqual(404); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating with invalid runner should return 404', async () => { | ||||
| 		const res2 = await axios.put(base + '/api/scans/trackscans/' + added_scan.id, { | ||||
| 			"id": added_scan.id, | ||||
| 			"station": added_station.id, | ||||
| 			"runner": 9999999999999999999 | ||||
| 		}, axios_config); | ||||
| 		expect(res2.status).toEqual(404); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| //--------------- | ||||
| describe('adding + updating successfilly', () => { | ||||
| 	let added_org; | ||||
| 	let added_runner; | ||||
| 	let added_runner2; | ||||
| 	let added_card; | ||||
| 	let added_track; | ||||
| 	let added_track2; | ||||
| 	let added_station; | ||||
| 	let added_station2; | ||||
| 	let added_scan; | ||||
| 	it('creating a new org with just a name should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/organisations', { | ||||
| 			"name": "test123" | ||||
| 		}, axios_config); | ||||
| 		added_org = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res2 = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		added_runner = res2.data; | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res2 = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		added_runner2 = res2.data; | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a card with the minimum amount of parameters should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/cards', { | ||||
| 			"runner": added_runner.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		added_card = res.data; | ||||
| 	}); | ||||
| 	it('creating a track should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/tracks', { | ||||
| 			"name": "test123", | ||||
| 			"distance": 123, | ||||
| 			"minimumLapTime": 30 | ||||
| 		}, axios_config); | ||||
| 		added_track = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a track should return 200', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/tracks', { | ||||
| 			"name": "test123", | ||||
| 			"distance": 123, | ||||
| 			"minimumLapTime": 30 | ||||
| 		}, axios_config); | ||||
| 		added_track2 = res1.data | ||||
| 		expect(res1.status).toEqual(200); | ||||
| 		expect(res1.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('correct description and track input for station creation return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/stations', { | ||||
| 			"track": added_track.id, | ||||
| 			"description": "I am but a simple test." | ||||
| 		}, axios_config); | ||||
| 		added_station = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('correct description and track input for station creation return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/stations', { | ||||
| 			"track": added_track.id, | ||||
| 			"description": "I am but a simple test." | ||||
| 		}, axios_config); | ||||
| 		added_station2 = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a scan should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/scans/trackscans', { | ||||
| 			"card": added_card.id, | ||||
| 			"station": added_station.id | ||||
| 		}, { | ||||
| 			headers: { "authorization": "Bearer " + added_station.key }, | ||||
| 			validateStatus: undefined | ||||
| 		}); | ||||
| 		added_scan = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 	}); | ||||
| 	it('updating with new runner should return 200', async () => { | ||||
| 		const res2 = await axios.put(base + '/api/scans/trackscans/' + added_scan.id, { | ||||
| 			"id": added_scan.id, | ||||
| 			"station": added_station.id, | ||||
| 			"runner": added_runner2.id | ||||
| 		}, axios_config); | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating with new station should return 200', async () => { | ||||
| 		const res2 = await axios.put(base + '/api/scans/trackscans/' + added_scan.id, { | ||||
| 			"id": added_scan.id, | ||||
| 			"station": added_station2.id, | ||||
| 			"runner": added_runner.id | ||||
| 		}, axios_config); | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating with valid=false should return 200', async () => { | ||||
| 		const res2 = await axios.put(base + '/api/scans/trackscans/' + added_scan.id, { | ||||
| 			"id": added_scan.id, | ||||
| 			"station": added_station2.id, | ||||
| 			"runner": added_runner.id, | ||||
| 			"valid": false | ||||
| 		}, axios_config); | ||||
| 		expect(res2.status).toEqual(200); | ||||
| 		expect(res2.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
		Reference in New Issue
	
	Block a user