diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index a11d2c6..4e74330 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -92,7 +92,7 @@ export class RunnerController { @ResponseSchema(ResponseRunner) @ResponseSchema(ResponseEmpty, { statusCode: 204 }) @OnUndefined(204) - @OpenAPI({ description: 'Delete the runner whose id you provided.
If no runner with this id exists it will just return 204(no content).' }) + @OpenAPI({ description: 'Delete the runner whose id you provided.
This will also delete all scans and cards associated with the runner.
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; } diff --git a/src/controllers/ScanController.ts b/src/controllers/ScanController.ts index 09833dc..46c7ba1 100644 --- a/src/controllers/ScanController.ts +++ b/src/controllers/ScanController.ts @@ -1,12 +1,14 @@ import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam, UseBefore } from 'routing-controllers'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; -import { getConnection, getConnectionManager, Repository } from 'typeorm'; +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 { UpdateTrackScan } from '../models/actions/UpdateTrackScan'; import { Scan } from '../models/entities/Scan'; import { TrackScan } from '../models/entities/TrackScan'; import { ResponseEmpty } from '../models/responses/ResponseEmpty'; @@ -17,12 +19,14 @@ import { ResponseTrackScan } from '../models/responses/ResponseTrackScan'; @OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) export class ScanController { private scanRepository: Repository; + private trackScanRepository: Repository; /** * Gets the repository of this controller's model/entity. */ constructor() { this.scanRepository = getConnectionManager().get().getRepository(Scan); + this.trackScanRepository = getConnectionManager().get().getRepository(TrackScan); } @Get() @@ -56,7 +60,7 @@ export class ScanController { @UseBefore(ScanAuth) @ResponseSchema(ResponseScan) @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) - @OpenAPI({ description: 'Create a new scan.
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).
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(); scan = await this.scanRepository.save(scan); @@ -65,12 +69,12 @@ export class ScanController { @Post("/trackscans") @UseBefore(ScanAuth) - @ResponseSchema(ResponseScan) + @ResponseSchema(ResponseTrackScan) @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) - @OpenAPI({ description: 'Create a new track scan.
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).
Please remember that to provide the scan\'s card\'s station\'s id.', security: [{ "ScanApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) async postTrackScans(@Body({ validate: true }) createScan: CreateTrackScan) { let scan = await createScan.toScan(); - scan = await getConnection().getRepository(TrackScan).save(scan); + 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(); } @@ -80,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.
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.
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 }); @@ -96,6 +100,29 @@ export class ScanController { 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.
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.updateScan(oldScan)); + return (await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] })).toResponse(); + } + @Delete('/:id') @Authorized("SCAN:DELETE") @ResponseSchema(ResponseScan) diff --git a/src/models/actions/UpdateTrackScan.ts b/src/models/actions/UpdateTrackScan.ts new file mode 100644 index 0000000..9d0e754 --- /dev/null +++ b/src/models/actions/UpdateTrackScan.ts @@ -0,0 +1,79 @@ +import { IsBoolean, IsInt, IsOptional, IsPositive } 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() + @IsPositive() + 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 updateScan(scan: TrackScan): Promise { + scan.valid = this.valid; + 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 { + 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 { + const station = await getConnection().getRepository(ScanStation).findOne({ id: this.station }); + if (!station) { + throw new ScanStationNotFoundError(); + } + return station; + } +} \ No newline at end of file