diff --git a/src/controllers/RunnerSelfServiceController.ts b/src/controllers/RunnerSelfServiceController.ts new file mode 100644 index 0000000..b2d2539 --- /dev/null +++ b/src/controllers/RunnerSelfServiceController.ts @@ -0,0 +1,49 @@ +import * as jwt from "jsonwebtoken"; +import { Get, JsonController, OnUndefined, Param } from 'routing-controllers'; +import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; +import { getConnectionManager, Repository } from 'typeorm'; +import { config } from '../config'; +import { InvalidCredentialsError } from '../errors/AuthError'; +import { RunnerNotFoundError } from '../errors/RunnerErrors'; +import { Runner } from '../models/entities/Runner'; +import { ResponseSelfServiceRunner } from '../models/responses/ResponseSelfServiceRunner'; + + +@JsonController('/runners') +export class RunnerSelfServiceController { + private runnerRepository: Repository; + + /** + * Gets the repository of this controller's model/entity. + */ + constructor() { + this.runnerRepository = getConnectionManager().get().getRepository(Runner); + } + + @Get('/me/:jwt') + @ResponseSchema(ResponseSelfServiceRunner) + @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) + @OnUndefined(RunnerNotFoundError) + @OpenAPI({ description: 'Lists all information about yourself.
Please provide your runner jwt(that code we gave you during registration) for auth.
If you lost your jwt/personalized link please contact support.' }) + async get(@Param('jwt') token: string) { + return (new ResponseSelfServiceRunner(await this.getRunner(token))); + } + + /** + * Get's a runner by a provided jwt token. + * @param token The runner jwt provided by the runner to identitfy themselves. + */ + private async getRunner(token: string): Promise { + let jwtPayload = undefined + try { + jwtPayload = jwt.verify(token, config.jwt_secret); + } catch (error) { + throw new InvalidCredentialsError(); + } + + const runner = await this.runnerRepository.findOne({ id: jwtPayload["id"] }, { relations: ['scans', 'group', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] }); + if (!runner) { throw new RunnerNotFoundError() } + return runner; + } + +} \ No newline at end of file diff --git a/src/models/entities/Runner.ts b/src/models/entities/Runner.ts index bc6bc5a..266abb9 100644 --- a/src/models/entities/Runner.ts +++ b/src/models/entities/Runner.ts @@ -65,7 +65,7 @@ export class Runner extends Participant { */ @IsInt() public get distanceDonationAmount(): number { - return this.distanceDonations.reduce((sum, current) => sum + current.amountPerDistance, 0) * this.distance; + return this.distanceDonations.reduce((sum, current) => sum + current.amount, 0); } /** diff --git a/src/models/responses/ResponseSelfServiceDonation.ts b/src/models/responses/ResponseSelfServiceDonation.ts new file mode 100644 index 0000000..4f10f3c --- /dev/null +++ b/src/models/responses/ResponseSelfServiceDonation.ts @@ -0,0 +1,36 @@ +import { IsInt, IsNotEmpty, IsPositive } from 'class-validator'; +import { DistanceDonation } from '../entities/DistanceDonation'; + +/** + * Defines the runner selfservice donation response. + * Why? B/C runner's are not allowed to view all information available to admin users. +*/ +export class ResponseSelfServiceDonation { + /** + * The donation's donor. + */ + @IsNotEmpty() + donor: string; + + /** + * The donation's amount in the smalles unit of your currency (default: euro cent). + */ + @IsInt() + amount: number; + + /** + * The donation's amount donated per distance. + * The amount the donor set to be donated per kilometer that the runner ran. + */ + @IsInt() + @IsPositive() + amountPerDistance: number; + + public constructor(donation: DistanceDonation) { + if (!donation.donor.middlename) { this.donor = donation.donor.firstname + " " + donation.donor.lastname; } + else { this.donor = donation.donor.firstname + " " + donation.donor.middlename + " " + donation.donor.lastname; } + + this.amountPerDistance = donation.amountPerDistance; + this.amount = donation.amount; + } +} \ No newline at end of file diff --git a/src/models/responses/ResponseSelfServiceRunner.ts b/src/models/responses/ResponseSelfServiceRunner.ts new file mode 100644 index 0000000..6727c76 --- /dev/null +++ b/src/models/responses/ResponseSelfServiceRunner.ts @@ -0,0 +1,75 @@ +import { IsInt, IsString } from "class-validator"; +import { DistanceDonation } from '../entities/DistanceDonation'; +import { Runner } from '../entities/Runner'; +import { RunnerGroup } from '../entities/RunnerGroup'; +import { RunnerTeam } from '../entities/RunnerTeam'; +import { ResponseParticipant } from './ResponseParticipant'; +import { ResponseSelfServiceDonation } from './ResponseSelfServiceDonation'; + +/** + * Defines the runner selfservice response. + * Why? B/C runner's are not allowed to view all information available to admin users. +*/ +export class ResponseSelfServiceRunner extends ResponseParticipant { + + /** + * The runner's currently ran distance in meters. + */ + @IsInt() + distance: number; + + /** + * The runner's currently collected donations. + */ + @IsInt() + donationAmount: number; + + /** + * The runner's group as a string (mix of org and team). + */ + @IsString() + group: string; + + /** + * The runner's associated donations. + */ + @IsString() + donations: ResponseSelfServiceDonation[] + + /** + * Creates a ResponseRunner object from a runner. + * @param runner The user the response shall be build for. + */ + public constructor(runner: Runner) { + super(runner); + this.distance = runner.distance; + this.donationAmount = runner.distanceDonationAmount; + this.group = this.getTeamString(runner.group); + this.donations = this.getDonations(runner.distanceDonations); + } + + /** + * Parses a runner's group into a string. + * If the runner's group is a team: `org name/team name` + * If the runner's group is an org: `org name` + * @param group The group that shall get parsed to a string. + */ + private getTeamString(group: RunnerGroup): string { + if (group instanceof RunnerTeam) { + return group.parentGroup.name + "/" + group.name; + } + return group.name; + } + + /** + * Converts all of the runner's donations to ResponseSelfServiceDonations. + * @param donations The donations that shall be converted to ResponseSelfServiceDonations. + */ + private getDonations(donations: DistanceDonation[]): ResponseSelfServiceDonation[] { + let responseDonations = new Array(); + for (let donation of donations) { + responseDonations.push(new ResponseSelfServiceDonation(donation)); + } + return responseDonations; + } +}