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