From 330cbd5f57a9f16cbf277ea57fadefcb6d24e0fa Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 3 Dec 2020 20:49:55 +0100 Subject: [PATCH 01/16] Added comments and decorators for existing create models ref #13 --- src/models/creation/CreateRunner.ts | 79 ++++++++++++++----- .../creation/CreateRunnerOrganisation.ts | 9 ++- 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/src/models/creation/CreateRunner.ts b/src/models/creation/CreateRunner.ts index 178c5fb..b68a90b 100644 --- a/src/models/creation/CreateRunner.ts +++ b/src/models/creation/CreateRunner.ts @@ -1,31 +1,70 @@ -import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsPositive, IsString } from 'class-validator'; +import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; +import { RunnerGroupNeededError, RunnerGroupNotFoundError, RunnerOnlyOneGroupAllowedError } from '../../errors/RunnerErrors'; import { Runner } from '../entities/Runner'; -import { getConnectionManager, Repository } from 'typeorm'; -import { group } from 'console'; -import { RunnerOnlyOneGroupAllowedError, RunnerGroupNeededError, RunnerGroupNotFoundError } from '../../errors/RunnerErrors'; import { RunnerOrganisation } from '../entities/RunnerOrganisation'; import { RunnerTeam } from '../entities/RunnerTeam'; -import { RunnerGroup } from '../entities/RunnerGroup'; -import { Address } from 'cluster'; export class CreateRunner { + /** + * The new runner's first name. + */ @IsString() + @IsNotEmpty() firstname: string; - @IsString() - middlename?: string; - @IsString() - lastname: string; - @IsString() - phone?: string; - @IsString() - email?: string; - @IsInt() - @IsOptional() - teamId?: number - @IsInt() - @IsOptional() - orgId?: number + /** + * The new runner's middle name. + * Optional. + */ + @IsString() + @IsNotEmpty() + middlename?: string; + + /** + * The new runner's last name. + */ + @IsString() + @IsNotEmpty() + lastname: string; + + /** + * The new runner's phone number. + * Optional. + */ + @IsString() + @IsOptional() + @IsPhoneNumber("ZZ") + phone?: string; + + /** + * The new runner's e-mail address. + * Optional. + */ + @IsString() + @IsOptional() + @IsEmail() + email?: string; + + /** + * The new runner's team's id. + * Either provide this or his organisation's id. + */ + @IsInt() + @IsOptional() + teamId?: number; + + /** + * The new runner's organisation's id. + * Either provide this or his teams's id. + */ + @IsInt() + @IsOptional() + orgId?: number; + + /** + * Creates a Runner entity from this. + */ public async toRunner(): Promise { let newRunner: Runner = new Runner(); diff --git a/src/models/creation/CreateRunnerOrganisation.ts b/src/models/creation/CreateRunnerOrganisation.ts index f054e5a..ff4ec65 100644 --- a/src/models/creation/CreateRunnerOrganisation.ts +++ b/src/models/creation/CreateRunnerOrganisation.ts @@ -1,10 +1,17 @@ -import { IsString } from 'class-validator'; +import { IsNotEmpty, IsString } from 'class-validator'; import { RunnerOrganisation } from '../entities/RunnerOrganisation'; export class CreateRunnerOrganisation { + /** + * The Organisation's name. + */ @IsString() + @IsNotEmpty() name: string; + /** + * Creates a RunnerOrganisation entity from this. + */ public async toRunnerOrganisation(): Promise { let newRunnerOrganisation: RunnerOrganisation = new RunnerOrganisation(); From 0c6528bdc5eb314399bc722087e3b907f0f59ff8 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 17:06:37 +0100 Subject: [PATCH 02/16] Impementing more methods for the runner orgs --- .../RunnerOrganisationController.ts | 23 +++++++++---- src/errors/RunnerOrganisationErrors.ts | 34 ++++++++++++++++--- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index 8033c9b..0bae97f 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -1,11 +1,10 @@ -import { JsonController, Param, Body, Get, Post, Put, Delete, OnUndefined } from 'routing-controllers'; +import { 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 { EntityFromBody } from 'typeorm-routing-controllers-extensions'; -import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; -import { RunnerOrganisation } from '../models/entities/RunnerOrganisation'; -import { RunnerOrganisationIdsNotMatchingError, RunnerOrganisationNotFoundError } from '../errors/RunnerOrganisationErrors'; +import { RunnerOrganisationHasRunnersError, RunnerOrganisationHasTeamsError, RunnerOrganisationIdsNotMatchingError, RunnerOrganisationNotFoundError } from '../errors/RunnerOrganisationErrors'; import { CreateRunnerOrganisation } from '../models/creation/CreateRunnerOrganisation'; -import { RunnerGroup } from '../models/entities/RunnerGroup'; +import { RunnerOrganisation } from '../models/entities/RunnerOrganisation'; @JsonController('/organisations') @@ -74,13 +73,25 @@ export class RunnerOrganisationController { @ResponseSchema(RunnerOrganisation) @ResponseSchema(RunnerOrganisationNotFoundError, { statusCode: 404 }) @OpenAPI({ description: 'Delete a specified runnerOrganisation (if it exists).' }) - async remove(@Param('id') id: number) { + async remove(@Param('id') id: number, @QueryParam("force") force) { let runnerOrganisation = await this.runnerOrganisationRepository.findOne({ id: id }); if (!runnerOrganisation) { throw new RunnerOrganisationNotFoundError(); } + if (!force) { + if (runnerOrganisation.runners.length != 0) { + throw new RunnerOrganisationHasRunnersError(); + } + + if (runnerOrganisation.teams.length != 0) { + throw new RunnerOrganisationHasTeamsError(); + } + } + + //TODO: Delete runner and teams + await this.runnerOrganisationRepository.delete(runnerOrganisation); return runnerOrganisation; } diff --git a/src/errors/RunnerOrganisationErrors.ts b/src/errors/RunnerOrganisationErrors.ts index f846ffa..1dfb68e 100644 --- a/src/errors/RunnerOrganisationErrors.ts +++ b/src/errors/RunnerOrganisationErrors.ts @@ -1,8 +1,8 @@ -import { JsonController, Param, Body, Get, Post, Put, Delete, NotFoundError, OnUndefined, NotAcceptableError } from 'routing-controllers'; -import { IsInt, IsNotEmpty, IsPositive, IsString } from 'class-validator'; +import { IsString } from 'class-validator'; +import { NotAcceptableError, NotFoundError } from 'routing-controllers'; /** - * Error to throw when a runner couldn't be found. + * Error to throw when a runner organisation couldn't be found. * Implemented this ways to work with the json-schema conversion for openapi. */ export class RunnerOrganisationNotFoundError extends NotFoundError { @@ -14,9 +14,9 @@ export class RunnerOrganisationNotFoundError extends NotFoundError { } /** - * Error to throw when two runners' ids don't match. + * Error to throw when two runner organisations' ids don't match. * Usually occurs when a user tries to change a runner's id. - * Implemented this ways to work with the json-schema conversion for openapi. + * Implemented this way to work with the json-schema conversion for openapi. */ export class RunnerOrganisationIdsNotMatchingError extends NotAcceptableError { @IsString() @@ -24,4 +24,28 @@ export class RunnerOrganisationIdsNotMatchingError extends NotAcceptableError { @IsString() message = "The id's don't match!! \n And if you wanted to change a runner's id: This isn't allowed" +} + +/** + * Error to throw when a organisation still has runners associated. + * Implemented this waysto work with the json-schema conversion for openapi. + */ +export class RunnerOrganisationHasRunnersError extends NotAcceptableError { + @IsString() + name = "RunnerOrganisationHasRunnersError" + + @IsString() + message = "This organisation still has runners associated with it. \n If you want to delete this organisation with all it's runners and teams ass `?force` to your query." +} + +/** + * Error to throw when a organisation still has runners associated. + * Implemented this waysto work with the json-schema conversion for openapi. + */ +export class RunnerOrganisationHasTeamsError extends NotAcceptableError { + @IsString() + name = "RunnerOrganisationHasTeamsError" + + @IsString() + message = "This organisation still has teams associated with it. \n If you want to delete this organisation with all it's runners and teams ass `?force` to your query." } \ No newline at end of file From ca917b057767c341f7d0e2f2d62603df9d291a3b Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 17:50:10 +0100 Subject: [PATCH 03/16] Added basics for the runner team controller ref #13 --- src/controllers/RunnerTeamController.ts | 102 ++++++++++++++++++++++++ src/errors/RunnerTeamErrors.ts | 39 +++++++++ src/models/creation/CreateRunnerTeam.ts | 36 +++++++++ src/models/entities/RunnerTeam.ts | 11 ++- 4 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 src/controllers/RunnerTeamController.ts create mode 100644 src/errors/RunnerTeamErrors.ts create mode 100644 src/models/creation/CreateRunnerTeam.ts diff --git a/src/controllers/RunnerTeamController.ts b/src/controllers/RunnerTeamController.ts new file mode 100644 index 0000000..466a9ff --- /dev/null +++ b/src/controllers/RunnerTeamController.ts @@ -0,0 +1,102 @@ +import { 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 { EntityFromBody } from 'typeorm-routing-controllers-extensions'; +import { RunnerTeamHasRunnersError, RunnerTeamIdsNotMatchingError, RunnerTeamNotFoundError } from '../errors/RunnerTeamErrors'; +import { CreateRunnerTeam } from '../models/creation/CreateRunnerTeam'; +import { Runner } from '../models/entities/Runner'; +import { RunnerTeam } from '../models/entities/RunnerTeam'; +import { RunnerController } from './RunnerController'; + + +@JsonController('/teams') +//@Authorized('RUNNERS:read') +export class RunnerTeamController { + private runnerTeamRepository: Repository; + + /** + * Gets the repository of this controller's model/entity. + */ + constructor() { + this.runnerTeamRepository = getConnectionManager().get().getRepository(RunnerTeam); + } + + @Get() + @ResponseSchema(RunnerTeam, { isArray: true }) + @OpenAPI({ description: 'Lists all runnerTeams.' }) + getAll() { + return this.runnerTeamRepository.find(); + } + + @Get('/:id') + @ResponseSchema(RunnerTeam) + @ResponseSchema(RunnerTeamNotFoundError, { statusCode: 404 }) + @OnUndefined(RunnerTeamNotFoundError) + @OpenAPI({ description: 'Returns a runnerTeam of a specified id (if it exists)' }) + getOne(@Param('id') id: number) { + return this.runnerTeamRepository.findOne({ id: id }); + } + + @Post() + @ResponseSchema(RunnerTeam) + @OpenAPI({ description: 'Create a new runnerTeam object (id will be generated automagicly).' }) + async post(@Body({ validate: true }) createRunnerTeam: CreateRunnerTeam) { + let runnerTeam; + try { + runnerTeam = await createRunnerTeam.toRunnerTeam(); + } catch (error) { + return error; + } + + return this.runnerTeamRepository.save(runnerTeam); + } + + @Put('/:id') + @ResponseSchema(RunnerTeam) + @ResponseSchema(RunnerTeamNotFoundError, { statusCode: 404 }) + @ResponseSchema(RunnerTeamIdsNotMatchingError, { statusCode: 406 }) + @OpenAPI({ description: "Update a runnerTeam object (id can't be changed)." }) + async put(@Param('id') id: number, @EntityFromBody() runnerTeam: RunnerTeam) { + let oldRunnerTeam = await this.runnerTeamRepository.findOne({ id: id }); + + if (!oldRunnerTeam) { + throw new RunnerTeamNotFoundError(); + } + + if (oldRunnerTeam.id != runnerTeam.id) { + throw new RunnerTeamIdsNotMatchingError(); + } + + await this.runnerTeamRepository.update(oldRunnerTeam, runnerTeam); + return runnerTeam; + } + + @Delete('/:id') + @ResponseSchema(RunnerTeam) + @ResponseSchema(RunnerTeamNotFoundError, { statusCode: 404 }) + @ResponseSchema(RunnerTeamHasRunnersError, { statusCode: 406 }) + @OpenAPI({ description: 'Delete a specified runnerTeam (if it exists).' }) + async remove(@Param('id') id: number, @QueryParam("force") force) { + let runnerTeam = await this.runnerTeamRepository.findOne({ id: id }); + + if (!runnerTeam) { + throw new RunnerTeamNotFoundError(); + } + + let runners: Runner[] = await runnerTeam.getRunners() + + if (!force) { + if (runners.length != 0) { + throw new RunnerTeamHasRunnersError(); + } + } + + const runnerController = new RunnerController() + runners.forEach(runner => { + runnerController.remove(runner.id) + }); + + await this.runnerTeamRepository.delete(runnerTeam); + return runnerTeam; + } +} diff --git a/src/errors/RunnerTeamErrors.ts b/src/errors/RunnerTeamErrors.ts new file mode 100644 index 0000000..a482c26 --- /dev/null +++ b/src/errors/RunnerTeamErrors.ts @@ -0,0 +1,39 @@ +import { IsString } from 'class-validator'; +import { NotAcceptableError, NotFoundError } from 'routing-controllers'; + +/** + * Error to throw when a runner team couldn't be found. + * Implemented this ways to work with the json-schema conversion for openapi. + */ +export class RunnerTeamNotFoundError extends NotFoundError { + @IsString() + name = "RunnerTeamNotFoundError" + + @IsString() + message = "RunnerTeam not found!" +} + +/** + * Error to throw when two runner teams' ids don't match. + * Usually occurs when a user tries to change a runner's id. + * Implemented this way to work with the json-schema conversion for openapi. + */ +export class RunnerTeamIdsNotMatchingError extends NotAcceptableError { + @IsString() + name = "RunnerTeamIdsNotMatchingError" + + @IsString() + message = "The id's don't match!! \n And if you wanted to change a runner's id: This isn't allowed" +} + +/** + * Error to throw when a team still has runners associated. + * Implemented this waysto work with the json-schema conversion for openapi. + */ +export class RunnerTeamHasRunnersError extends NotAcceptableError { + @IsString() + name = "RunnerTeamHasRunnersError" + + @IsString() + message = "This team still has runners associated with it. \n If you want to delete this team with all it's runners and teams ass `?force` to your query." +} \ No newline at end of file diff --git a/src/models/creation/CreateRunnerTeam.ts b/src/models/creation/CreateRunnerTeam.ts new file mode 100644 index 0000000..101a516 --- /dev/null +++ b/src/models/creation/CreateRunnerTeam.ts @@ -0,0 +1,36 @@ +import { IsInt, IsNotEmpty, IsString } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; +import { RunnerOrganisationNotFoundError } from '../../errors/RunnerOrganisationErrors'; +import { RunnerOrganisation } from '../entities/RunnerOrganisation'; +import { RunnerTeam } from '../entities/RunnerTeam'; + +export class CreateRunnerTeam { + /** + * The teams's name. + */ + @IsString() + @IsNotEmpty() + name: string; + + /** + * The team's parent group (organisation). + */ + @IsInt() + @IsNotEmpty() + parentId: number + + /** + * Creates a RunnerTeam entity from this. + */ + public async toRunnerTeam(): Promise { + let newRunnerTeam: RunnerTeam = new RunnerTeam(); + + newRunnerTeam.name = this.name; + newRunnerTeam.parentGroup = await getConnectionManager().get().getRepository(RunnerOrganisation).findOne({ id: this.parentId }); + if (!newRunnerTeam.parentGroup) { + throw new RunnerOrganisationNotFoundError(); + } + + return newRunnerTeam; + } +} \ No newline at end of file diff --git a/src/models/entities/RunnerTeam.ts b/src/models/entities/RunnerTeam.ts index a9fb2f0..551dfce 100644 --- a/src/models/entities/RunnerTeam.ts +++ b/src/models/entities/RunnerTeam.ts @@ -1,5 +1,6 @@ -import { Entity, Column, ManyToOne, ChildEntity } from "typeorm"; import { IsNotEmpty } from "class-validator"; +import { ChildEntity, getConnectionManager, ManyToOne } from "typeorm"; +import { Runner } from './Runner'; import { RunnerGroup } from "./RunnerGroup"; import { RunnerOrganisation } from "./RunnerOrganisation"; @@ -16,4 +17,12 @@ export class RunnerTeam extends RunnerGroup { @IsNotEmpty() @ManyToOne(() => RunnerOrganisation, org => org.teams, { nullable: true }) parentGroup?: RunnerOrganisation; + + /** + * Returns all runners associated with this team. + */ + public async getRunners() { + let runnerRepo = await getConnectionManager().get().getRepository(Runner); + return await runnerRepo.find({ group: this }); + } } \ No newline at end of file From c4f02023b949312a8e09d2fbd1c4d036816f30e0 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 17:51:27 +0100 Subject: [PATCH 04/16] Shortened db call ref #13 --- src/models/entities/RunnerOrganisation.ts | 3 +++ src/models/entities/RunnerTeam.ts | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/models/entities/RunnerOrganisation.ts b/src/models/entities/RunnerOrganisation.ts index b0a7220..f6e850e 100644 --- a/src/models/entities/RunnerOrganisation.ts +++ b/src/models/entities/RunnerOrganisation.ts @@ -23,4 +23,7 @@ export class RunnerOrganisation extends RunnerGroup { */ @OneToMany(() => RunnerTeam, team => team.parentGroup, { nullable: true }) teams: RunnerTeam[]; + public async getTeams() { + return await getConnectionManager().get().getRepository(RunnerTeam).find({ parentGroup: this }); + } } \ No newline at end of file diff --git a/src/models/entities/RunnerTeam.ts b/src/models/entities/RunnerTeam.ts index 551dfce..44a047a 100644 --- a/src/models/entities/RunnerTeam.ts +++ b/src/models/entities/RunnerTeam.ts @@ -22,7 +22,6 @@ export class RunnerTeam extends RunnerGroup { * Returns all runners associated with this team. */ public async getRunners() { - let runnerRepo = await getConnectionManager().get().getRepository(Runner); - return await runnerRepo.find({ group: this }); + return await getConnectionManager().get().getRepository(Runner).find({ group: this }); } } \ No newline at end of file From 795599fd388150a07c959cb3fbed6f676d3db790 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 18:15:38 +0100 Subject: [PATCH 05/16] Working(tm) implementation of group and team deletion ref #13 --- src/controllers/RunnerController.ts | 8 ++--- .../RunnerOrganisationController.ts | 31 +++++++++++++------ src/controllers/RunnerTeamController.ts | 6 ++-- src/models/entities/RunnerGroup.ts | 7 +++-- src/models/entities/RunnerOrganisation.ts | 27 ++++++++++++++-- 5 files changed, 56 insertions(+), 23 deletions(-) diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 798d3c6..124a062 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -1,10 +1,10 @@ -import { JsonController, Param, Body, Get, Post, Put, Delete, OnUndefined } from 'routing-controllers'; +import { 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 { EntityFromBody } from 'typeorm-routing-controllers-extensions'; -import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; -import { Runner } from '../models/entities/Runner'; import { RunnerGroupNeededError, RunnerGroupNotFoundError, RunnerIdsNotMatchingError, RunnerNotFoundError, RunnerOnlyOneGroupAllowedError } from '../errors/RunnerErrors'; import { CreateRunner } from '../models/creation/CreateRunner'; +import { Runner } from '../models/entities/Runner'; @JsonController('/runners') @@ -76,7 +76,7 @@ export class RunnerController { @ResponseSchema(Runner) @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) @OpenAPI({ description: 'Delete a specified runner (if it exists).' }) - async remove(@Param('id') id: number) { + async remove(@Param('id') id: number, @QueryParam("force") force: boolean) { let runner = await this.runnerRepository.findOne({ id: id }); if (!runner) { diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index 0bae97f..2ad2e25 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -4,7 +4,11 @@ import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; import { RunnerOrganisationHasRunnersError, RunnerOrganisationHasTeamsError, RunnerOrganisationIdsNotMatchingError, RunnerOrganisationNotFoundError } from '../errors/RunnerOrganisationErrors'; import { CreateRunnerOrganisation } from '../models/creation/CreateRunnerOrganisation'; +import { Runner } from '../models/entities/Runner'; import { RunnerOrganisation } from '../models/entities/RunnerOrganisation'; +import { RunnerTeam } from '../models/entities/RunnerTeam'; +import { RunnerController } from './RunnerController'; +import { RunnerTeamController } from './RunnerTeamController'; @JsonController('/organisations') @@ -72,25 +76,34 @@ export class RunnerOrganisationController { @Delete('/:id') @ResponseSchema(RunnerOrganisation) @ResponseSchema(RunnerOrganisationNotFoundError, { statusCode: 404 }) + @ResponseSchema(RunnerOrganisationHasRunnersError, { statusCode: 406 }) + @ResponseSchema(RunnerOrganisationHasTeamsError, { statusCode: 406 }) @OpenAPI({ description: 'Delete a specified runnerOrganisation (if it exists).' }) - async remove(@Param('id') id: number, @QueryParam("force") force) { + async remove(@Param('id') id: number, @QueryParam("force") force: boolean) { let runnerOrganisation = await this.runnerOrganisationRepository.findOne({ id: id }); if (!runnerOrganisation) { throw new RunnerOrganisationNotFoundError(); } - if (!force) { - if (runnerOrganisation.runners.length != 0) { - throw new RunnerOrganisationHasRunnersError(); - } + let runners: Runner[] = await runnerOrganisation.getRunners() - if (runnerOrganisation.teams.length != 0) { - throw new RunnerOrganisationHasTeamsError(); - } + if (!force && runners.length != 0) { + throw new RunnerOrganisationHasRunnersError(); } + const runnerController = new RunnerController() + await runners.forEach(async runner => { + await runnerController.remove(runner.id, true); + }); - //TODO: Delete runner and teams + let teams: RunnerTeam[] = await runnerOrganisation.getTeams() + if (!force && teams.length != 0) { + throw new RunnerOrganisationHasTeamsError(); + } + const teamController = new RunnerTeamController() + await teams.forEach(async team => { + await teamController.remove(team.id, true); + }); await this.runnerOrganisationRepository.delete(runnerOrganisation); return runnerOrganisation; diff --git a/src/controllers/RunnerTeamController.ts b/src/controllers/RunnerTeamController.ts index 466a9ff..ecbe9a2 100644 --- a/src/controllers/RunnerTeamController.ts +++ b/src/controllers/RunnerTeamController.ts @@ -76,7 +76,7 @@ export class RunnerTeamController { @ResponseSchema(RunnerTeamNotFoundError, { statusCode: 404 }) @ResponseSchema(RunnerTeamHasRunnersError, { statusCode: 406 }) @OpenAPI({ description: 'Delete a specified runnerTeam (if it exists).' }) - async remove(@Param('id') id: number, @QueryParam("force") force) { + async remove(@Param('id') id: number, @QueryParam("force") force: boolean) { let runnerTeam = await this.runnerTeamRepository.findOne({ id: id }); if (!runnerTeam) { @@ -84,16 +84,14 @@ export class RunnerTeamController { } let runners: Runner[] = await runnerTeam.getRunners() - if (!force) { if (runners.length != 0) { throw new RunnerTeamHasRunnersError(); } } - const runnerController = new RunnerController() runners.forEach(runner => { - runnerController.remove(runner.id) + runnerController.remove(runner.id, true) }); await this.runnerTeamRepository.delete(runnerTeam); diff --git a/src/models/entities/RunnerGroup.ts b/src/models/entities/RunnerGroup.ts index 8142754..2033d88 100644 --- a/src/models/entities/RunnerGroup.ts +++ b/src/models/entities/RunnerGroup.ts @@ -1,13 +1,12 @@ -import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne, Entity, TableInheritance } from "typeorm"; import { IsInt, IsNotEmpty, IsOptional, - IsString, + IsString } from "class-validator"; +import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, TableInheritance } from "typeorm"; import { GroupContact } from "./GroupContact"; import { Runner } from "./Runner"; -import { RunnerTeam } from "./RunnerTeam"; /** * Defines the runnerGroup interface. @@ -44,4 +43,6 @@ export abstract class RunnerGroup { */ @OneToMany(() => Runner, runner => runner.group, { nullable: true }) runners: Runner[]; + + public abstract getRunners(); } \ No newline at end of file diff --git a/src/models/entities/RunnerOrganisation.ts b/src/models/entities/RunnerOrganisation.ts index f6e850e..bfdfe49 100644 --- a/src/models/entities/RunnerOrganisation.ts +++ b/src/models/entities/RunnerOrganisation.ts @@ -1,7 +1,8 @@ -import { Entity, Column, ManyToOne, OneToMany, ChildEntity } from "typeorm"; -import { IsOptional, } from "class-validator"; -import { RunnerGroup } from "./RunnerGroup"; +import { IsOptional } from "class-validator"; +import { ChildEntity, getConnectionManager, ManyToOne, OneToMany } from "typeorm"; import { Address } from "./Address"; +import { Runner } from './Runner'; +import { RunnerGroup } from "./RunnerGroup"; import { RunnerTeam } from "./RunnerTeam"; /** @@ -23,6 +24,26 @@ export class RunnerOrganisation extends RunnerGroup { */ @OneToMany(() => RunnerTeam, team => team.parentGroup, { nullable: true }) teams: RunnerTeam[]; + + + /** + * Returns all runners associated with this organisation or it's teams. + */ + public async getRunners() { + let runners: Runner[] = new Array(); + const teams = await this.getTeams(); + + await teams.forEach(async team => { + runners.push(... await team.getRunners()); + }); + await runners.push(... await getConnectionManager().get().getRepository(Runner).find({ group: this })); + + return runners; + } + + /** + * Returns all teams associated with this organisation. + */ public async getTeams() { return await getConnectionManager().get().getRepository(RunnerTeam).find({ parentGroup: this }); } From 65f995cb9fe27b142437d3b83b0fcc7e03c2d6b9 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 18:16:34 +0100 Subject: [PATCH 06/16] Removed console logging only used for dev ref #13 --- src/models/creation/CreateRunner.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/models/creation/CreateRunner.ts b/src/models/creation/CreateRunner.ts index b68a90b..9913ee8 100644 --- a/src/models/creation/CreateRunner.ts +++ b/src/models/creation/CreateRunner.ts @@ -91,7 +91,6 @@ export class CreateRunner { newRunner.phone = this.phone; newRunner.email = this.email; - console.log(newRunner) return newRunner; } } \ No newline at end of file From 913033373be3ece9bed301f787e7d7036042ca26 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 18:34:01 +0100 Subject: [PATCH 07/16] Switched to using a response model for tracks --- src/controllers/TrackController.ts | 39 +++++++++++++++------------ src/models/responses/ResponseTrack.ts | 35 ++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 src/models/responses/ResponseTrack.ts diff --git a/src/controllers/TrackController.ts b/src/controllers/TrackController.ts index 869f1be..ae451be 100644 --- a/src/controllers/TrackController.ts +++ b/src/controllers/TrackController.ts @@ -1,11 +1,11 @@ -import { JsonController, Param, Body, Get, Post, Put, Delete, NotFoundError, OnUndefined, NotAcceptableError, Authorized } from 'routing-controllers'; +import { Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put } from 'routing-controllers'; +import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; -import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; -import { Track } from '../models/entities/Track'; -import { IsInt, IsNotEmpty, IsPositive, IsString } from 'class-validator'; import { TrackIdsNotMatchingError, TrackNotFoundError } from "../errors/TrackErrors"; import { CreateTrack } from '../models/creation/CreateTrack'; +import { Track } from '../models/entities/Track'; +import { ResponseTrack } from '../models/responses/ResponseTrack'; @JsonController('/tracks') //@Authorized("TRACKS:read") @@ -20,33 +20,38 @@ export class TrackController { } @Get() - @ResponseSchema(Track, { isArray: true }) + @ResponseSchema(ResponseTrack, { isArray: true }) @OpenAPI({ description: "Lists all tracks." }) - getAll() { - return this.trackRepository.find(); + async getAll() { + let responseTracks: ResponseTrack[] = new Array(); + const tracks = await this.trackRepository.find(); + tracks.forEach(track => { + responseTracks.push(new ResponseTrack(track)); + }); + return responseTracks; } @Get('/:id') - @ResponseSchema(Track) + @ResponseSchema(ResponseTrack) @ResponseSchema(TrackNotFoundError, { statusCode: 404 }) @OnUndefined(TrackNotFoundError) @OpenAPI({ description: "Returns a track of a specified id (if it exists)" }) - getOne(@Param('id') id: number) { - return this.trackRepository.findOne({ id: id }); + async getOne(@Param('id') id: number) { + return new ResponseTrack(await this.trackRepository.findOne({ id: id })); } @Post() - @ResponseSchema(Track) + @ResponseSchema(ResponseTrack) @OpenAPI({ description: "Create a new track object (id will be generated automagicly)." }) - post( + async post( @Body({ validate: true }) track: CreateTrack ) { - return this.trackRepository.save(track.toTrack()); + return new ResponseTrack(await this.trackRepository.save(track.toTrack())); } @Put('/:id') - @ResponseSchema(Track) + @ResponseSchema(ResponseTrack) @ResponseSchema(TrackNotFoundError, { statusCode: 404 }) @ResponseSchema(TrackIdsNotMatchingError, { statusCode: 406 }) @OpenAPI({ description: "Update a track object (id can't be changed)." }) @@ -62,11 +67,11 @@ export class TrackController { } await this.trackRepository.update(oldTrack, track); - return track; + return new ResponseTrack(track); } @Delete('/:id') - @ResponseSchema(Track) + @ResponseSchema(ResponseTrack) @ResponseSchema(TrackNotFoundError, { statusCode: 404 }) @OpenAPI({ description: "Delete a specified track (if it exists)." }) async remove(@Param('id') id: number) { @@ -77,6 +82,6 @@ export class TrackController { } await this.trackRepository.delete(track); - return track; + return new ResponseTrack(track); } } diff --git a/src/models/responses/ResponseTrack.ts b/src/models/responses/ResponseTrack.ts new file mode 100644 index 0000000..ce1d74d --- /dev/null +++ b/src/models/responses/ResponseTrack.ts @@ -0,0 +1,35 @@ +import { + IsInt, + + IsString +} from "class-validator"; +import { Track } from '../entities/Track'; + +/** + * Defines a track of given length. +*/ +export class ResponseTrack { + /** + * Autogenerated unique id (primary key). + */ + @IsInt() + id: number;; + + /** + * The track's name. + */ + @IsString() + name: string; + + /** + * The track's length/distance in meters. + */ + @IsInt() + distance: number; + + public constructor(track: Track) { + this.id = track.id; + this.name = track.name; + this.distance = track.distance; + } +} From 8beb658bccb754b73ff7cb39d9d1e18e33cde0dd Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 19:01:36 +0100 Subject: [PATCH 08/16] New response model for runners ref #13 --- src/controllers/RunnerController.ts | 13 +++-- src/models/entities/Runner.ts | 16 ++++-- src/models/responses/ResponseRunner.ts | 78 ++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 src/models/responses/ResponseRunner.ts diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 124a062..851a495 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -5,7 +5,7 @@ import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; import { RunnerGroupNeededError, RunnerGroupNotFoundError, RunnerIdsNotMatchingError, RunnerNotFoundError, RunnerOnlyOneGroupAllowedError } from '../errors/RunnerErrors'; import { CreateRunner } from '../models/creation/CreateRunner'; import { Runner } from '../models/entities/Runner'; - +import { ResponseRunner } from '../models/responses/ResponseRunner'; @JsonController('/runners') //@Authorized('RUNNERS:read') @@ -20,10 +20,15 @@ export class RunnerController { } @Get() - @ResponseSchema(Runner, { isArray: true }) + @ResponseSchema(ResponseRunner, { isArray: true }) @OpenAPI({ description: 'Lists all runners.' }) - getAll() { - return this.runnerRepository.find(); + async getAll() { + let responseRunners: ResponseRunner[] = new Array(); + const runners = await this.runnerRepository.find(); + runners.forEach(runner => { + responseRunners.push(new ResponseRunner(runner)); + }); + return responseRunners; } @Get('/:id') diff --git a/src/models/entities/Runner.ts b/src/models/entities/Runner.ts index e4d3ff7..843d36a 100644 --- a/src/models/entities/Runner.ts +++ b/src/models/entities/Runner.ts @@ -1,9 +1,9 @@ -import { Entity, Column, OneToMany, ManyToOne, ChildEntity } from "typeorm"; -import { IsInt, IsNotEmpty, } from "class-validator"; -import { Participant } from "./Participant"; -import { RunnerGroup } from "./RunnerGroup"; +import { IsInt, IsNotEmpty } from "class-validator"; +import { ChildEntity, getConnectionManager, ManyToOne, OneToMany } from "typeorm"; import { DistanceDonation } from "./DistanceDonation"; +import { Participant } from "./Participant"; import { RunnerCard } from "./RunnerCard"; +import { RunnerGroup } from "./RunnerGroup"; import { Scan } from "./Scan"; /** @@ -38,7 +38,11 @@ export class Runner extends Participant { @IsInt() public get distance(): number { - return this.scans.filter(scan => scan.valid === true).reduce((sum, current) => sum + current.distance, 0); + getConnectionManager().get().getRepository(Scan).find({ runner: this }) + .then(myScans => { + return myScans.reduce((sum, current) => sum + current.distance, 0); + }) + .catch(err => { throw err }) + .finally(do => { return -1; }); } - } diff --git a/src/models/responses/ResponseRunner.ts b/src/models/responses/ResponseRunner.ts new file mode 100644 index 0000000..b2f3dfe --- /dev/null +++ b/src/models/responses/ResponseRunner.ts @@ -0,0 +1,78 @@ +import { + IsInt, + + IsObject, + + IsString +} from "class-validator"; +import { Runner } from '../entities/Runner'; +import { RunnerGroup } from '../entities/RunnerGroup'; + +/** + * Defines a track of given length. +*/ +export class ResponseRunner { + /** + * Autogenerated unique id (primary key). + */ + @IsInt() + id: number;; + + /** + * The runner's first name. + */ + @IsString() + firstname: string; + + /** + * The runner's middle name. + * Optional. + */ + @IsString() + middlename?: string; + + /** + * The runner's last name. + */ + @IsString() + lastname: string; + + /** + * The runner's phone number. + * Optional. + */ + @IsString() + phone?: string; + + /** + * The runner's e-mail address. + * Optional. + */ + @IsString() + email?: string; + + /** + * The runner's currently ran distance in meters. + * Optional. + */ + @IsInt() + distance: number; + + /** + * The runner's group. + */ + @IsObject() + group: RunnerGroup; + + + public constructor(runner: Runner) { + this.id = runner.id; + this.firstname = runner.firstname; + this.middlename = runner.middlename; + this.lastname = runner.lastname; + this.phone = runner.phone; + this.email = runner.email; + this.distance = runner.distance; + this.group = runner.group; + } +} From c30922e3250ad2a0510ab8fd296e364943994eba Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 19:31:20 +0100 Subject: [PATCH 09/16] Still broken distance, we'll fix this together ref #13 --- src/models/entities/DistanceDonation.ts | 4 ++-- src/models/entities/Runner.ts | 30 +++++++++++++++++-------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/models/entities/DistanceDonation.ts b/src/models/entities/DistanceDonation.ts index b54c642..669f88f 100644 --- a/src/models/entities/DistanceDonation.ts +++ b/src/models/entities/DistanceDonation.ts @@ -1,5 +1,5 @@ -import { Entity, Column, ManyToOne, ChildEntity } from "typeorm"; -import { IsInt, IsNotEmpty, IsPositive, } from "class-validator"; +import { IsInt, IsNotEmpty, IsPositive } from "class-validator"; +import { ChildEntity, Column, ManyToOne } from "typeorm"; import { Donation } from "./Donation"; import { Runner } from "./Runner"; diff --git a/src/models/entities/Runner.ts b/src/models/entities/Runner.ts index 843d36a..7a8d0ad 100644 --- a/src/models/entities/Runner.ts +++ b/src/models/entities/Runner.ts @@ -36,13 +36,25 @@ export class Runner extends Participant { @OneToMany(() => Scan, scan => scan.runner, { nullable: true }) scans: Scan[]; - @IsInt() - public get distance(): number { - getConnectionManager().get().getRepository(Scan).find({ runner: this }) - .then(myScans => { - return myScans.reduce((sum, current) => sum + current.distance, 0); - }) - .catch(err => { throw err }) - .finally(do => { return -1; }); + /** + * Returns all scans associated with this runner. + */ + public async getScans(): Promise { + return await getConnectionManager().get().getRepository(Scan).find({ runner: this }); } -} + + /** + * Returns all valid scans associated with this runner. + */ + public async getValidScans(): Promise { + return (await this.getScans()).filter(scan => { scan.valid === true }); + } + + /** + * Returns the total distance ran by this runner. + */ + @IsInt() + public async distance(): Promise { + return await (await this.getValidScans()).reduce((sum, current) => sum + current.distance, 0); + } +} \ No newline at end of file From 056413560ec29cb8d8a1f7a94528d84025e994c1 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 19:40:00 +0100 Subject: [PATCH 10/16] Now all runner endpoints return a response runner ref #13 --- src/controllers/RunnerController.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 851a495..6885d3c 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -32,16 +32,16 @@ export class RunnerController { } @Get('/:id') - @ResponseSchema(Runner) + @ResponseSchema(ResponseRunner) @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) @OnUndefined(RunnerNotFoundError) @OpenAPI({ description: 'Returns a runner of a specified id (if it exists)' }) - getOne(@Param('id') id: number) { - return this.runnerRepository.findOne({ id: id }); + async getOne(@Param('id') id: number) { + return new ResponseRunner(await this.runnerRepository.findOne({ id: id })); } @Post() - @ResponseSchema(Runner) + @ResponseSchema(ResponseRunner) @ResponseSchema(RunnerOnlyOneGroupAllowedError) @ResponseSchema(RunnerGroupNeededError) @ResponseSchema(RunnerGroupNotFoundError) @@ -54,11 +54,11 @@ export class RunnerController { return error; } - return this.runnerRepository.save(runner); + return new ResponseRunner(await this.runnerRepository.save(runner)); } @Put('/:id') - @ResponseSchema(Runner) + @ResponseSchema(ResponseRunner) @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) @ResponseSchema(RunnerIdsNotMatchingError, { statusCode: 406 }) @OpenAPI({ description: "Update a runner object (id can't be changed)." }) @@ -74,11 +74,11 @@ export class RunnerController { } await this.runnerRepository.update(oldRunner, runner); - return runner; + return new ResponseRunner(runner); } @Delete('/:id') - @ResponseSchema(Runner) + @ResponseSchema(ResponseRunner) @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) @OpenAPI({ description: 'Delete a specified runner (if it exists).' }) async remove(@Param('id') id: number, @QueryParam("force") force: boolean) { @@ -89,6 +89,6 @@ export class RunnerController { } await this.runnerRepository.delete(runner); - return runner; + return new ResponseRunner(runner); } } From c53e94d20517837b871f1649ac3fe850e9658c9c Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 21:18:44 +0100 Subject: [PATCH 11/16] Part 1 of the relation fix --- src/controllers/RunnerController.ts | 5 +++-- src/controllers/RunnerOrganisationController.ts | 8 ++++---- src/controllers/RunnerTeamController.ts | 8 ++++---- src/models/entities/Runner.ts | 4 ++-- src/models/responses/ResponseRunner.ts | 2 +- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 6885d3c..14658da 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -24,7 +24,8 @@ export class RunnerController { @OpenAPI({ description: 'Lists all runners.' }) async getAll() { let responseRunners: ResponseRunner[] = new Array(); - const runners = await this.runnerRepository.find(); + const runners = await this.runnerRepository.find({ relations: ['scans', 'group'] }); + console.log(runners); runners.forEach(runner => { responseRunners.push(new ResponseRunner(runner)); }); @@ -37,7 +38,7 @@ export class RunnerController { @OnUndefined(RunnerNotFoundError) @OpenAPI({ description: 'Returns a runner of a specified id (if it exists)' }) async getOne(@Param('id') id: number) { - return new ResponseRunner(await this.runnerRepository.findOne({ id: id })); + return new ResponseRunner(await this.runnerRepository.findOne({ id: id }, { relations: ['scans', 'group'] })); } @Post() diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index 2ad2e25..bee281f 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -27,7 +27,7 @@ export class RunnerOrganisationController { @ResponseSchema(RunnerOrganisation, { isArray: true }) @OpenAPI({ description: 'Lists all runnerOrganisations.' }) getAll() { - return this.runnerOrganisationRepository.find(); + return this.runnerOrganisationRepository.find({ relations: ['address', 'contact', 'teams'] }); } @Get('/:id') @@ -36,7 +36,7 @@ export class RunnerOrganisationController { @OnUndefined(RunnerOrganisationNotFoundError) @OpenAPI({ description: 'Returns a runnerOrganisation of a specified id (if it exists)' }) getOne(@Param('id') id: number) { - return this.runnerOrganisationRepository.findOne({ id: id }); + return this.runnerOrganisationRepository.findOne({ id: id }, { relations: ['address', 'contact', 'teams'] }); } @Post() @@ -59,7 +59,7 @@ export class RunnerOrganisationController { @ResponseSchema(RunnerOrganisationIdsNotMatchingError, { statusCode: 406 }) @OpenAPI({ description: "Update a runnerOrganisation object (id can't be changed)." }) async put(@Param('id') id: number, @EntityFromBody() runnerOrganisation: RunnerOrganisation) { - let oldRunnerOrganisation = await this.runnerOrganisationRepository.findOne({ id: id }); + let oldRunnerOrganisation = await this.runnerOrganisationRepository.findOne({ id: id }, { relations: ['address', 'contact', 'teams'] }); if (!oldRunnerOrganisation) { throw new RunnerOrganisationNotFoundError(); @@ -80,7 +80,7 @@ export class RunnerOrganisationController { @ResponseSchema(RunnerOrganisationHasTeamsError, { statusCode: 406 }) @OpenAPI({ description: 'Delete a specified runnerOrganisation (if it exists).' }) async remove(@Param('id') id: number, @QueryParam("force") force: boolean) { - let runnerOrganisation = await this.runnerOrganisationRepository.findOne({ id: id }); + let runnerOrganisation = await this.runnerOrganisationRepository.findOne({ id: id }, { relations: ['address', 'contact', 'teams'] }); if (!runnerOrganisation) { throw new RunnerOrganisationNotFoundError(); diff --git a/src/controllers/RunnerTeamController.ts b/src/controllers/RunnerTeamController.ts index ecbe9a2..37b4c9d 100644 --- a/src/controllers/RunnerTeamController.ts +++ b/src/controllers/RunnerTeamController.ts @@ -25,7 +25,7 @@ export class RunnerTeamController { @ResponseSchema(RunnerTeam, { isArray: true }) @OpenAPI({ description: 'Lists all runnerTeams.' }) getAll() { - return this.runnerTeamRepository.find(); + return this.runnerTeamRepository.find({ relations: ['parentGroup', 'contact'] }); } @Get('/:id') @@ -34,7 +34,7 @@ export class RunnerTeamController { @OnUndefined(RunnerTeamNotFoundError) @OpenAPI({ description: 'Returns a runnerTeam of a specified id (if it exists)' }) getOne(@Param('id') id: number) { - return this.runnerTeamRepository.findOne({ id: id }); + return this.runnerTeamRepository.findOne({ id: id }, { relations: ['parentGroup', 'contact'] }); } @Post() @@ -57,7 +57,7 @@ export class RunnerTeamController { @ResponseSchema(RunnerTeamIdsNotMatchingError, { statusCode: 406 }) @OpenAPI({ description: "Update a runnerTeam object (id can't be changed)." }) async put(@Param('id') id: number, @EntityFromBody() runnerTeam: RunnerTeam) { - let oldRunnerTeam = await this.runnerTeamRepository.findOne({ id: id }); + let oldRunnerTeam = await this.runnerTeamRepository.findOne({ id: id }, { relations: ['parentGroup', 'contact'] }); if (!oldRunnerTeam) { throw new RunnerTeamNotFoundError(); @@ -77,7 +77,7 @@ export class RunnerTeamController { @ResponseSchema(RunnerTeamHasRunnersError, { statusCode: 406 }) @OpenAPI({ description: 'Delete a specified runnerTeam (if it exists).' }) async remove(@Param('id') id: number, @QueryParam("force") force: boolean) { - let runnerTeam = await this.runnerTeamRepository.findOne({ id: id }); + let runnerTeam = await this.runnerTeamRepository.findOne({ id: id }, { relations: ['parentGroup', 'contact'] }); if (!runnerTeam) { throw new RunnerTeamNotFoundError(); diff --git a/src/models/entities/Runner.ts b/src/models/entities/Runner.ts index 7a8d0ad..2496d98 100644 --- a/src/models/entities/Runner.ts +++ b/src/models/entities/Runner.ts @@ -54,7 +54,7 @@ export class Runner extends Participant { * Returns the total distance ran by this runner. */ @IsInt() - public async distance(): Promise { - return await (await this.getValidScans()).reduce((sum, current) => sum + current.distance, 0); + public get distance(): number { + return 0; } } \ No newline at end of file diff --git a/src/models/responses/ResponseRunner.ts b/src/models/responses/ResponseRunner.ts index b2f3dfe..591c0a3 100644 --- a/src/models/responses/ResponseRunner.ts +++ b/src/models/responses/ResponseRunner.ts @@ -72,7 +72,7 @@ export class ResponseRunner { this.lastname = runner.lastname; this.phone = runner.phone; this.email = runner.email; - this.distance = runner.distance; + this.distance = runner.scans.reduce((sum, current) => sum + current.distance, 0); this.group = runner.group; } } From a437ada3b3cee64c69548cf0cd1e93d4fa2495b3 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 21:38:28 +0100 Subject: [PATCH 12/16] Runnerteams now with resolving relations and response types :O ref #13 --- src/controllers/RunnerTeamController.ts | 36 +++++++++----- src/models/responses/ResponseRunnerGroup.ts | 55 +++++++++++++++++++++ src/models/responses/ResponseRunnerTeam.ts | 26 ++++++++++ 3 files changed, 105 insertions(+), 12 deletions(-) create mode 100644 src/models/responses/ResponseRunnerGroup.ts create mode 100644 src/models/responses/ResponseRunnerTeam.ts diff --git a/src/controllers/RunnerTeamController.ts b/src/controllers/RunnerTeamController.ts index 37b4c9d..e07931a 100644 --- a/src/controllers/RunnerTeamController.ts +++ b/src/controllers/RunnerTeamController.ts @@ -6,6 +6,7 @@ import { RunnerTeamHasRunnersError, RunnerTeamIdsNotMatchingError, RunnerTeamNot import { CreateRunnerTeam } from '../models/creation/CreateRunnerTeam'; import { Runner } from '../models/entities/Runner'; import { RunnerTeam } from '../models/entities/RunnerTeam'; +import { ResponseRunnerTeam } from '../models/responses/ResponseRunnerTeam'; import { RunnerController } from './RunnerController'; @@ -22,23 +23,29 @@ export class RunnerTeamController { } @Get() - @ResponseSchema(RunnerTeam, { isArray: true }) + @ResponseSchema(ResponseRunnerTeam, { isArray: true }) @OpenAPI({ description: 'Lists all runnerTeams.' }) - getAll() { - return this.runnerTeamRepository.find({ relations: ['parentGroup', 'contact'] }); + async getAll() { + let responseTeams: ResponseRunnerTeam[] = new Array(); + const runners = await this.runnerTeamRepository.find({ relations: ['parentGroup', 'contact'] }); + console.log(runners); + runners.forEach(runner => { + responseTeams.push(new ResponseRunnerTeam(runner)); + }); + return responseTeams; } @Get('/:id') - @ResponseSchema(RunnerTeam) + @ResponseSchema(ResponseRunnerTeam) @ResponseSchema(RunnerTeamNotFoundError, { statusCode: 404 }) @OnUndefined(RunnerTeamNotFoundError) @OpenAPI({ description: 'Returns a runnerTeam of a specified id (if it exists)' }) - getOne(@Param('id') id: number) { - return this.runnerTeamRepository.findOne({ id: id }, { relations: ['parentGroup', 'contact'] }); + async getOne(@Param('id') id: number) { + return new ResponseRunnerTeam(await this.runnerTeamRepository.findOne({ id: id }, { relations: ['parentGroup', 'contact'] })); } @Post() - @ResponseSchema(RunnerTeam) + @ResponseSchema(ResponseRunnerTeam) @OpenAPI({ description: 'Create a new runnerTeam object (id will be generated automagicly).' }) async post(@Body({ validate: true }) createRunnerTeam: CreateRunnerTeam) { let runnerTeam; @@ -48,11 +55,14 @@ export class RunnerTeamController { return error; } - return this.runnerTeamRepository.save(runnerTeam); + runnerTeam = await this.runnerTeamRepository.save(runnerTeam); + runnerTeam = await this.runnerTeamRepository.findOne(runnerTeam, { relations: ['parentGroup', 'contact'] }); + + return new ResponseRunnerTeam(runnerTeam); } @Put('/:id') - @ResponseSchema(RunnerTeam) + @ResponseSchema(ResponseRunnerTeam) @ResponseSchema(RunnerTeamNotFoundError, { statusCode: 404 }) @ResponseSchema(RunnerTeamIdsNotMatchingError, { statusCode: 406 }) @OpenAPI({ description: "Update a runnerTeam object (id can't be changed)." }) @@ -68,11 +78,13 @@ export class RunnerTeamController { } await this.runnerTeamRepository.update(oldRunnerTeam, runnerTeam); - return runnerTeam; + + runnerTeam = await this.runnerTeamRepository.findOne(runnerTeam, { relations: ['parentGroup', 'contact'] }); + return new ResponseRunnerTeam(runnerTeam); } @Delete('/:id') - @ResponseSchema(RunnerTeam) + @ResponseSchema(ResponseRunnerTeam) @ResponseSchema(RunnerTeamNotFoundError, { statusCode: 404 }) @ResponseSchema(RunnerTeamHasRunnersError, { statusCode: 406 }) @OpenAPI({ description: 'Delete a specified runnerTeam (if it exists).' }) @@ -95,6 +107,6 @@ export class RunnerTeamController { }); await this.runnerTeamRepository.delete(runnerTeam); - return runnerTeam; + return new ResponseRunnerTeam(runnerTeam); } } diff --git a/src/models/responses/ResponseRunnerGroup.ts b/src/models/responses/ResponseRunnerGroup.ts new file mode 100644 index 0000000..922f141 --- /dev/null +++ b/src/models/responses/ResponseRunnerGroup.ts @@ -0,0 +1,55 @@ +import { + IsInt, + + + + IsNotEmpty, + + + + IsObject, + + + + IsOptional, + + + + IsString +} from "class-validator"; +import { GroupContact } from '../entities/GroupContact'; +import { RunnerGroup } from '../entities/RunnerGroup'; + +/** + * Defines a track of given length. +*/ +export abstract class ResponseRunnerGroup { + /** + * Autogenerated unique id (primary key). + */ + @IsInt() + @IsNotEmpty() + id: number;; + + /** + * The groups's name. + */ + @IsString() + @IsNotEmpty() + name: string; + + + /** + * The group's contact. + * Optional. + */ + @IsObject() + @IsOptional() + contact?: GroupContact; + + public constructor(group: RunnerGroup) { + this.id = group.id; + this.name = group.name; + this.contact = group.contact; + } +} diff --git a/src/models/responses/ResponseRunnerTeam.ts b/src/models/responses/ResponseRunnerTeam.ts new file mode 100644 index 0000000..b26e2c2 --- /dev/null +++ b/src/models/responses/ResponseRunnerTeam.ts @@ -0,0 +1,26 @@ +import { + IsNotEmpty, + IsObject +} from "class-validator"; +import { RunnerOrganisation } from '../entities/RunnerOrganisation'; +import { RunnerTeam } from '../entities/RunnerTeam'; +import { ResponseRunnerGroup } from './ResponseRunnerGroup'; + +/** + * Defines RunnerTeam's response class. +*/ +export class ResponseRunnerTeam extends ResponseRunnerGroup { + + /** + * The team's parent group (organisation). + * Optional. + */ + @IsObject() + @IsNotEmpty() + parentGroup: RunnerOrganisation; + + public constructor(team: RunnerTeam) { + super(team); + this.parentGroup = team.parentGroup; + } +} From 7b084895334431445ae2c038bd086c8833a8e530 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 21:55:09 +0100 Subject: [PATCH 13/16] Now with working runner orga controller including responses ref #13 --- .../RunnerOrganisationController.ts | 63 ++++++++++++------- src/controllers/RunnerTeamController.ts | 5 +- .../responses/ResponseRunnerOrganisation.ts | 37 +++++++++++ 3 files changed, 79 insertions(+), 26 deletions(-) create mode 100644 src/models/responses/ResponseRunnerOrganisation.ts diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index bee281f..bed6eec 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -7,11 +7,12 @@ import { CreateRunnerOrganisation } from '../models/creation/CreateRunnerOrganis import { Runner } from '../models/entities/Runner'; import { RunnerOrganisation } from '../models/entities/RunnerOrganisation'; import { RunnerTeam } from '../models/entities/RunnerTeam'; +import { ResponseRunnerOrganisation } from '../models/responses/ResponseRunnerOrganisation'; import { RunnerController } from './RunnerController'; import { RunnerTeamController } from './RunnerTeamController'; -@JsonController('/organisations') +@JsonController('/organisation') //@Authorized('RUNNERS:read') export class RunnerOrganisationController { private runnerOrganisationRepository: Repository; @@ -24,23 +25,29 @@ export class RunnerOrganisationController { } @Get() - @ResponseSchema(RunnerOrganisation, { isArray: true }) + @ResponseSchema(ResponseRunnerOrganisation, { isArray: true }) @OpenAPI({ description: 'Lists all runnerOrganisations.' }) - getAll() { - return this.runnerOrganisationRepository.find({ relations: ['address', 'contact', 'teams'] }); + async getAll() { + let responseTeams: ResponseRunnerOrganisation[] = new Array(); + const runners = await this.runnerOrganisationRepository.find({ relations: ['address', 'contact', 'teams'] }); + console.log(runners); + runners.forEach(runner => { + responseTeams.push(new ResponseRunnerOrganisation(runner)); + }); + return responseTeams; } @Get('/:id') - @ResponseSchema(RunnerOrganisation) + @ResponseSchema(ResponseRunnerOrganisation) @ResponseSchema(RunnerOrganisationNotFoundError, { statusCode: 404 }) @OnUndefined(RunnerOrganisationNotFoundError) @OpenAPI({ description: 'Returns a runnerOrganisation of a specified id (if it exists)' }) - getOne(@Param('id') id: number) { - return this.runnerOrganisationRepository.findOne({ id: id }, { relations: ['address', 'contact', 'teams'] }); + async getOne(@Param('id') id: number) { + return new ResponseRunnerOrganisation(await this.runnerOrganisationRepository.findOne({ id: id }, { relations: ['address', 'contact', 'teams'] })); } @Post() - @ResponseSchema(RunnerOrganisation) + @ResponseSchema(ResponseRunnerOrganisation) @OpenAPI({ description: 'Create a new runnerOrganisation object (id will be generated automagicly).' }) async post(@Body({ validate: true }) createRunnerOrganisation: CreateRunnerOrganisation) { let runnerOrganisation; @@ -50,11 +57,14 @@ export class RunnerOrganisationController { return error; } - return this.runnerOrganisationRepository.save(runnerOrganisation); + runnerOrganisation = await this.runnerOrganisationRepository.save(runnerOrganisation); + runnerOrganisation = await this.runnerOrganisationRepository.findOne(runnerOrganisation, { relations: ['address', 'contact', 'teams'] }); + + return new ResponseRunnerOrganisation(runnerOrganisation); } @Put('/:id') - @ResponseSchema(RunnerOrganisation) + @ResponseSchema(ResponseRunnerOrganisation) @ResponseSchema(RunnerOrganisationNotFoundError, { statusCode: 404 }) @ResponseSchema(RunnerOrganisationIdsNotMatchingError, { statusCode: 406 }) @OpenAPI({ description: "Update a runnerOrganisation object (id can't be changed)." }) @@ -70,14 +80,15 @@ export class RunnerOrganisationController { } await this.runnerOrganisationRepository.update(oldRunnerOrganisation, runnerOrganisation); - return runnerOrganisation; + + runnerOrganisation = await this.runnerOrganisationRepository.findOne(runnerOrganisation, { relations: ['address', 'contact', 'teams'] }); + return new ResponseRunnerOrganisation(runnerOrganisation); } @Delete('/:id') - @ResponseSchema(RunnerOrganisation) + @ResponseSchema(ResponseRunnerOrganisation) @ResponseSchema(RunnerOrganisationNotFoundError, { statusCode: 404 }) @ResponseSchema(RunnerOrganisationHasRunnersError, { statusCode: 406 }) - @ResponseSchema(RunnerOrganisationHasTeamsError, { statusCode: 406 }) @OpenAPI({ description: 'Delete a specified runnerOrganisation (if it exists).' }) async remove(@Param('id') id: number, @QueryParam("force") force: boolean) { let runnerOrganisation = await this.runnerOrganisationRepository.findOne({ id: id }, { relations: ['address', 'contact', 'teams'] }); @@ -87,25 +98,29 @@ export class RunnerOrganisationController { } let runners: Runner[] = await runnerOrganisation.getRunners() - - if (!force && runners.length != 0) { - throw new RunnerOrganisationHasRunnersError(); + if (!force) { + if (runners.length != 0) { + throw new RunnerOrganisationHasRunnersError(); + } } const runnerController = new RunnerController() - await runners.forEach(async runner => { - await runnerController.remove(runner.id, true); + runners.forEach(runner => { + runnerController.remove(runner.id, true) }); let teams: RunnerTeam[] = await runnerOrganisation.getTeams() - if (!force && teams.length != 0) { - throw new RunnerOrganisationHasTeamsError(); + if (!force) { + if (teams.length != 0) { + throw new RunnerOrganisationHasTeamsError(); + } } const teamController = new RunnerTeamController() - await teams.forEach(async team => { - await teamController.remove(team.id, true); + teams.forEach(team => { + teamController.remove(team.id, true) }); - await this.runnerOrganisationRepository.delete(runnerOrganisation); - return runnerOrganisation; + const responseOrganisation = new ResponseRunnerOrganisation(runnerOrganisation); + await this.runnerOrganisationRepository.delete({ id: runnerOrganisation.id }); + return responseOrganisation; } } diff --git a/src/controllers/RunnerTeamController.ts b/src/controllers/RunnerTeamController.ts index e07931a..6103083 100644 --- a/src/controllers/RunnerTeamController.ts +++ b/src/controllers/RunnerTeamController.ts @@ -106,7 +106,8 @@ export class RunnerTeamController { runnerController.remove(runner.id, true) }); - await this.runnerTeamRepository.delete(runnerTeam); - return new ResponseRunnerTeam(runnerTeam); + const responseTeam = new ResponseRunnerTeam(runnerTeam); + await this.runnerTeamRepository.delete({ id: runnerTeam.id }); + return responseTeam; } } diff --git a/src/models/responses/ResponseRunnerOrganisation.ts b/src/models/responses/ResponseRunnerOrganisation.ts new file mode 100644 index 0000000..2aecb75 --- /dev/null +++ b/src/models/responses/ResponseRunnerOrganisation.ts @@ -0,0 +1,37 @@ +import { + IsArray, + IsNotEmpty, + IsObject +} from "class-validator"; +import { Address } from '../entities/Address'; +import { RunnerOrganisation } from '../entities/RunnerOrganisation'; +import { RunnerTeam } from '../entities/RunnerTeam'; +import { ResponseRunnerGroup } from './ResponseRunnerGroup'; + +/** + * Defines RunnerOrgs's response class. +*/ +export class ResponseRunnerOrganisation extends ResponseRunnerGroup { + + /** + * The orgs's address. + * Optional. + */ + @IsObject() + @IsNotEmpty() + address?: Address; + + /** + * The orgs associated teams. + */ + @IsObject() + @IsArray() + teams: RunnerTeam[]; + + + public constructor(org: RunnerOrganisation) { + super(org); + this.address = org.address; + this.teams = org.teams; + } +} From b480912bd8c643391865da4c7f4bfeb796315d7a Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 22:00:48 +0100 Subject: [PATCH 14/16] Now with even more inheritance and fancy stuff: RunnerResponses now get their information from participant responses ref #13 --- src/models/responses/ResponseParticipant.ts | 61 +++++++++++++++++++++ src/models/responses/ResponseRunner.ts | 56 ++----------------- 2 files changed, 66 insertions(+), 51 deletions(-) create mode 100644 src/models/responses/ResponseParticipant.ts diff --git a/src/models/responses/ResponseParticipant.ts b/src/models/responses/ResponseParticipant.ts new file mode 100644 index 0000000..22ec2a3 --- /dev/null +++ b/src/models/responses/ResponseParticipant.ts @@ -0,0 +1,61 @@ +import { + IsInt, + + + + IsString +} from "class-validator"; +import { Participant } from '../entities/Participant'; + +/** + * Defines a participant response. +*/ +export abstract class ResponseParticipant { + /** + * Autogenerated unique id (primary key). + */ + @IsInt() + id: number;; + + /** + * The participant's first name. + */ + @IsString() + firstname: string; + + /** + * The participant's middle name. + * Optional. + */ + @IsString() + middlename?: string; + + /** + * The participant's last name. + */ + @IsString() + lastname: string; + + /** + * The participant's phone number. + * Optional. + */ + @IsString() + phone?: string; + + /** + * The participant's e-mail address. + * Optional. + */ + @IsString() + email?: string; + + public constructor(participant: Participant) { + this.id = participant.id; + this.firstname = participant.firstname; + this.middlename = participant.middlename; + this.lastname = participant.lastname; + this.phone = participant.phone; + this.email = participant.email; + } +} diff --git a/src/models/responses/ResponseRunner.ts b/src/models/responses/ResponseRunner.ts index 591c0a3..677ca93 100644 --- a/src/models/responses/ResponseRunner.ts +++ b/src/models/responses/ResponseRunner.ts @@ -1,55 +1,15 @@ import { IsInt, - - IsObject, - - IsString + IsObject } from "class-validator"; import { Runner } from '../entities/Runner'; import { RunnerGroup } from '../entities/RunnerGroup'; +import { ResponseParticipant } from './ResponseParticipant'; /** - * Defines a track of given length. + * Defines RunnerTeam's response class. */ -export class ResponseRunner { - /** - * Autogenerated unique id (primary key). - */ - @IsInt() - id: number;; - - /** - * The runner's first name. - */ - @IsString() - firstname: string; - - /** - * The runner's middle name. - * Optional. - */ - @IsString() - middlename?: string; - - /** - * The runner's last name. - */ - @IsString() - lastname: string; - - /** - * The runner's phone number. - * Optional. - */ - @IsString() - phone?: string; - - /** - * The runner's e-mail address. - * Optional. - */ - @IsString() - email?: string; +export class ResponseRunner extends ResponseParticipant { /** * The runner's currently ran distance in meters. @@ -64,14 +24,8 @@ export class ResponseRunner { @IsObject() group: RunnerGroup; - public constructor(runner: Runner) { - this.id = runner.id; - this.firstname = runner.firstname; - this.middlename = runner.middlename; - this.lastname = runner.lastname; - this.phone = runner.phone; - this.email = runner.email; + super(runner); this.distance = runner.scans.reduce((sum, current) => sum + current.distance, 0); this.group = runner.group; } From afef95e14ea1e77144a9cd0ccbf9b02e4d63b4f4 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 22:07:30 +0100 Subject: [PATCH 15/16] Fixed amount calculations --- src/models/entities/DistanceDonation.ts | 11 +++++++++-- src/models/entities/Donation.ts | 7 +++---- src/models/entities/Runner.ts | 4 ++-- src/models/responses/ResponseRunner.ts | 2 +- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/models/entities/DistanceDonation.ts b/src/models/entities/DistanceDonation.ts index 669f88f..d5610d7 100644 --- a/src/models/entities/DistanceDonation.ts +++ b/src/models/entities/DistanceDonation.ts @@ -29,10 +29,17 @@ export class DistanceDonation extends Donation { * The exact implementation may differ for each type of donation. */ @IsInt() - public get amount(): number { + public get amount() { + return this.getAmount(); + } + + /** + * The function that calculates the amount based on the runner object's distance. + */ + public async getAmount(): Promise { let calculatedAmount = -1; try { - calculatedAmount = this.amountPerDistance * this.runner.distance; + calculatedAmount = this.amountPerDistance * await this.runner.distance(); } catch (error) { throw error; } diff --git a/src/models/entities/Donation.ts b/src/models/entities/Donation.ts index c4ab699..2609a98 100644 --- a/src/models/entities/Donation.ts +++ b/src/models/entities/Donation.ts @@ -1,10 +1,9 @@ -import { PrimaryGeneratedColumn, Column, ManyToOne, Entity, TableInheritance } from "typeorm"; import { IsInt, IsNotEmpty, - IsOptional, - IsPositive, + IsOptional } from "class-validator"; +import { Entity, ManyToOne, PrimaryGeneratedColumn, TableInheritance } from "typeorm"; import { Participant } from "./Participant"; /** @@ -32,5 +31,5 @@ export abstract class Donation { * The donation's amount in cents (or whatever your currency's smallest unit is.). * The exact implementation may differ for each type of donation. */ - abstract amount: number; + abstract amount: number | Promise; } \ No newline at end of file diff --git a/src/models/entities/Runner.ts b/src/models/entities/Runner.ts index 2496d98..7a8d0ad 100644 --- a/src/models/entities/Runner.ts +++ b/src/models/entities/Runner.ts @@ -54,7 +54,7 @@ export class Runner extends Participant { * Returns the total distance ran by this runner. */ @IsInt() - public get distance(): number { - return 0; + public async distance(): Promise { + return await (await this.getValidScans()).reduce((sum, current) => sum + current.distance, 0); } } \ No newline at end of file diff --git a/src/models/responses/ResponseRunner.ts b/src/models/responses/ResponseRunner.ts index 677ca93..01327c2 100644 --- a/src/models/responses/ResponseRunner.ts +++ b/src/models/responses/ResponseRunner.ts @@ -26,7 +26,7 @@ export class ResponseRunner extends ResponseParticipant { public constructor(runner: Runner) { super(runner); - this.distance = runner.scans.reduce((sum, current) => sum + current.distance, 0); + this.distance = runner.scans.filter(scan => { scan.valid === true }).reduce((sum, current) => sum + current.distance, 0); this.group = runner.group; } } From 56202ec3094444ec60170953aee5829e2a4ffba1 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 22:40:14 +0100 Subject: [PATCH 16/16] Create models now feature the createparticipant abstract ref #13 --- src/errors/ParticipantErrors.ts | 18 +++++ src/models/creation/CreateAddress.ts | 64 ++++++++++++++++++ src/models/creation/CreateParticipant.ts | 83 ++++++++++++++++++++++++ src/models/creation/CreateRunner.ts | 76 +++++++--------------- 4 files changed, 189 insertions(+), 52 deletions(-) create mode 100644 src/errors/ParticipantErrors.ts create mode 100644 src/models/creation/CreateAddress.ts create mode 100644 src/models/creation/CreateParticipant.ts diff --git a/src/errors/ParticipantErrors.ts b/src/errors/ParticipantErrors.ts new file mode 100644 index 0000000..de672d8 --- /dev/null +++ b/src/errors/ParticipantErrors.ts @@ -0,0 +1,18 @@ +import { IsString } from 'class-validator'; +import { NotAcceptableError, NotFoundError } from 'routing-controllers'; + +export class ParticipantOnlyOneAddressAllowedError extends NotAcceptableError { + @IsString() + name = "ParticipantOnlyOneAddressAllowedError" + + @IsString() + message = "Participant's can only have one address! \n You provided an id and address object.." +} + +export class ParticipantAddressNotFoundError extends NotFoundError { + @IsString() + name = "ParticipantAddressNotFoundError" + + @IsString() + message = "The address you provided couldn't be located in the system. \n Please check your request." +} \ No newline at end of file diff --git a/src/models/creation/CreateAddress.ts b/src/models/creation/CreateAddress.ts new file mode 100644 index 0000000..ef78410 --- /dev/null +++ b/src/models/creation/CreateAddress.ts @@ -0,0 +1,64 @@ +import { IsNotEmpty, IsOptional, IsPostalCode, IsString } from 'class-validator'; +import { Address } from '../entities/Address'; + +export class CreateAddress { + /** + * The address's description. + */ + @IsString() + @IsOptional() + description?: string; + + /** + * The address's first line. + * Containing the street and house number. + */ + @IsString() + @IsNotEmpty() + address1: string; + + /** + * The address's second line. + * Containing optional information. + */ + @IsString() + @IsOptional() + address2?: string; + + /** + * The address's postal code. + */ + @IsString() + @IsNotEmpty() + @IsPostalCode("DE") + postalcode: string; + + /** + * The address's city. + */ + @IsString() + @IsNotEmpty() + city: string; + + /** + * The address's country. + */ + @IsString() + @IsNotEmpty() + country: string; + + /** + * Creates a Address object based on this. + */ + public toAddress(): Address { + let newAddress: Address = new Address(); + + newAddress.address1 = this.address1; + newAddress.address2 = this.address2; + newAddress.postalcode = this.postalcode; + newAddress.city = this.city; + newAddress.country = this.country; + + return newAddress; + } +} \ No newline at end of file diff --git a/src/models/creation/CreateParticipant.ts b/src/models/creation/CreateParticipant.ts new file mode 100644 index 0000000..8436186 --- /dev/null +++ b/src/models/creation/CreateParticipant.ts @@ -0,0 +1,83 @@ +import { IsEmail, IsInt, IsNotEmpty, IsObject, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; +import { ParticipantOnlyOneAddressAllowedError } from '../../errors/ParticipantErrors'; +import { Address } from '../entities/Address'; +import { CreateAddress } from './CreateAddress'; + +export abstract class CreateParticipant { + /** + * The new participant's first name. + */ + @IsString() + @IsNotEmpty() + firstname: string; + + /** + * The new participant's middle name. + * Optional. + */ + @IsString() + @IsNotEmpty() + middlename?: string; + + /** + * The new participant's last name. + */ + @IsString() + @IsNotEmpty() + lastname: string; + + /** + * The new participant's phone number. + * Optional. + */ + @IsString() + @IsOptional() + @IsPhoneNumber("ZZ") + phone?: string; + + /** + * The new participant's e-mail address. + * Optional. + */ + @IsString() + @IsOptional() + @IsEmail() + email?: string; + + /** + * The new participant's address's id. + * Optional - please provide either addressId or address. + */ + @IsInt() + @IsOptional() + addressId?: number; + + /** + * The new participant's address. + * Optional - please provide either addressId or address. + */ + @IsObject() + @IsOptional() + address?: CreateAddress; + + /** + * Creates a Participant entity from this. + */ + public async getAddress(): Promise
{ + let address: Address; + + if (this.addressId !== undefined && this.address !== undefined) { + throw new ParticipantOnlyOneAddressAllowedError + } + if (this.addressId === undefined && this.address === undefined) { + return null; + } + + if (this.addressId) { + return await getConnectionManager().get().getRepository(Address).findOne({ id: this.addressId }); + } + + return this.address.toAddress(); + } +} \ No newline at end of file diff --git a/src/models/creation/CreateRunner.ts b/src/models/creation/CreateRunner.ts index 9913ee8..b34ec9d 100644 --- a/src/models/creation/CreateRunner.ts +++ b/src/models/creation/CreateRunner.ts @@ -1,50 +1,13 @@ -import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; +import { IsInt, IsOptional } from 'class-validator'; import { getConnectionManager } from 'typeorm'; import { RunnerGroupNeededError, RunnerGroupNotFoundError, RunnerOnlyOneGroupAllowedError } from '../../errors/RunnerErrors'; import { Runner } from '../entities/Runner'; +import { RunnerGroup } from '../entities/RunnerGroup'; import { RunnerOrganisation } from '../entities/RunnerOrganisation'; import { RunnerTeam } from '../entities/RunnerTeam'; +import { CreateParticipant } from './CreateParticipant'; -export class CreateRunner { - /** - * The new runner's first name. - */ - @IsString() - @IsNotEmpty() - firstname: string; - - /** - * The new runner's middle name. - * Optional. - */ - @IsString() - @IsNotEmpty() - middlename?: string; - - /** - * The new runner's last name. - */ - @IsString() - @IsNotEmpty() - lastname: string; - - /** - * The new runner's phone number. - * Optional. - */ - @IsString() - @IsOptional() - @IsPhoneNumber("ZZ") - phone?: string; - - /** - * The new runner's e-mail address. - * Optional. - */ - @IsString() - @IsOptional() - @IsEmail() - email?: string; +export class CreateRunner extends CreateParticipant { /** * The new runner's team's id. @@ -68,6 +31,22 @@ export class CreateRunner { public async toRunner(): Promise { let newRunner: Runner = new Runner(); + newRunner.firstname = this.firstname; + newRunner.middlename = this.middlename; + newRunner.lastname = this.lastname; + newRunner.phone = this.phone; + newRunner.email = this.email; + newRunner.group = await this.getGroup(); + newRunner.address = await this.getAddress(); + + return newRunner; + } + + /** + * Manages all the different ways a group can be provided. + */ + public async getGroup(): Promise { + let group: RunnerGroup; if (this.teamId !== undefined && this.orgId !== undefined) { throw new RunnerOnlyOneGroupAllowedError(); } @@ -76,21 +55,14 @@ export class CreateRunner { } if (this.teamId) { - newRunner.group = await getConnectionManager().get().getRepository(RunnerTeam).findOne({ id: this.teamId }); + group = await getConnectionManager().get().getRepository(RunnerTeam).findOne({ id: this.teamId }); } if (this.orgId) { - newRunner.group = await getConnectionManager().get().getRepository(RunnerOrganisation).findOne({ id: this.orgId }); + group = await getConnectionManager().get().getRepository(RunnerOrganisation).findOne({ id: this.orgId }); } - if (!newRunner.group) { + if (!group) { throw new RunnerGroupNotFoundError(); } - - newRunner.firstname = this.firstname; - newRunner.middlename = this.middlename; - newRunner.lastname = this.lastname; - newRunner.phone = this.phone; - newRunner.email = this.email; - - return newRunner; + return group; } } \ No newline at end of file