From 2274b476d6caa1de91bb13b6944f8dc233cf446e Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 20 Jan 2021 19:05:59 +0100 Subject: [PATCH 1/8] Added barebones controller for the runner info selfservice ref #111 --- .../RunnerSelfServiceController.ts | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/controllers/RunnerSelfServiceController.ts diff --git a/src/controllers/RunnerSelfServiceController.ts b/src/controllers/RunnerSelfServiceController.ts new file mode 100644 index 0000000..290ccfc --- /dev/null +++ b/src/controllers/RunnerSelfServiceController.ts @@ -0,0 +1,34 @@ +import { Get, JsonController, OnUndefined, Param } from 'routing-controllers'; +import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; +import { getConnectionManager, Repository } from 'typeorm'; +import { RunnerNotFoundError } from '../errors/RunnerErrors'; +import { Runner } from '../models/entities/Runner'; +import { ResponseUser } from '../models/responses/ResponseUser'; + + +@JsonController('/runners') +@OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) +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(ResponseUser) + @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) + @OnUndefined(RunnerNotFoundError) + @OpenAPI({ description: 'Lists all information about yourself.
Please provide your runner jwt for auth.' }) + async get(@Param('jwt') jwt: string) { + //TODO: + } + + public async getRunner(jwt: string): Promise { + return null; + } + +} \ No newline at end of file From 80797698818f456c7746523d5a4f66267fdab10d Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 20 Jan 2021 19:20:08 +0100 Subject: [PATCH 2/8] Implemented a method for getting the runner object from a jwt ref #110 --- .../RunnerSelfServiceController.ts | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/controllers/RunnerSelfServiceController.ts b/src/controllers/RunnerSelfServiceController.ts index 290ccfc..fa4c10e 100644 --- a/src/controllers/RunnerSelfServiceController.ts +++ b/src/controllers/RunnerSelfServiceController.ts @@ -1,13 +1,15 @@ +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 { ResponseUser } from '../models/responses/ResponseUser'; @JsonController('/runners') -@OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) export class RunnerSelfServiceController { private runnerRepository: Repository; @@ -23,12 +25,27 @@ export class RunnerSelfServiceController { @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) @OnUndefined(RunnerNotFoundError) @OpenAPI({ description: 'Lists all information about yourself.
Please provide your runner jwt for auth.' }) - async get(@Param('jwt') jwt: string) { - //TODO: + async get(@Param('jwt') token: string) { + //TODO } - public async getRunner(jwt: string): Promise { - return null; + /** + * 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(); + } + + console.log(jwtPayload); + + const runner = await this.runnerRepository.findOne({ id: jwtPayload["id"] }); + if (!runner) { throw new RunnerNotFoundError() } + return runner; } } \ No newline at end of file From b89f7ac1b4ddd6e53e6e2e8330c1fa2170b48591 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 20 Jan 2021 19:43:20 +0100 Subject: [PATCH 3/8] Created a donation respoinse class for the runner selfservice ref #111 --- .../responses/ResponseSelfServiceDonation.ts | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/models/responses/ResponseSelfServiceDonation.ts 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 From 88a7089289e35be4468cb952b311fcb15c54c5a1 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 20 Jan 2021 19:43:53 +0100 Subject: [PATCH 4/8] Created a donation runner response class for the runner selfservice ref #111 --- .../responses/ResponseSelfServiceRunner.ts | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/models/responses/ResponseSelfServiceRunner.ts diff --git a/src/models/responses/ResponseSelfServiceRunner.ts b/src/models/responses/ResponseSelfServiceRunner.ts new file mode 100644 index 0000000..ea7e2bb --- /dev/null +++ b/src/models/responses/ResponseSelfServiceRunner.ts @@ -0,0 +1,83 @@ +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; + } +} From c5f7cb2c68dbee0ab1e0361754f4d4b876666c82 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 20 Jan 2021 19:44:24 +0100 Subject: [PATCH 5/8] Beautified import ref #111 --- src/models/responses/ResponseSelfServiceRunner.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/models/responses/ResponseSelfServiceRunner.ts b/src/models/responses/ResponseSelfServiceRunner.ts index ea7e2bb..6727c76 100644 --- a/src/models/responses/ResponseSelfServiceRunner.ts +++ b/src/models/responses/ResponseSelfServiceRunner.ts @@ -1,12 +1,4 @@ -import { - IsInt, - - - - - - IsString -} from "class-validator"; +import { IsInt, IsString } from "class-validator"; import { DistanceDonation } from '../entities/DistanceDonation'; import { Runner } from '../entities/Runner'; import { RunnerGroup } from '../entities/RunnerGroup'; From 4ee807973e1995681ec549f7c482bc5514a6ec55 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 20 Jan 2021 20:02:30 +0100 Subject: [PATCH 6/8] Fixed wrong amount calculation ref #111 --- src/models/entities/Runner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); } /** From da1fe34249a741115c1aeedcade16c5c852e896b Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 20 Jan 2021 20:05:07 +0100 Subject: [PATCH 7/8] Implemented the get part of the runner selfservice (no jwts are availdable yet (tm) ref #111 --- src/controllers/RunnerSelfServiceController.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/controllers/RunnerSelfServiceController.ts b/src/controllers/RunnerSelfServiceController.ts index fa4c10e..23ad450 100644 --- a/src/controllers/RunnerSelfServiceController.ts +++ b/src/controllers/RunnerSelfServiceController.ts @@ -6,7 +6,7 @@ import { config } from '../config'; import { InvalidCredentialsError } from '../errors/AuthError'; import { RunnerNotFoundError } from '../errors/RunnerErrors'; import { Runner } from '../models/entities/Runner'; -import { ResponseUser } from '../models/responses/ResponseUser'; +import { ResponseSelfServiceRunner } from '../models/responses/ResponseSelfServiceRunner'; @JsonController('/runners') @@ -21,12 +21,12 @@ export class RunnerSelfServiceController { } @Get('/me/:jwt') - @ResponseSchema(ResponseUser) + @ResponseSchema(ResponseSelfServiceRunner) @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) @OnUndefined(RunnerNotFoundError) @OpenAPI({ description: 'Lists all information about yourself.
Please provide your runner jwt for auth.' }) async get(@Param('jwt') token: string) { - //TODO + return (new ResponseSelfServiceRunner(await this.getRunner(token))); } /** @@ -41,9 +41,7 @@ export class RunnerSelfServiceController { throw new InvalidCredentialsError(); } - console.log(jwtPayload); - - const runner = await this.runnerRepository.findOne({ id: jwtPayload["id"] }); + 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; } From 191569792c9a5cee93718555bba4e7679e4391af Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 20 Jan 2021 20:07:16 +0100 Subject: [PATCH 8/8] Updated the openapi description ref #111 --- src/controllers/RunnerSelfServiceController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/RunnerSelfServiceController.ts b/src/controllers/RunnerSelfServiceController.ts index 23ad450..b2d2539 100644 --- a/src/controllers/RunnerSelfServiceController.ts +++ b/src/controllers/RunnerSelfServiceController.ts @@ -24,7 +24,7 @@ export class RunnerSelfServiceController { @ResponseSchema(ResponseSelfServiceRunner) @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) @OnUndefined(RunnerNotFoundError) - @OpenAPI({ description: 'Lists all information about yourself.
Please provide your runner jwt for auth.' }) + @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))); }