From 1b1f8f2b09bc72b90c0c12bbd1cc5a68a1290cc3 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 16 Dec 2020 18:20:31 +0100 Subject: [PATCH 01/16] Added a basic import controller ref #22 --- src/controllers/ImportController.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/controllers/ImportController.ts diff --git a/src/controllers/ImportController.ts b/src/controllers/ImportController.ts new file mode 100644 index 0000000..b3dc6d0 --- /dev/null +++ b/src/controllers/ImportController.ts @@ -0,0 +1,23 @@ +import { Body, ContentType, Controller, Post } from 'routing-controllers'; +import { OpenAPI } from 'routing-controllers-openapi'; + +@Controller('/import') +//@Authorized("IMPORT:read") +export class ImportController { + // private runnerRepository: Repository; + + /** + * Gets the repository of this controller's model/entity. + */ + constructor() { + //this.runnerRepository = getConnectionManager().get().getRepository(Runner); + } + + @Post() + @ContentType("text/csv") + @OpenAPI({ description: "Create new runners based on a csv file" }) + async post(@Body() body: any) { + console.log(body) + return body; + } +} \ No newline at end of file From b9fd2379f4bc83265441c08814d85f359c3cd11b Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 16 Dec 2020 19:00:25 +0100 Subject: [PATCH 02/16] Added rawbody if needed ref #22 --- src/middlewares/RawBody.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/middlewares/RawBody.ts diff --git a/src/middlewares/RawBody.ts b/src/middlewares/RawBody.ts new file mode 100644 index 0000000..58ff67d --- /dev/null +++ b/src/middlewares/RawBody.ts @@ -0,0 +1,27 @@ +import { Request, Response } from 'express'; + +const RawBodyMiddleware = (req: Request, res: Response, next: () => void) => { + const body = [] + req.on('data', chunk => { + body.push(chunk) + }) + req.on('end', () => { + const rawBody = Buffer.concat(body) + req['rawBody'] = rawBody + /* + switch (req.header('content-type')) { + case 'application/json': + req.body = JSON.parse(rawBody.toString()) + break + // add more body parsing if needs be + default: + } + */ + next() + }) + req.on('error', () => { + res.sendStatus(400) + }) +} + +export default RawBodyMiddleware \ No newline at end of file From 2e4a4f1661093ad8816aca44684ea4a97a8816d7 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 16 Dec 2020 19:03:27 +0100 Subject: [PATCH 03/16] Added endpoints for runner import by json and csv --- src/controllers/ImportController.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/controllers/ImportController.ts b/src/controllers/ImportController.ts index b3dc6d0..7a4331d 100644 --- a/src/controllers/ImportController.ts +++ b/src/controllers/ImportController.ts @@ -1,7 +1,8 @@ -import { Body, ContentType, Controller, Post } from 'routing-controllers'; +import { ContentType, Controller, Post, Req, UseBefore } from 'routing-controllers'; import { OpenAPI } from 'routing-controllers-openapi'; +import RawBodyMiddleware from '../middlewares/RawBody'; -@Controller('/import') +@Controller() //@Authorized("IMPORT:read") export class ImportController { // private runnerRepository: Repository; @@ -13,11 +14,20 @@ export class ImportController { //this.runnerRepository = getConnectionManager().get().getRepository(Runner); } - @Post() + @Post('/runners/import') + @ContentType("application.json") + @OpenAPI({ description: "Create new runners from json" }) + postJSON(@Req() request: any) { + console.log(request.rawBody.toString()) + return request.rawBody.toString(); + } + + @Post('/runners/import/csv') @ContentType("text/csv") - @OpenAPI({ description: "Create new runners based on a csv file" }) - async post(@Body() body: any) { - console.log(body) - return body; + @UseBefore(RawBodyMiddleware) + @OpenAPI({ description: "Create new runners from csv" }) + postCSV(@Req() request: any) { + console.log(request.rawBody.toString()) + return request.rawBody.toString(); } } \ No newline at end of file From 30952aa14f12f7af59780c9c5d2358764db7ac0e Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 17 Dec 2020 16:20:20 +0100 Subject: [PATCH 04/16] Marked csv import as not implemented --- src/controllers/ImportController.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/controllers/ImportController.ts b/src/controllers/ImportController.ts index 7a4331d..843e9ba 100644 --- a/src/controllers/ImportController.ts +++ b/src/controllers/ImportController.ts @@ -27,7 +27,6 @@ export class ImportController { @UseBefore(RawBodyMiddleware) @OpenAPI({ description: "Create new runners from csv" }) postCSV(@Req() request: any) { - console.log(request.rawBody.toString()) - return request.rawBody.toString(); + throw new Error("Not implemented yet."); } } \ No newline at end of file From a8ec0142b06eb367e496d395a74105cd7df77d06 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 17 Dec 2020 16:21:02 +0100 Subject: [PATCH 05/16] Added import-action classes ref #22 --- src/models/actions/ImportRunner.ts | 26 ++++++++++++ src/models/actions/ImportSchoolRunner.ts | 52 ++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 src/models/actions/ImportRunner.ts create mode 100644 src/models/actions/ImportSchoolRunner.ts diff --git a/src/models/actions/ImportRunner.ts b/src/models/actions/ImportRunner.ts new file mode 100644 index 0000000..2adb5cc --- /dev/null +++ b/src/models/actions/ImportRunner.ts @@ -0,0 +1,26 @@ +import { IsNotEmpty, IsOptional, IsString } from 'class-validator'; + +export class ImportRunner { + + /** + * The new runner's first name. + */ + @IsString() + @IsNotEmpty() + firstname: string; + + /** + * The new runner's middle name. + * Optional. + */ + @IsString() + @IsOptional() + middlename?: string; + + /** + * The new runner's last name. + */ + @IsString() + @IsNotEmpty() + lastname: string; +} \ No newline at end of file diff --git a/src/models/actions/ImportSchoolRunner.ts b/src/models/actions/ImportSchoolRunner.ts new file mode 100644 index 0000000..59320b7 --- /dev/null +++ b/src/models/actions/ImportSchoolRunner.ts @@ -0,0 +1,52 @@ +import { IsNotEmpty, IsString } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; +import { RunnerOrganisationNotFoundError } from '../../errors/RunnerOrganisationErrors'; +import { RunnerGroup } from '../entities/RunnerGroup'; +import { RunnerOrganisation } from '../entities/RunnerOrganisation'; +import { RunnerTeam } from '../entities/RunnerTeam'; +import { CreateRunner } from './CreateRunner'; +import { ImportRunner } from './ImportRunner'; + +/** + * A special class for importing runners from tabels provided by schools. + */ +export class ImportSchoolRunner extends ImportRunner { + /** + * The pupils's class. + */ + @IsString() + @IsNotEmpty() + class: string; + + public async toCreateRunner(orgID: number): Promise { + let newRunner: CreateRunner = new CreateRunner(); + + newRunner.firstname = this.firstname; + newRunner.middlename = this.middlename; + newRunner.lastname = this.lastname; + newRunner.group = (await this.getGroup(orgID)).id; + + return newRunner; + } + + public async getGroup(orgID: number): Promise { + if (this.class === undefined) { + throw new Error("TODO:Error for runner missing teams"); + } + + let org = await getConnectionManager().get().getRepository(RunnerOrganisation).findOne({ id: orgID }); + if (!org) { + throw new RunnerOrganisationNotFoundError(); + } + + let team = await getConnectionManager().get().getRepository(RunnerTeam).findOne({ name: this.class, parentGroup: org }); + if (!team) { + let newRunnerTeam: RunnerTeam = new RunnerTeam(); + newRunnerTeam.name = this.class; + newRunnerTeam.parentGroup = org; + team = await getConnectionManager().get().getRepository(RunnerTeam).create(newRunnerTeam); + } + + return team; + } +} \ No newline at end of file From cad30c7f6326d288173223e77df59f90696576da Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 17 Dec 2020 16:26:33 +0100 Subject: [PATCH 06/16] Fixed the dynamic class creation --- src/controllers/ImportController.ts | 21 ++++++++++++++------- src/models/actions/ImportSchoolRunner.ts | 3 ++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/controllers/ImportController.ts b/src/controllers/ImportController.ts index 843e9ba..746fbe8 100644 --- a/src/controllers/ImportController.ts +++ b/src/controllers/ImportController.ts @@ -1,25 +1,31 @@ -import { ContentType, Controller, Post, Req, UseBefore } from 'routing-controllers'; +import { Body, ContentType, Controller, Post, Req, UseBefore } from 'routing-controllers'; import { OpenAPI } from 'routing-controllers-openapi'; import RawBodyMiddleware from '../middlewares/RawBody'; +import { ImportSchoolRunner } from '../models/actions/ImportSchoolRunner'; +import { ResponseRunner } from '../models/responses/ResponseRunner'; +import { RunnerController } from './RunnerController'; @Controller() //@Authorized("IMPORT:read") export class ImportController { - // private runnerRepository: Repository; + private runnerController: RunnerController; /** * Gets the repository of this controller's model/entity. */ constructor() { - //this.runnerRepository = getConnectionManager().get().getRepository(Runner); + this.runnerController = new RunnerController(); } @Post('/runners/import') - @ContentType("application.json") + @ContentType("application/json") @OpenAPI({ description: "Create new runners from json" }) - postJSON(@Req() request: any) { - console.log(request.rawBody.toString()) - return request.rawBody.toString(); + async postJSON(@Body({ validate: true, type: ImportSchoolRunner }) importRunners: ImportSchoolRunner[]) { + let responseRunners: ResponseRunner[] = new Array(); + for await (let runner of importRunners) { + responseRunners.push(await this.runnerController.post(await runner.toCreateRunner(1))); + } + return responseRunners; } @Post('/runners/import/csv') @@ -27,6 +33,7 @@ export class ImportController { @UseBefore(RawBodyMiddleware) @OpenAPI({ description: "Create new runners from csv" }) postCSV(@Req() request: any) { + console.log(request.rawBody.toString()) throw new Error("Not implemented yet."); } } \ No newline at end of file diff --git a/src/models/actions/ImportSchoolRunner.ts b/src/models/actions/ImportSchoolRunner.ts index 59320b7..9964442 100644 --- a/src/models/actions/ImportSchoolRunner.ts +++ b/src/models/actions/ImportSchoolRunner.ts @@ -25,6 +25,7 @@ export class ImportSchoolRunner extends ImportRunner { newRunner.middlename = this.middlename; newRunner.lastname = this.lastname; newRunner.group = (await this.getGroup(orgID)).id; + console.log(newRunner.group) return newRunner; } @@ -44,7 +45,7 @@ export class ImportSchoolRunner extends ImportRunner { let newRunnerTeam: RunnerTeam = new RunnerTeam(); newRunnerTeam.name = this.class; newRunnerTeam.parentGroup = org; - team = await getConnectionManager().get().getRepository(RunnerTeam).create(newRunnerTeam); + team = await getConnectionManager().get().getRepository(RunnerTeam).save(newRunnerTeam); } return team; From 1b59d58c6023803a5b729b0a274c6d17013219d7 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 17 Dec 2020 16:32:29 +0100 Subject: [PATCH 07/16] Abstracted a little bit more for potential company runner import --- src/controllers/ImportController.ts | 3 ++- src/models/actions/ImportRunner.ts | 4 +++- src/models/actions/ImportSchoolRunner.ts | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/controllers/ImportController.ts b/src/controllers/ImportController.ts index 746fbe8..18a5991 100644 --- a/src/controllers/ImportController.ts +++ b/src/controllers/ImportController.ts @@ -1,6 +1,7 @@ import { Body, ContentType, Controller, Post, Req, UseBefore } from 'routing-controllers'; import { OpenAPI } from 'routing-controllers-openapi'; import RawBodyMiddleware from '../middlewares/RawBody'; +import { ImportRunner } from '../models/actions/ImportRunner'; import { ImportSchoolRunner } from '../models/actions/ImportSchoolRunner'; import { ResponseRunner } from '../models/responses/ResponseRunner'; import { RunnerController } from './RunnerController'; @@ -20,7 +21,7 @@ export class ImportController { @Post('/runners/import') @ContentType("application/json") @OpenAPI({ description: "Create new runners from json" }) - async postJSON(@Body({ validate: true, type: ImportSchoolRunner }) importRunners: ImportSchoolRunner[]) { + async postJSON(@Body({ validate: true, type: ImportSchoolRunner }) importRunners: ImportRunner[]) { let responseRunners: ResponseRunner[] = new Array(); for await (let runner of importRunners) { responseRunners.push(await this.runnerController.post(await runner.toCreateRunner(1))); diff --git a/src/models/actions/ImportRunner.ts b/src/models/actions/ImportRunner.ts index 2adb5cc..ba7daa2 100644 --- a/src/models/actions/ImportRunner.ts +++ b/src/models/actions/ImportRunner.ts @@ -1,6 +1,6 @@ import { IsNotEmpty, IsOptional, IsString } from 'class-validator'; -export class ImportRunner { +export abstract class ImportRunner { /** * The new runner's first name. @@ -23,4 +23,6 @@ export class ImportRunner { @IsString() @IsNotEmpty() lastname: string; + + public abstract toCreateRunner(groupID?: number); } \ No newline at end of file diff --git a/src/models/actions/ImportSchoolRunner.ts b/src/models/actions/ImportSchoolRunner.ts index 9964442..18db259 100644 --- a/src/models/actions/ImportSchoolRunner.ts +++ b/src/models/actions/ImportSchoolRunner.ts @@ -18,13 +18,13 @@ export class ImportSchoolRunner extends ImportRunner { @IsNotEmpty() class: string; - public async toCreateRunner(orgID: number): Promise { + public async toCreateRunner(groupID: number): Promise { let newRunner: CreateRunner = new CreateRunner(); newRunner.firstname = this.firstname; newRunner.middlename = this.middlename; newRunner.lastname = this.lastname; - newRunner.group = (await this.getGroup(orgID)).id; + newRunner.group = (await this.getGroup(groupID)).id; console.log(newRunner.group) return newRunner; From 4801e010b4e7327507f7465f4b86d1c57cb3c369 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 17 Dec 2020 16:33:54 +0100 Subject: [PATCH 08/16] Removed useless console.log ref #22 --- src/models/actions/ImportSchoolRunner.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/models/actions/ImportSchoolRunner.ts b/src/models/actions/ImportSchoolRunner.ts index 18db259..75b20e5 100644 --- a/src/models/actions/ImportSchoolRunner.ts +++ b/src/models/actions/ImportSchoolRunner.ts @@ -25,7 +25,6 @@ export class ImportSchoolRunner extends ImportRunner { newRunner.middlename = this.middlename; newRunner.lastname = this.lastname; newRunner.group = (await this.getGroup(groupID)).id; - console.log(newRunner.group) return newRunner; } From 97494aeaf71d258980256592ae4406f2a2be0bb9 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 17 Dec 2020 16:46:00 +0100 Subject: [PATCH 09/16] Runners can now be imported into a org --- src/controllers/ImportController.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/controllers/ImportController.ts b/src/controllers/ImportController.ts index 18a5991..70cd9de 100644 --- a/src/controllers/ImportController.ts +++ b/src/controllers/ImportController.ts @@ -1,4 +1,4 @@ -import { Body, ContentType, Controller, Post, Req, UseBefore } from 'routing-controllers'; +import { Body, ContentType, Controller, Param, Post, QueryParam, Req, UseBefore } from 'routing-controllers'; import { OpenAPI } from 'routing-controllers-openapi'; import RawBodyMiddleware from '../middlewares/RawBody'; import { ImportRunner } from '../models/actions/ImportRunner'; @@ -21,14 +21,21 @@ export class ImportController { @Post('/runners/import') @ContentType("application/json") @OpenAPI({ description: "Create new runners from json" }) - async postJSON(@Body({ validate: true, type: ImportSchoolRunner }) importRunners: ImportRunner[]) { + async postJSON(@Body({ validate: true, type: ImportSchoolRunner }) importRunners: ImportRunner[], @QueryParam("org") orgID: number) { let responseRunners: ResponseRunner[] = new Array(); for await (let runner of importRunners) { - responseRunners.push(await this.runnerController.post(await runner.toCreateRunner(1))); + responseRunners.push(await this.runnerController.post(await runner.toCreateRunner(orgID))); } return responseRunners; } + @Post('/organisations/:id/import') + @ContentType("application/json") + @OpenAPI({ description: "Create new runners from json" }) + async postOrgsJSON(@Body({ validate: true, type: ImportSchoolRunner }) importRunners: ImportRunner[], @Param('id') id: number) { + return await this.postJSON(importRunners, id) + } + @Post('/runners/import/csv') @ContentType("text/csv") @UseBefore(RawBodyMiddleware) From 71228fbf33a59e9d1d77ee8415c43a69a66ac010 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 17 Dec 2020 17:14:08 +0100 Subject: [PATCH 10/16] Now organisations and teams can import runners --- src/controllers/ImportController.ts | 13 +++++++++++-- src/models/actions/ImportSchoolRunner.ts | 14 +++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/controllers/ImportController.ts b/src/controllers/ImportController.ts index 70cd9de..d82bf8c 100644 --- a/src/controllers/ImportController.ts +++ b/src/controllers/ImportController.ts @@ -1,5 +1,6 @@ import { Body, ContentType, Controller, Param, Post, QueryParam, Req, UseBefore } from 'routing-controllers'; import { OpenAPI } from 'routing-controllers-openapi'; +import { RunnerGroupNeededError } from '../errors/RunnerErrors'; import RawBodyMiddleware from '../middlewares/RawBody'; import { ImportRunner } from '../models/actions/ImportRunner'; import { ImportSchoolRunner } from '../models/actions/ImportSchoolRunner'; @@ -21,10 +22,11 @@ export class ImportController { @Post('/runners/import') @ContentType("application/json") @OpenAPI({ description: "Create new runners from json" }) - async postJSON(@Body({ validate: true, type: ImportSchoolRunner }) importRunners: ImportRunner[], @QueryParam("org") orgID: number) { + async postJSON(@Body({ validate: true, type: ImportSchoolRunner }) importRunners: ImportRunner[], @QueryParam("group") groupID: number) { + if (!groupID) { throw new RunnerGroupNeededError(); } let responseRunners: ResponseRunner[] = new Array(); for await (let runner of importRunners) { - responseRunners.push(await this.runnerController.post(await runner.toCreateRunner(orgID))); + responseRunners.push(await this.runnerController.post(await runner.toCreateRunner(groupID))); } return responseRunners; } @@ -36,6 +38,13 @@ export class ImportController { return await this.postJSON(importRunners, id) } + @Post('/teams/:id/import') + @ContentType("application/json") + @OpenAPI({ description: "Create new runners from json" }) + async postTeamsJSON(@Body({ validate: true, type: ImportSchoolRunner }) importRunners: ImportRunner[], @Param('id') id: number) { + return await this.postJSON(importRunners, id) + } + @Post('/runners/import/csv') @ContentType("text/csv") @UseBefore(RawBodyMiddleware) diff --git a/src/models/actions/ImportSchoolRunner.ts b/src/models/actions/ImportSchoolRunner.ts index 75b20e5..a12b9ec 100644 --- a/src/models/actions/ImportSchoolRunner.ts +++ b/src/models/actions/ImportSchoolRunner.ts @@ -1,5 +1,6 @@ import { IsNotEmpty, IsString } from 'class-validator'; import { getConnectionManager } from 'typeorm'; +import { RunnerGroupNeededError } from '../../errors/RunnerErrors'; import { RunnerOrganisationNotFoundError } from '../../errors/RunnerOrganisationErrors'; import { RunnerGroup } from '../entities/RunnerGroup'; import { RunnerOrganisation } from '../entities/RunnerOrganisation'; @@ -29,17 +30,20 @@ export class ImportSchoolRunner extends ImportRunner { return newRunner; } - public async getGroup(orgID: number): Promise { - if (this.class === undefined) { - throw new Error("TODO:Error for runner missing teams"); + public async getGroup(groupID: number): Promise { + if (this.class === undefined && groupID === undefined) { + throw new RunnerGroupNeededError(); } - let org = await getConnectionManager().get().getRepository(RunnerOrganisation).findOne({ id: orgID }); + let team = await getConnectionManager().get().getRepository(RunnerTeam).findOne({ id: groupID }); + if (team) { return team; } + + let org = await getConnectionManager().get().getRepository(RunnerOrganisation).findOne({ id: groupID }); if (!org) { throw new RunnerOrganisationNotFoundError(); } - let team = await getConnectionManager().get().getRepository(RunnerTeam).findOne({ name: this.class, parentGroup: org }); + team = await getConnectionManager().get().getRepository(RunnerTeam).findOne({ name: this.class, parentGroup: org }); if (!team) { let newRunnerTeam: RunnerTeam = new RunnerTeam(); newRunnerTeam.name = this.class; From 0d8fbf1eca512c3a14cd4ca09b0eda820742ed43 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 17 Dec 2020 17:25:17 +0100 Subject: [PATCH 11/16] Consolidated the json import for a cleaner result ref #22 --- src/controllers/ImportController.ts | 7 ++- src/models/actions/ImportRunner.ts | 58 +++++++++++++++++++++++- src/models/actions/ImportSchoolRunner.ts | 56 ----------------------- 3 files changed, 59 insertions(+), 62 deletions(-) delete mode 100644 src/models/actions/ImportSchoolRunner.ts diff --git a/src/controllers/ImportController.ts b/src/controllers/ImportController.ts index d82bf8c..48d3f78 100644 --- a/src/controllers/ImportController.ts +++ b/src/controllers/ImportController.ts @@ -3,7 +3,6 @@ import { OpenAPI } from 'routing-controllers-openapi'; import { RunnerGroupNeededError } from '../errors/RunnerErrors'; import RawBodyMiddleware from '../middlewares/RawBody'; import { ImportRunner } from '../models/actions/ImportRunner'; -import { ImportSchoolRunner } from '../models/actions/ImportSchoolRunner'; import { ResponseRunner } from '../models/responses/ResponseRunner'; import { RunnerController } from './RunnerController'; @@ -22,7 +21,7 @@ export class ImportController { @Post('/runners/import') @ContentType("application/json") @OpenAPI({ description: "Create new runners from json" }) - async postJSON(@Body({ validate: true, type: ImportSchoolRunner }) importRunners: ImportRunner[], @QueryParam("group") groupID: number) { + async postJSON(@Body({ validate: true, type: ImportRunner }) importRunners: ImportRunner[], @QueryParam("group") groupID: number) { if (!groupID) { throw new RunnerGroupNeededError(); } let responseRunners: ResponseRunner[] = new Array(); for await (let runner of importRunners) { @@ -34,14 +33,14 @@ export class ImportController { @Post('/organisations/:id/import') @ContentType("application/json") @OpenAPI({ description: "Create new runners from json" }) - async postOrgsJSON(@Body({ validate: true, type: ImportSchoolRunner }) importRunners: ImportRunner[], @Param('id') id: number) { + async postOrgsJSON(@Body({ validate: true, type: ImportRunner }) importRunners: ImportRunner[], @Param('id') id: number) { return await this.postJSON(importRunners, id) } @Post('/teams/:id/import') @ContentType("application/json") @OpenAPI({ description: "Create new runners from json" }) - async postTeamsJSON(@Body({ validate: true, type: ImportSchoolRunner }) importRunners: ImportRunner[], @Param('id') id: number) { + async postTeamsJSON(@Body({ validate: true, type: ImportRunner }) importRunners: ImportRunner[], @Param('id') id: number) { return await this.postJSON(importRunners, id) } diff --git a/src/models/actions/ImportRunner.ts b/src/models/actions/ImportRunner.ts index ba7daa2..3f5d97e 100644 --- a/src/models/actions/ImportRunner.ts +++ b/src/models/actions/ImportRunner.ts @@ -1,6 +1,13 @@ import { IsNotEmpty, IsOptional, IsString } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; +import { RunnerGroupNeededError } from '../../errors/RunnerErrors'; +import { RunnerOrganisationNotFoundError } from '../../errors/RunnerOrganisationErrors'; +import { RunnerGroup } from '../entities/RunnerGroup'; +import { RunnerOrganisation } from '../entities/RunnerOrganisation'; +import { RunnerTeam } from '../entities/RunnerTeam'; +import { CreateRunner } from './CreateRunner'; -export abstract class ImportRunner { +export class ImportRunner { /** * The new runner's first name. @@ -24,5 +31,52 @@ export abstract class ImportRunner { @IsNotEmpty() lastname: string; - public abstract toCreateRunner(groupID?: number); + /** + * The new runner's class (if not provided otherwise). + */ + @IsString() + @IsOptional() + team?: string; + + @IsOptional() + @IsString() + public set class(value: string) { + this.team = value; + } + + public async toCreateRunner(groupID: number): Promise { + let newRunner: CreateRunner = new CreateRunner(); + + newRunner.firstname = this.firstname; + newRunner.middlename = this.middlename; + newRunner.lastname = this.lastname; + newRunner.group = (await this.getGroup(groupID)).id; + + return newRunner; + } + + public async getGroup(groupID: number): Promise { + if (this.team === undefined && groupID === undefined) { + throw new RunnerGroupNeededError(); + } + + let team = await getConnectionManager().get().getRepository(RunnerTeam).findOne({ id: groupID }); + if (team) { return team; } + + let org = await getConnectionManager().get().getRepository(RunnerOrganisation).findOne({ id: groupID }); + if (!org) { + throw new RunnerOrganisationNotFoundError(); + } + if (this.team === undefined) { return org; } + + team = await getConnectionManager().get().getRepository(RunnerTeam).findOne({ name: this.team, parentGroup: org }); + if (!team) { + let newRunnerTeam: RunnerTeam = new RunnerTeam(); + newRunnerTeam.name = this.team; + newRunnerTeam.parentGroup = org; + team = await getConnectionManager().get().getRepository(RunnerTeam).save(newRunnerTeam); + } + + return team; + } } \ No newline at end of file diff --git a/src/models/actions/ImportSchoolRunner.ts b/src/models/actions/ImportSchoolRunner.ts deleted file mode 100644 index a12b9ec..0000000 --- a/src/models/actions/ImportSchoolRunner.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { IsNotEmpty, IsString } from 'class-validator'; -import { getConnectionManager } from 'typeorm'; -import { RunnerGroupNeededError } from '../../errors/RunnerErrors'; -import { RunnerOrganisationNotFoundError } from '../../errors/RunnerOrganisationErrors'; -import { RunnerGroup } from '../entities/RunnerGroup'; -import { RunnerOrganisation } from '../entities/RunnerOrganisation'; -import { RunnerTeam } from '../entities/RunnerTeam'; -import { CreateRunner } from './CreateRunner'; -import { ImportRunner } from './ImportRunner'; - -/** - * A special class for importing runners from tabels provided by schools. - */ -export class ImportSchoolRunner extends ImportRunner { - /** - * The pupils's class. - */ - @IsString() - @IsNotEmpty() - class: string; - - public async toCreateRunner(groupID: number): Promise { - let newRunner: CreateRunner = new CreateRunner(); - - newRunner.firstname = this.firstname; - newRunner.middlename = this.middlename; - newRunner.lastname = this.lastname; - newRunner.group = (await this.getGroup(groupID)).id; - - return newRunner; - } - - public async getGroup(groupID: number): Promise { - if (this.class === undefined && groupID === undefined) { - throw new RunnerGroupNeededError(); - } - - let team = await getConnectionManager().get().getRepository(RunnerTeam).findOne({ id: groupID }); - if (team) { return team; } - - let org = await getConnectionManager().get().getRepository(RunnerOrganisation).findOne({ id: groupID }); - if (!org) { - throw new RunnerOrganisationNotFoundError(); - } - - team = await getConnectionManager().get().getRepository(RunnerTeam).findOne({ name: this.class, parentGroup: org }); - if (!team) { - let newRunnerTeam: RunnerTeam = new RunnerTeam(); - newRunnerTeam.name = this.class; - newRunnerTeam.parentGroup = org; - team = await getConnectionManager().get().getRepository(RunnerTeam).save(newRunnerTeam); - } - - return team; - } -} \ No newline at end of file From 03b7e346ab52a2f70b39f8784d8525a7f4da9123 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 17 Dec 2020 18:36:51 +0100 Subject: [PATCH 12/16] Working csv import ref #22 --- package.json | 2 ++ src/controllers/ImportController.ts | 34 ++++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 61ecdd4..c8a2473 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "consola": "^2.15.0", "cookie-parser": "^1.4.5", "cors": "^2.8.5", + "csvtojson": "^2.0.10", "dotenv": "^8.2.0", "express": "^4.17.1", "jsonwebtoken": "^8.5.1", @@ -47,6 +48,7 @@ }, "devDependencies": { "@types/cors": "^2.8.8", + "@types/csvtojson": "^1.1.5", "@types/express": "^4.17.9", "@types/jest": "^26.0.16", "@types/jsonwebtoken": "^8.5.0", diff --git a/src/controllers/ImportController.ts b/src/controllers/ImportController.ts index 48d3f78..1fbf50c 100644 --- a/src/controllers/ImportController.ts +++ b/src/controllers/ImportController.ts @@ -1,3 +1,4 @@ +import csv from 'csvtojson'; import { Body, ContentType, Controller, Param, Post, QueryParam, Req, UseBefore } from 'routing-controllers'; import { OpenAPI } from 'routing-controllers-openapi'; import { RunnerGroupNeededError } from '../errors/RunnerErrors'; @@ -44,12 +45,35 @@ export class ImportController { return await this.postJSON(importRunners, id) } - @Post('/runners/import/csv') - @ContentType("text/csv") + @Post('/import/csv') @UseBefore(RawBodyMiddleware) @OpenAPI({ description: "Create new runners from csv" }) - postCSV(@Req() request: any) { - console.log(request.rawBody.toString()) - throw new Error("Not implemented yet."); + async postCSV(@Req() request: any, @QueryParam("group") groupID: number) { + let csvParse = await csv({ delimiter: [",", ";"], trim: true }).fromString(request.rawBody.toString()); + let importRunners: ImportRunner[] = new Array(); + for await (let runner of csvParse) { + let newImportRunner = new ImportRunner(); + newImportRunner.firstname = runner.firstname; + newImportRunner.middlename = runner.middlename; + newImportRunner.lastname = runner.lastname; + if (runner.class === undefined) { newImportRunner.team = runner.team; } + else { newImportRunner.class = runner.class; } + importRunners.push(newImportRunner); + } + return await this.postJSON(importRunners, groupID); + } + + @Post('/organisations/:id/import/csv') + @UseBefore(RawBodyMiddleware) + @OpenAPI({ description: "Create new runners from csv" }) + async postOrgsCSV(@Req() request: any, @Param("id") id: number) { + return await this.postCSV(request, id); + } + + @Post('/teams/:id/import/csv') + @UseBefore(RawBodyMiddleware) + @OpenAPI({ description: "Create new runners from csv" }) + async postTeamsCSV(@Req() request: any, @Param("id") id: number) { + return await this.postCSV(request, id); } } \ No newline at end of file From 9db4344153ebd8965e2639b4bb008e5e86893055 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 17 Dec 2020 19:15:11 +0100 Subject: [PATCH 13/16] Expanded API Decriptions ref #22 --- src/controllers/ImportController.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/controllers/ImportController.ts b/src/controllers/ImportController.ts index 1fbf50c..17a90ba 100644 --- a/src/controllers/ImportController.ts +++ b/src/controllers/ImportController.ts @@ -21,7 +21,7 @@ export class ImportController { @Post('/runners/import') @ContentType("application/json") - @OpenAPI({ description: "Create new runners from json" }) + @OpenAPI({ description: "Create new runners from json and insert them (or their teams) into the provided group" }) async postJSON(@Body({ validate: true, type: ImportRunner }) importRunners: ImportRunner[], @QueryParam("group") groupID: number) { if (!groupID) { throw new RunnerGroupNeededError(); } let responseRunners: ResponseRunner[] = new Array(); @@ -33,21 +33,21 @@ export class ImportController { @Post('/organisations/:id/import') @ContentType("application/json") - @OpenAPI({ description: "Create new runners from json" }) + @OpenAPI({ description: "Create new runners from json and insert them (or their teams) into the provided org" }) async postOrgsJSON(@Body({ validate: true, type: ImportRunner }) importRunners: ImportRunner[], @Param('id') id: number) { return await this.postJSON(importRunners, id) } @Post('/teams/:id/import') @ContentType("application/json") - @OpenAPI({ description: "Create new runners from json" }) + @OpenAPI({ description: "Create new runners from json and insert them into the provided team" }) async postTeamsJSON(@Body({ validate: true, type: ImportRunner }) importRunners: ImportRunner[], @Param('id') id: number) { return await this.postJSON(importRunners, id) } @Post('/import/csv') @UseBefore(RawBodyMiddleware) - @OpenAPI({ description: "Create new runners from csv" }) + @OpenAPI({ description: "Create new runners from csv and insert them (or their teams) into the provided group" }) async postCSV(@Req() request: any, @QueryParam("group") groupID: number) { let csvParse = await csv({ delimiter: [",", ";"], trim: true }).fromString(request.rawBody.toString()); let importRunners: ImportRunner[] = new Array(); @@ -65,14 +65,14 @@ export class ImportController { @Post('/organisations/:id/import/csv') @UseBefore(RawBodyMiddleware) - @OpenAPI({ description: "Create new runners from csv" }) + @OpenAPI({ description: "Create new runners from csv and insert them (or their teams) into the provided org" }) async postOrgsCSV(@Req() request: any, @Param("id") id: number) { return await this.postCSV(request, id); } @Post('/teams/:id/import/csv') @UseBefore(RawBodyMiddleware) - @OpenAPI({ description: "Create new runners from csv" }) + @OpenAPI({ description: "Create new runners from csv and insert them into the provided team" }) async postTeamsCSV(@Req() request: any, @Param("id") id: number) { return await this.postCSV(request, id); } From 15ed9f58d5aa22ba7d53f1e54e6f45f72ec01948 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 17 Dec 2020 19:17:35 +0100 Subject: [PATCH 14/16] Added responseschemas and content types ref #22 --- src/controllers/ImportController.ts | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/controllers/ImportController.ts b/src/controllers/ImportController.ts index 17a90ba..5b7defb 100644 --- a/src/controllers/ImportController.ts +++ b/src/controllers/ImportController.ts @@ -1,7 +1,8 @@ import csv from 'csvtojson'; import { Body, ContentType, Controller, Param, Post, QueryParam, Req, UseBefore } from 'routing-controllers'; -import { OpenAPI } from 'routing-controllers-openapi'; +import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { RunnerGroupNeededError } from '../errors/RunnerErrors'; +import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors'; import RawBodyMiddleware from '../middlewares/RawBody'; import { ImportRunner } from '../models/actions/ImportRunner'; import { ResponseRunner } from '../models/responses/ResponseRunner'; @@ -21,6 +22,9 @@ export class ImportController { @Post('/runners/import') @ContentType("application/json") + @ResponseSchema(ResponseRunner, { isArray: true }) + @ResponseSchema(RunnerGroupNotFoundError) + @ResponseSchema(RunnerGroupNeededError) @OpenAPI({ description: "Create new runners from json and insert them (or their teams) into the provided group" }) async postJSON(@Body({ validate: true, type: ImportRunner }) importRunners: ImportRunner[], @QueryParam("group") groupID: number) { if (!groupID) { throw new RunnerGroupNeededError(); } @@ -33,6 +37,9 @@ export class ImportController { @Post('/organisations/:id/import') @ContentType("application/json") + @ResponseSchema(ResponseRunner, { isArray: true }) + @ResponseSchema(RunnerGroupNotFoundError) + @ResponseSchema(RunnerGroupNeededError) @OpenAPI({ description: "Create new runners from json and insert them (or their teams) into the provided org" }) async postOrgsJSON(@Body({ validate: true, type: ImportRunner }) importRunners: ImportRunner[], @Param('id') id: number) { return await this.postJSON(importRunners, id) @@ -40,13 +47,20 @@ export class ImportController { @Post('/teams/:id/import') @ContentType("application/json") + @ResponseSchema(ResponseRunner, { isArray: true }) + @ResponseSchema(RunnerGroupNotFoundError) + @ResponseSchema(RunnerGroupNeededError) @OpenAPI({ description: "Create new runners from json and insert them into the provided team" }) async postTeamsJSON(@Body({ validate: true, type: ImportRunner }) importRunners: ImportRunner[], @Param('id') id: number) { return await this.postJSON(importRunners, id) } @Post('/import/csv') + @ContentType("application/json") @UseBefore(RawBodyMiddleware) + @ResponseSchema(ResponseRunner, { isArray: true }) + @ResponseSchema(RunnerGroupNotFoundError) + @ResponseSchema(RunnerGroupNeededError) @OpenAPI({ description: "Create new runners from csv and insert them (or their teams) into the provided group" }) async postCSV(@Req() request: any, @QueryParam("group") groupID: number) { let csvParse = await csv({ delimiter: [",", ";"], trim: true }).fromString(request.rawBody.toString()); @@ -64,14 +78,22 @@ export class ImportController { } @Post('/organisations/:id/import/csv') + @ContentType("application/json") @UseBefore(RawBodyMiddleware) + @ResponseSchema(ResponseRunner, { isArray: true }) + @ResponseSchema(RunnerGroupNotFoundError) + @ResponseSchema(RunnerGroupNeededError) @OpenAPI({ description: "Create new runners from csv and insert them (or their teams) into the provided org" }) async postOrgsCSV(@Req() request: any, @Param("id") id: number) { return await this.postCSV(request, id); } @Post('/teams/:id/import/csv') + @ContentType("application/json") @UseBefore(RawBodyMiddleware) + @ResponseSchema(ResponseRunner, { isArray: true }) + @ResponseSchema(RunnerGroupNotFoundError) + @ResponseSchema(RunnerGroupNeededError) @OpenAPI({ description: "Create new runners from csv and insert them into the provided team" }) async postTeamsCSV(@Req() request: any, @Param("id") id: number) { return await this.postCSV(request, id); From c90f9f1dd44d78b828b2d084c66f1d5cfcf7a8cf Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 17 Dec 2020 19:27:38 +0100 Subject: [PATCH 15/16] Fixed path --- src/controllers/ImportController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/ImportController.ts b/src/controllers/ImportController.ts index 5b7defb..8a57a3b 100644 --- a/src/controllers/ImportController.ts +++ b/src/controllers/ImportController.ts @@ -55,7 +55,7 @@ export class ImportController { return await this.postJSON(importRunners, id) } - @Post('/import/csv') + @Post('/runners/import/csv') @ContentType("application/json") @UseBefore(RawBodyMiddleware) @ResponseSchema(ResponseRunner, { isArray: true }) From 3aae8f85c430115b0a9122881821415261c10b5d Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 17 Dec 2020 20:54:01 +0100 Subject: [PATCH 16/16] Added status codes ref #22 --- src/controllers/ImportController.ts | 36 ++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/controllers/ImportController.ts b/src/controllers/ImportController.ts index 8a57a3b..232df6f 100644 --- a/src/controllers/ImportController.ts +++ b/src/controllers/ImportController.ts @@ -22,9 +22,9 @@ export class ImportController { @Post('/runners/import') @ContentType("application/json") - @ResponseSchema(ResponseRunner, { isArray: true }) - @ResponseSchema(RunnerGroupNotFoundError) - @ResponseSchema(RunnerGroupNeededError) + @ResponseSchema(ResponseRunner, { isArray: true, statusCode: 200 }) + @ResponseSchema(RunnerGroupNotFoundError, { statusCode: 404 }) + @ResponseSchema(RunnerGroupNeededError, { statusCode: 406 }) @OpenAPI({ description: "Create new runners from json and insert them (or their teams) into the provided group" }) async postJSON(@Body({ validate: true, type: ImportRunner }) importRunners: ImportRunner[], @QueryParam("group") groupID: number) { if (!groupID) { throw new RunnerGroupNeededError(); } @@ -37,9 +37,9 @@ export class ImportController { @Post('/organisations/:id/import') @ContentType("application/json") - @ResponseSchema(ResponseRunner, { isArray: true }) - @ResponseSchema(RunnerGroupNotFoundError) - @ResponseSchema(RunnerGroupNeededError) + @ResponseSchema(ResponseRunner, { isArray: true, statusCode: 200 }) + @ResponseSchema(RunnerGroupNotFoundError, { statusCode: 404 }) + @ResponseSchema(RunnerGroupNeededError, { statusCode: 406 }) @OpenAPI({ description: "Create new runners from json and insert them (or their teams) into the provided org" }) async postOrgsJSON(@Body({ validate: true, type: ImportRunner }) importRunners: ImportRunner[], @Param('id') id: number) { return await this.postJSON(importRunners, id) @@ -47,9 +47,9 @@ export class ImportController { @Post('/teams/:id/import') @ContentType("application/json") - @ResponseSchema(ResponseRunner, { isArray: true }) - @ResponseSchema(RunnerGroupNotFoundError) - @ResponseSchema(RunnerGroupNeededError) + @ResponseSchema(ResponseRunner, { isArray: true, statusCode: 200 }) + @ResponseSchema(RunnerGroupNotFoundError, { statusCode: 404 }) + @ResponseSchema(RunnerGroupNeededError, { statusCode: 406 }) @OpenAPI({ description: "Create new runners from json and insert them into the provided team" }) async postTeamsJSON(@Body({ validate: true, type: ImportRunner }) importRunners: ImportRunner[], @Param('id') id: number) { return await this.postJSON(importRunners, id) @@ -58,9 +58,9 @@ export class ImportController { @Post('/runners/import/csv') @ContentType("application/json") @UseBefore(RawBodyMiddleware) - @ResponseSchema(ResponseRunner, { isArray: true }) - @ResponseSchema(RunnerGroupNotFoundError) - @ResponseSchema(RunnerGroupNeededError) + @ResponseSchema(ResponseRunner, { isArray: true, statusCode: 200 }) + @ResponseSchema(RunnerGroupNotFoundError, { statusCode: 404 }) + @ResponseSchema(RunnerGroupNeededError, { statusCode: 406 }) @OpenAPI({ description: "Create new runners from csv and insert them (or their teams) into the provided group" }) async postCSV(@Req() request: any, @QueryParam("group") groupID: number) { let csvParse = await csv({ delimiter: [",", ";"], trim: true }).fromString(request.rawBody.toString()); @@ -80,9 +80,9 @@ export class ImportController { @Post('/organisations/:id/import/csv') @ContentType("application/json") @UseBefore(RawBodyMiddleware) - @ResponseSchema(ResponseRunner, { isArray: true }) - @ResponseSchema(RunnerGroupNotFoundError) - @ResponseSchema(RunnerGroupNeededError) + @ResponseSchema(ResponseRunner, { isArray: true, statusCode: 200 }) + @ResponseSchema(RunnerGroupNotFoundError, { statusCode: 404 }) + @ResponseSchema(RunnerGroupNeededError, { statusCode: 406 }) @OpenAPI({ description: "Create new runners from csv and insert them (or their teams) into the provided org" }) async postOrgsCSV(@Req() request: any, @Param("id") id: number) { return await this.postCSV(request, id); @@ -91,9 +91,9 @@ export class ImportController { @Post('/teams/:id/import/csv') @ContentType("application/json") @UseBefore(RawBodyMiddleware) - @ResponseSchema(ResponseRunner, { isArray: true }) - @ResponseSchema(RunnerGroupNotFoundError) - @ResponseSchema(RunnerGroupNeededError) + @ResponseSchema(ResponseRunner, { isArray: true, statusCode: 200 }) + @ResponseSchema(RunnerGroupNotFoundError, { statusCode: 404 }) + @ResponseSchema(RunnerGroupNeededError, { statusCode: 406 }) @OpenAPI({ description: "Create new runners from csv and insert them into the provided team" }) async postTeamsCSV(@Req() request: any, @Param("id") id: number) { return await this.postCSV(request, id);